C++11新特性深度解析
目录
一、统一的初始化语法
1.1 基本使用示例
1.2 对象初始化
二、initializer_list容器
2.1 标准容器支持
2.2 自定义容器实现编辑
三、类型推导与decltype
3.1 auto关键字
3.2 decltype类型推导
四、nullptr替代NULL
五、array容器
六、右值引用与移动语义
6.1 左右值概念
6.2 移动构造函数与移动赋值
6.3 push_back 的拷贝与移动
6.4 完美转发
6.4.1左值引用与右值引用
6.4.2 万能引用与引用折叠
七、 Lambda 表达式与排序
7.1 传统仿函数 vs. Lambda
7.2 Lambda 的灵活应用
八、可变参数模板
8.1 基本概念
示例:递归展开参数包
8.2. 可变参数模板的应用
8.2.1 灵活构造对象
8.2.2 emplace_back vs. push_back
8.3 移动语义的默认行为
8.3.1 默认生成的移动操作
九、 包装器 (function)
9.1示例:统计调用次数
9.2使用 std::function 优化
十、 参数绑定(bind)
10.1 调整参数顺序与固定参数
10.2 绑定成员函数
10.3 实现多策略计算器
一、统一的初始化语法
C++11引入了统一初始化语法,允许使用花括号{}
进行各种初始化操作,使代码更加一致和清晰。
1.1 基本使用示例
int x = 1; // 传统初始化
int y = {2}; // 使用初始化列表
int z{3}; // 直接列表初始化int a1[] = {1,2,3}; // 传统数组初始化
int a2[] {1,2,3}; // 列表初始化数组
1.2 对象初始化
struct Point {Point(int x, int y) : _x(x), _y(y) {cout << "Point构造" << endl;}int _x, _y;
};Point p0(0, 0); // 传统构造
Point p1 = {1,1}; // 隐式类型转换
Point p2{2,2}; // 直接列表初始化
const Point& r = {3,3}; // 临时对象绑定到const引用
二、initializer_list容器
C++11引入了initializer_list
类型,支持使用初始化列表构造容器。
2.1 标准容器支持
vector<int> v1 = {1,2,3,4,5}; // 调用initializer_list构造函数
map<string, string> dict = { {"sort", "排序"}, {"left", "左边"}
};
2.2 自定义容器实现
// 自定义vector添加initializer_list支持
template<class T>
class vector {
public:vector(initializer_list<T> il) {reserve(il.size());for(auto& e : il) {push_back(e);}}// ...
};
三、类型推导与decltype
3.1 auto关键字
auto i = 10; // int
auto p = &i; // int*
auto pf = malloc; // void*(*)(size_t)
vector<int> v;
auto it = v.begin(); // vector<int>::iterator
3.2 decltype类型推导
int x = 1;
double y = 2.2;decltype(x) a; // int
decltype(x*y) b; // double
decltype(pf) pf2; // 与pf相同类型template<class Func>
class B {Func _f; // 使用decltype作为模板参数
};
B<decltype(pf)> bb1; // 实例化模板
四、nullptr替代NULL
C++11引入nullptr
解决NULL的二义性问题:
NULL是宏定义的0
找不到nullptr的宏定义,但是从下图知道,nullptr是(void*) 0
void func(int) {}
void func(int*) {}int main() {func(NULL); // 调用func(int)func(nullptr); // 调用func(int*)return 0;
}
五、array容器
C++11提供了更安全的固定大小数组替代品:
区别就是有无报错
int a1[10]; // 传统C数组
array<int,10> a2; // C++11 array容器cout << sizeof(a1) << endl; // 40 (假设int为4字节)
cout << sizeof(a2) << endl; // 40a1[15] = 1; // 未定义行为
a2[15] = 1; // 运行时抛出异常(调试模式下)
六、右值引用与移动语义
6.1 左右值概念
左值(Lvalue):有名字、可寻址的对象(如变量、数组元素)。
右值(Rvalue):临时对象、字面量、表达式结果(如 10
、x + y
)。
我的理解:能取地址的是左值,不能取地址的是右值
int a = 10; // a是左值
int* p = &a; // 可以取地址
10; // 右值
a + 10; // 右值
fmin(a, 10); // 右值
6.2 移动构造函数与移动赋值
移动语义通过“窃取”资源(如动态内存)来优化性能。
示例代码(string
类)
string(string&& s) : _str(nullptr) {cout << "移动构造" << endl;swap(s); // 直接交换资源,避免深拷贝
}string& operator=(string&& s) {cout << "移动赋值" << endl;swap(s);return *this;
}
6.3 push_back
的拷贝与移动
list<bit::string> lt;
bit::string s1("111111111111111111111");
lt.push_back(s1); // 调用拷贝构造bit::string s2("111111111111111111111");
lt.push_back(move(s2)); // 调用移动构造lt.push_back("22222222222222222222222222222"); // 直接构造(最优)
说明:
push_back(s1)
触发拷贝构造(深拷贝)
push_back(move(s2))
触发移动构造(资源转移)
push_back("...")
直接构造(最优化,避免临时对象)
6.4 完美转发
6.4.1左值引用与右值引用
double x = 1.1, y = 2.2;// 左值引用:给左值取别名int a = 0;int& r1 = a;// 左值引用能否给右值取别名?// const左值引用可以const int& r2 = 10;const double& r3 = x + y;// 右值引用:给右值取别名int&& r5 = 10;double&& r6 = x + y;// 右值引用能否给左值取别名?// 右值引用可以引用move以后的左值int&& r7 = move(a);
6.4.2 万能引用与引用折叠
template<typename T>
void PerfectForward(T&& t) {Fun(forward<T>(t)); // 保持左值/右值属性
}
示例调用:
PerfectForward(10); // 右值引用
int a;
PerfectForward(a); // 左值引用
PerfectForward(move(a)); // 右值引用
const int b = 8;
PerfectForward(b); // const 左值引用
PerfectForward(move(b)); // const 右值引用
T&&
是万能引用,根据实参推导左值/右值
std::forward<T>
保持参数的原始类型(避免不必要的拷贝)
七、 Lambda 表达式与排序
7.1 传统仿函数 vs. Lambda
// 传统仿函数
struct ComparePriceLess {bool operator()(const Goods& x, const Goods& y) {return x._price < y._price;}
};
sort(v.begin(), v.end(), ComparePriceLess());// Lambda 表达式
sort(v.begin(), v.end(), [](const Goods& x, const Goods& y) {return x._price < y._price;
});
7.2 Lambda 的灵活应用
vector<Goods> v = { {"苹果", 2.1, 5}, {"香蕉", 3, 4}, {"橙子", 2.2, 3} };// 按价格升序
sort(v.begin(), v.end(), [](auto& x, auto& y) { return x._price < y._price; });// 按评价降序
sort(v.begin(), v.end(), [](auto& x, auto& y) { return x._evaluate > y._evaluate; });
八、可变参数模板
8.1 基本概念
可变参数模板允许函数/类接受任意数量、任意类型的参数包
示例:递归展开参数包
void _ShowList() { cout << endl; } // 终止条件template <class T, class... Args>
void _ShowList(T val, Args... args) {cout << val << " ";_ShowList(args...); // 递归调用
}template <class... Args>
void CppPrint(Args... args) {_ShowList(args...);
}int main() {CppPrint(1, 2.2, "hello"); // 输出:1 2.2 helloreturn 0;
}
示例:数组展开参数包
template <class T>
int PrintArg(T t) {cout << t << " ";return 0;
}template <class... Args>
void CppPrint(Args... args) {int arr[] = { PrintArg(args)... }; // 展开为 {PrintArg(1), PrintArg(2.2), ...}cout << endl;
}
8.2. 可变参数模板的应用
8.2.1 灵活构造对象
template <class... Args>
Date* Create(Args... args) {return new Date(args...); // 直接传递参数包构造对象
}int main() {Date* p1 = Create(); // 默认构造Date* p2 = Create(2023, 9, 27); // 多参数构造return 0;
}
8.2.2 emplace_back
vs. push_back
std::list<std::pair<int, string>> mylist;
mylist.emplace_back(10, "sort"); // 直接构造,无拷贝
mylist.push_back({20, "left"}); // 构造临时对象 + 移动构造
8.3 移动语义的默认行为
8.3.1 默认生成的移动操作
class Person {
public:Person(Person&&) = default; // 默认移动构造Person(const Person&) = default; // 默认拷贝构造
private:bit::string _name;int _age;
};int main() {Person s1;Person s2 = s1; // 拷贝构造Person s3 = move(s1); // 移动构造return 0;
}
如果你没有自己实现移动构造函数,且没有实现析构函数、铐贝构造、铐贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
九、 包装器 (function)
std::function
可以包装函数指针、仿函数、Lambda 等,提供统一的调用接口。
9.1示例:统计调用次数
template<class F, class T>
T useF(F f, T x) {static int count = 0;cout << "count:" << ++count << endl;return f(x);
}int main() {// 函数指针cout << useF(f, 11.11) << endl; // count:1// 仿函数cout << useF(Functor(), 11.11) << endl; // count:1(不同实例)// Lambdacout << useF([](double d) { return d/4; }, 11.11) << endl; // count:1return 0;
}
问题:模板实例化导致多次计数(不同调用类型生成不同 useF
实例)。
9.2使用 std::function
优化
#include <functional>double useF(std::function<double(double)> f, double x) {static int count = 0;cout << "count:" << ++count << endl;return f(x);
}int main() {// 统一类型,共享静态变量useF(f, 11.11); // 函数指针useF(Functor(), 11.11); // 仿函数useF([](double d) { return d/4; }, 11.11); // Lambdareturn 0;
}
十、 参数绑定(bind)
10.1 调整参数顺序与固定参数
int Sub(int a, int b) { return a - b; }int main() {// 绑定参数顺序:原函数Sub(a,b) -> 新函数Sub(b,a)auto reversedSub = bind(Sub, placeholders::_2, placeholders::_1);cout << reversedSub(10, 5) << endl; // 输出-5(即5-10)// 固定部分参数auto subFrom100 = bind(Sub, 100, placeholders::_1);cout << subFrom100(30) << endl; // 输出70(100-30)return 0;
}
10.2 绑定成员函数
class Calculator {
public:static int Add(int a, int b) { return a + b; }int Multiply(int a, int b) { return a * b; }
};int main() {// 绑定静态成员函数auto add = bind(&Calculator::Add, placeholders::_1, placeholders::_2);cout << add(3, 4) << endl; // 输出7// 绑定普通成员函数(需对象实例)Calculator calc;auto multiply = bind(&Calculator::Multiply, &calc, placeholders::_1, placeholders::_2);cout << multiply(3, 4) << endl; // 输出12return 0;
}
10.3 实现多策略计算器
class MathProcessor {vector<function<double(double, double)>> operations;
public:void addOperation(function<double(double, double)> op) {operations.push_back(op);}void process(double x, double y) {for (auto& op : operations) {cout << "Result: " << op(x, y) << endl;}}
};int main() {MathProcessor processor;// 添加不同操作processor.addOperation([](double a, double b) { return a + b; });processor.addOperation(bind(Sub, placeholders::_1, placeholders::_2));processor.addOperation(bind(PPlus, placeholders::_1, 1.5, placeholders::_2));processor.process(10.0, 5.0);/* 输出:Result: 15Result: 5Result: 22.5*/return 0;
}