C++11深入学习知识点整理(三)

还有些其他知识,放在这篇文章中。当前还是缺很多知识点的,只是我暂时只整理这些。


[属性指示符]

>

它主要是用来引入一些对象,类型,代码的属性,也可以理解为限制对象,类型,代码的一些行为。它为实现定义的语言扩展提供标准统一的语法,比如GNU和IBM的__attribute__((…)),微软的__declspec()语言扩展。

#__declspec

>

>

>

>

__declspec是微软特有(Microsoft Specific)的关键字,用于指定存储类(storage-class)相关属性的『扩展属性语句』就要用到它。『扩展属性语句』指定“给定类型”的实例(instance of a given type)运用下表中(微软特有存储类属性表)某种属性进行存储。__declspec的作用很类似于关键字static或extern,都用来修饰实例的某种存储属性。然而,虽然这两个关键字是C/C++ ANSI规范的一部分,但是它俩却不属于『扩展属性语句』。实际上,『扩展属性语句』只是简化并标准化了微软平台对C/C++语言的扩展,它并不是C/C++ ANSI规范。

 

__declspec(扩展声明修饰词序列)

这个序列可以只包含一个修饰词,也可以包含多个。

微软特有存储类属性表

align(#) 

allocate("segname") 

appdomain 

deprecated 

dllimport 

dllexport 

jitintrinsic 

naked 

noalias 

noinline 

noreturn 

nothrow 

novtable 

process 

property({get=get_func_name|,put=put_func_name}) 

restrict 

selectany 

thread 

uuid("ComObjectGUID") 

//__uuidof
struct s
{
    int i;
};
struct __declspec(uuid("93A1665E-C9FA-4147-AC3A-3CC855281AF8")) s;
s a, *b, &c;
__uuidof(s);
__uuidof(a);
__uuidof(b);
__uuidof(c);


//__alignof align(2的幂)
typedef __declspec(align(8)) struct { int a; long long a33; } S1;
std::cout << sizeof(S1) << std::endl; //16
std::cout << __alignof(S1) << std::endl; //8


typedef __declspec(align(32)) struct { int a; long long a33; } S1;
std::cout << sizeof(S1) << std::endl; //32
std::cout << __alignof(S1) << std::endl; //3

[内联函数和宏定义]

>

 内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以像调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。内联函数与带参数的宏定义进行下比较,它们的代码效率是一样,但是内联函数要优于宏定义,因为内联函数遵循的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关联上内联扩展,将与一般函数一样进行调用,比较方便。

[typeid]

>

如果typeid的操作数不是类类型或者是没有虚函数的类,则typeid指出该操作数的静态类型。如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型。

#include 
#include 

class Base 
{
public:
    virtual void vvfunc() {}
};
class Derived : public Base {};

using namespace std;
int main() 
{
    Derived* pd = new Derived;
    Base* pb = pd;

    cout << typeid( pb ).name() << endl;   // prints "class Base *"
    cout << typeid( *pb ).name() << endl;  // prints "class Derived"
    cout << typeid( pd ).name() << endl;   // prints "class Derived *"
    cout << typeid( *pd ).name() << endl;  // prints "class Derived"

    delete pd;
}

[__cdecl&__stdcall&__fastcall]

>

C++11深入学习知识点整理(三)_第1张图片

[auto  decltype  declval  c++函数名带箭头  result_of]

>

decltype和auto都可以用来推断类型,但是二者有几处明显的差异:

- auto忽略顶层const,decltype保留顶层const;

- 对引用操作,auto推断出原有类型,decltype推断出引用;

- 对解引用操作,auto推断出原有类型,decltype推断出引用;

---

$declval

>

>

template

typename add_rvalue_reference::type declval() noexcept;

 

- declval是个没有函数体的模板函数,所以直接调用declval函数是错误的。它只能用于不求值的语境(如sizeof或decltype)。

- 当使用decltype进行返回类型推导的时候,是无法获取到模板参数的,需要使用declval进行辅助推导。

//这种方法无法做到

template

decltype(a + b) compose(T1 a, T2 b);

 

//需要使用declval

template

decltype(std::declval() + std::declval()) compose(T1 a, T2 b);

 

//由于使用declval()这种方法太过于繁琐,所以使用以下语法。

template

auto compose(T1 a, T2 b) -> decltype(a + b);

 

$result_of

//result_of的可能实现

template  

struct result_of  

{  

  typedef decltype(declval()(declval()...)) type;  

}  

 

[static_cast&reinterpret_cast&const_cast&dynamic_cast]

>

C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下三个缺点:

- 没有从形式上体现转换功能和风险的不同。

- 将多态基类指针转换成派生类指针时不检查安全性,即无法判断转换后的指针是否确实指向一个派生类对象。

- 难以在程序中寻找到底什么地方进行了强制类型转换。

$static_cast
static_cast 用于进行比较“自然”和低风险的转换,如整型和浮点型、字符型之间的互相转换。另外,如果对象所属的类重载了强制类型转换运算符 T(如 T 是 int、int* 或其他类型名),则 static_cast 也能用来进行对象到 T 类型的转换。
static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。


$reinterpret_cast
reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。
这种转换提供了很强的灵活性,但转换的安全性只能由程序员的细心来保证了。例如,程序员执意要把一个 int* 指针、函数指针或其他类型的指针转换成 string* 类型的指针也是可以的,至于以后用转换后的指针调用 string 类的成员函数引发错误,程序员也只能自行承担查找错误的烦琐工作:(C++ 标准不允许将函数指针转换成对象指针,但有些编译器,如 Visual Studio 2010,则支持这种转换)。


$const_cast
const_cast 运算符仅用于进行去除 const 属性的转换,它也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。
将 const 引用转换为同类型的非 const 引用,将 const 指针转换为同类型的非 const 指针时可以使用 const_cast 运算符


$reinterpret_cast
用 reinterpret_cast 可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,但是这种转换不检查安全性,即不检查转换后的指针是否确实指向一个派生类对象。dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。
dynamic_cast 是通过“运行时类型检查”来保证安全性的。dynamic_cast 不能用于将非多态基类的指针或引用强制转换为派生类的指针或引用——这种转换没法保证安全性,只好用 reinterpret_cast 来完成。
dynamic_cast 在进行引用的强制转换时,如果发现转换不安全,就会拋出一个异常,通过处理异常,就能发现不安全

[constexpr&const]

>

>

>

- const 

const可以在编译阶段初始也可以在运行阶段初始化; 

const int a = 20;//编译阶段初始化; 

std::string s("abcdef"); 

const auto b = s.length();//length()的返回值 size_type; //运行阶段初始化; 

说明:

常常认为const是在编译阶段初始化,const的作用是此变量无论在编译阶段还是运行阶段都不要改变它。

- constexpr

变量:const并不限定是编译期常量还是运行期常量,而constexpr必须是编译期常量(在编译阶段得到结果)(在编译期去做被constexpr修饰的表达式的优化)。

参数: 如果参数可以在编译阶段确定,那么使用constexpr(在编译期去做被constexpr修饰的表达式的优化),如果不可以,使用了也会被编译器忽略。

>

- const修饰函数

在类中将成员函数修饰为const表明在该函数体内,不能修改对象的数据成员而且不能调用非const函数

- 函数参数

- 函数返回值

[const&volatile&mutable]

>

>

$mutable

在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。

 

$volatile
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。


下面是volatile变量的几个例子:
1.并行设备的硬件寄存器,如:状态寄存器
2.一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3.多线程应用中被几个任务共享的变量
---
const int a = 2;
int *p = const_cast(&a);
*p = 3;
int e = a;
cout << a << endl; // 2
cout << e << endl; // 2
---
volatile const int a = 2;
int *p = const_cast(&a);
*p = 3;
int e = a;
cout << a << endl; // 3
cout << e << endl; // 3
---
int a = 2;
int *p = const_cast(&a);
*p = 3;
int e = a;
cout << a << endl; // 3
cout << e << endl; // 3

[#define&typedef&using]

$#define&typedef&using

>

>

使用typedef定义的别名和使用using定义的别名在语义上是等效的。 唯一的区别是typedef在模板中有一定的局限性,而using没有。

$在保护继承和私有继承中使用using重新定义成员的访问权限

>

[override&final]

>

>

$overide

强制编译器检查某个函数是否重写基类虚函数,如果没有则报错。

$final

当不希望某个类被继承,或不希望某个虚函数被重写,可以在类名和虚函数后添加final关键字。

$保留字和关键字

override只是C++保留字,不是关键字,这意味着只有在正确的使用位置,oerride才启“关键字”的作用,其他地方可以作为标志符(如:int override;是合法的)。

[文件操作]

>

>

>

$fflush和文件缓冲区(setvbuf和setbuf(默认BUFSIZ是512))

fflush()的作用是用来刷新缓冲区,进行真正的I/O操作。

fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃;fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上。

$输入缓存区和输出缓存区

缓冲区是内存空间的一部分,也就是说在内存空间中预留了一定大小的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

$缓存区类型

全缓冲、行缓存、无缓存

>

>

---

$文件操作效率对比(CreateFile>fopen>fstream/CFile)

CreateFile是系统API,fopen是C库函数,内部应该还是调用CreateFile的。其余fstream/CFile等都是包装过的,如果硬要分出性能差异来,显然CreateFile是最底层的,效率也最高。提高IO性能用内存文件映射是比较好的方法。

$FILE/fstream效率对比

针对读写文件中百万行字符串的操作,二进制文件的操作要快于文本文件;写文件的操作要快于读文件;C语言文件指针操作方式要快于C++流式读写文件,其中写操作快近4倍,读操作C的优势不太明显。

 

 

C++11深入学习知识点整理(三)_第2张图片

[C++反射]

>

反射就是程序在运行的过程中,可以通过类名称创建对象,并获取类中申明的成员变量和方法。

[std::map]

>

>

[修饰 C++ 成员函数的两种关键字]

>

const

volatile

修饰和非修饰的函数是可同时出现的。

你可能感兴趣的:(C++,c++)