一、简介
1、设计目标
- 让C++成为更好的适用于系统开发及库开发的语言;
- 让C++成为更易于教学的语言;
- - 保证语言的稳定性,以及C++03、C语言的兼容性;
2、C++11的增强
- 通过内存模型、线程、原子操作等来支持本地并行编程;
- 通过统一初始化表达式、auto、declytype、移动语义等支持泛型编程;
- 通过constexpr、POD等来支持系统编程;
- 通过内联命名空间、继承构造函数和右值引用等来支持库的构建;
3、新增关键字
- alignas
- alignof decltype
- auto
- static_assert
- using
- noexcept
- nuillptr
- constexpr
- thread_local
二、保证稳定性和兼容性
1.1 保持与C99兼容
- C99的预定义宏;
- __func__预定义标识符
- _Pragma操作符
- 不定参数宏定义以及__VA_ARGS
- 宽窄字符串连接
1.2 预定义宏
__STDC_HOSTED__:是否包含完整标准C库;
__STDC__:编译器的实现是否和C标准一致;
__STDC_VERSION__:C标准版本;
__STDC_ISO_10646:C++编译环境符合的版本ISO/IEC 10646标准;
__func__:返回当前函数的名字,允许使用与class和struct;
- 不允许用于默认参数;
_Pragma操作符:该操作符与#pragma功能相同(#pragma once=_Pragma("once"));
- 该操作符可以在宏中进行展开,具有更大的灵活性;
__VA_ARGS__:指宏定义的最后一个参数为省略号代替的字符串;
char_t转wchar_t:C++11会自动将窄字符转成宽字符串;
__cplusplus:C++编译器给程序的每个函数换一个独一无二的名字,由于C++中有函数重载;
如果声明了一个函数,则经过编译器可能改变;在C++编译器下,当调用C函数时,要禁止名变换;
1.3 long long整型
long long==> long long(LL/ll)和unsigned long long(ULL);
1.4 扩展的整型
五种标准的有符号整型:
- signed char
- short int
- int
- long int
- long long int
以上都有无符号整数版本对应,且和符号有相同的存储空间大小;
【整型提升】:当类型不匹配时会往`等级高`、`无符号`提升;
- 长度达的等级高;
- 长度相同,标准整型的等级高于扩展类型;
- 有符号和无符号相同;
1.5 静态断言
断言
判断该判断式(状态)是否为真,迫使参数异常的时候退出程序;
- 可以使用NDEBUG来禁用asert宏;
静态断言
assert再程序运行时才起作用,当我们需要再编译时做一些断言时,此时需要用到静态断言;
系统库没有提供静态断言供我们使用,故我们通过语言的规则来实现:
- 利用【除0】的特性;
#define assert_static(e)\
enum{assert_static__ = 1/(e)};
后来C++11引入static_assert,接收两个参数,【常量表达式、警告信息】;
可以独立于任何调用之外运行;
【注意】表达式中不能有参数变量;
1.6 noexcept修饰符与操作符
与断言不同,异常用于逻辑上可能发生的错误,用来保证应用程序的安全;
C++11中弃用了throw,被noexcept取代;
当它修饰函数时,则函数不会抛出异常,若修饰的函数抛出异常(有效制止异常的传播与扩散),则直接终止,效率高于throw;
【两种形式】:
- 修饰符:直接再函数声明后添加,即该函数不会抛出异常;
- 操作符:也是用于函数声明后,(bool)内为常量表达式,为true时不会抛出异常,
void test() noexcept;
void test() noexcept(true);
template<class T>
void test() noexcept(noexcept(T())) {}
- C++11中为了保证析构函数的安全,默认再类析构中添加noexcept(true);
1.7 快速初始化成员变量
在C++98中,如果静态成员不满足常量性,则不可以直接的声明,即使初始化const static也只能在int和enum两种类型;
如:
class A{
private:
int a = 1; // ×
static int b = 1; // ×
static const doube c = 1.1; // ×
static const int d = 1; // √
};
在C++11中,允许使用{}或=对非静态成员变量初始化,但对就地初始化两者并非等价,要使用{};
struct C {
C(int i) : c(i) {}
int c;
};
class A{
private:
C c(1);
C c{2};
};
初始化列表与就地初始化
class A{
public:
A(int a, string name) : m_a(a), m_name(name) {
cout << m_a << " " << m_name << endl;
}
private:
int m_a {0};
string m_name{"123"};
};
void test_init() {
A a(10, "xiao");
}
经测试告诉我们,初始化列表与就地初始化并不冲突,且初始化列表总是优先于就地初始化;
当然使用就地初始化可以减轻我们对构造函数中对成员变量的初始化;
1.8 非静态成员的sizeof
C++11中sizeof可以对非静态成员变量使用sizeof操作符;
在C++98中,如果对类成员获取大小 sizeof(((class*)0)->hand);
1.9 扩展friend语法
在C++11中友元声明可直接`friend 名称`可直接省略class,或可使用别名;
增加了新功能,能在类模板中声明友元;
template<class T> class A {
friend T;
};
1.10 final/overide控制
final:不然该函数在子类中被重写;
override:防止父类中的虚函数在子类中要被重写时,错该函数的名称或参数;
1.11 模板函数的默认模板参数
C++11中模板可以有默认的参数,且编译器能够自动推导实参中的类型;
默认模板参数的作用好比函数的默认形参;当然默认模板参数与函数类似,需要遵循从右往左的规则;
如果可以从函数实参中推导出类型,则默认模板参数就不会被使用;
默认模板参数通常时需要跟默认参数一起使用;
template<class T, class U = double >
void f(T t = 0, U u = 0);
void g() {
f(1, 'c');
f(1);
f();
f<int>();
f<int,char>();
}
1.12 外部模板
类似于外部变量;
当一个头文件中:template<typename T>void func(T) {}
分别用在cpp1:void test1() { func(3); }
分别用在cpp2:void test2() { func(4); }
由于使用模板一致,故其编译时两个目标文件中会有两份相同的func<int>(int);
如何避免上述代码重复
由于代码重复会导致编译器的增加编译时间和链接时间;
在链接过程中,链接器通过一些编译器辅助的手段将重复的模板函数代码删除,只保留一个副本;
解决该问题,类似于使用外部变量;
我们只需声明
template void func<int>(int);即可人编译器实例化一个版本
若使用外部模板
则只需在声明添加一个extern;
分别用在cpp1:template void func<int>(int);// 实例化 void test1() { func(3); }
分别用在cpp2:extern template void func<int>(int);// 外部声明 void test2() { func(4); }
也可将外部模板声明放置与头文件中,即包含该头文件即可共享这个外部模板声明;
注意事项
- 如果外部模板声明于某个编译单元,则显示实例化必须在另一个/同一个编译单元中;
- 外部模板声明不用于一个静态函数,由于静态函数没有外部链接属性;
- 当项目较大时,才建议这样使用;
1.13 局部和匿名类型作为模板实参
template<typename T> class X{};
template<typename T> void TempFun(T t) {};
struct A{} a;
struct {int i;}b;
typedef struct {int i;}B;
void Func() {
struct C{} c;
X<A> x1;
X<B> x2;
X<C> x3;
TempFun(a);
TempFun(b);
TempFun(c);
}
C++11支持用普通的全局结构体、匿名的全局结构体、局部的结构体作为参数传给模板;
【注意】匿名类型的声明不能在模板实参的位置 TempFun<struct {int a; }> t,需要使用其别名;
|