1. 移动语义
转让或者资源窃取,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用。使用移动语义可以避免很多无用的拷贝,提高程序性能,C++所有的STL都实现了移动语义,方便我们使用,例如:
std::vector<string> vecs;
...
std::vector<string> vecm = std::move(vecs); // 免去很多拷贝
注意:移动语义仅针对于那些实现了移动构造函数的类的对象,对于那种基本类型int、float等没有任何优化作用,还是会拷贝,因为它们没有对应的移动构造函数
2. 返回值优化
一种C++编译优化技术,当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。
编译器进行返回值优化的情况:
- return的值类型与函数的返回值类型相同
- return的是一个局部对象
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}
std::vector<int> rval_ref = return_vector();
这段代码会触发RVO,不拷贝也不移动,不生成临时对象。
3. 列表初始化
- 可以直接在变量名后面加上初始化列表来进行对象的初始化
struct A {
public:
A(int) {}
private:
A(const A&) {}
};
int main() {
A a(123);
A b = 123; // error
A c = { 123 };
A d{123}; // c++ 11
int e = {123};
int f{123}; // c++ 11
return 0;
}
- 列表初始化也可以用在函数的返回值上
std::vector<int> func() {
return {1, 2, 3};
}
- 列表初始化的规则:聚合类型可以直接进行列表初始化,对于一个聚合类型,使用列表初始化相当于对其中的每个元素分别赋值;对于非聚合类型,需要先自定义一个对应的构造函数,此时列表初始化将调用相应的构造函数。
聚合类型:
1. 类型是一个普通数组,如int[5],char[],double[]等
2. 类型是一个类,且满足以下条件:
① 没有用户声明的构造函数
② 没有用户提供的构造函数(允许显示预置或弃置的构造函数)
③ 没有私有或保护的非静态数据成员
④ 没有基类
⑤ 没有虚函数
⑥ 没有{}和=直接初始化的非静态数据成员
⑦ 没有默认成员初始化器
std::initializer_list,可以接收任意长度的初始化列表,但是里面必须是相同类型T,或者都可以转换为T
struct CustomVec {
std::vector<int> data;
CustomVec(std::initializer_list<int> list) {
for (auto iter = list.begin(); iter != list.end(); ++iter) {
data.push_back(*iter);
}
}
};
- 列表初始化优点
① 方便,且基本上可以替代括号初始化
② 可以使用初始化列表接受任意长度
③ 可以防止类型窄化,避免精度丢失的隐式类型转换
4. enum class(有作用域的枚举类型)
不带作用域的枚举类型可以自动转换成整形,且不同的枚举可以相互比较,这都是潜在的难以调试的bug,可以通过有作用域的枚举来规避。使用带有作用域的枚举类型后,对不同的枚举进行比较会导致编译失败,消除潜在bug,同时带作用域的枚举类型可以选择底层类型,默认是int,可以改成char等别的类型。
enum class AColor {
kRed,
kGreen,
kBlue
};
enum class BColor {
kWhite,
kBlack,
kYellow
};
int main() {
if (AColor::kRed == BColor::kWhite) { // 编译失败
cout << "red == white" << endl;
}
return 0;
}
//带有作用域的枚举类型
enum class AColor : char {
kRed,
kGreen,
kBlue
};
5. sizeof
c++ 11中sizeof可以用到类的数据成员上
struct A {
int data[10];
int a;
};
int main() {
//C++ 11前
A a;
cout << "size " << sizeof(a.data) << endl;
//C++ 11后,不需要定义一个对象,再去计算对象的成员大小
cout << "size " << sizeof(A::data) << endl;
return 0;
}
6. static_assert(true/false, message)
用于在编译期间检查,如果第一个参数值为false,则打印message,编译失败
7. 新增算法