1 C++11 发展历史
1.1 版本基础信息
解释:C++98是最早标准,C++11是改动最大的一次升级,原本计划2010年前发布,代号C++0x,2011年正式定稿。C++03到C++11间隔8年,之后标准固定每3年更新一次。
1.2 C++11 新增核心大类功能
统一初始化、auto/decltype自动类型推导、lambda匿名函数、constexpr编译期常量、右值引用&移动语义、可变参数模板、智能指针、多线程、正则、全新类语法、STL容器拓展、函数包装器function/bind。
2 列表初始化(统一初始化 {})
2.1 C++98 旧版{}限制
解释:老标准只有数组、结构体能用{}初始化,普通类不能用,初始化方式不统一。
示例:
cpp
#include <iostream>
using namespace std;
struct Point { int x, y; };
int main()
{
int arr[5] = {1,2,3,4,5}; // 数组支持
Point p = {10,20}; // 结构体支持
return 0;
}
2.2 C++11 统一列表初始化
2.2.1 核心规则
解释:所有类型(内置/类/引用)都能用{}初始化 ,等号=可以省略;编译器会自动构造临时对象,并且优化掉拷贝,效率更高。
2.2.2 完整示例
cpp
#include <iostream>
#include <vector>
using namespace std;
class Date
{
public:
Date(int y=1, int m=1, int d=1)
{
cout << "构造函数执行" << endl;
}
Date(const Date&) { cout << "拷贝构造执行" << endl; }
};
int main()
{
// 1.内置类型,可省略=
int a{10};
int b = {20};
// 2.自定义类两种写法
Date d1{2026,8,7};
Date d2 = {2026,1,1};
// 3.const引用绑定临时对象
const Date& ref{2025,12,1};
// 4.容器传参简化,不用写Date(xxx)
vector<Date> v;
v.push_back({2024,5,6});
return 0;
}
运行说明:不会打印拷贝构造,编译器直接优化构造,没有临时对象拷贝。
2.3 std::initializer_list
2.3.1 通俗解释
{1,2,3,4} 这种多值列表底层会转成initializer_list,内部存一个数组+首尾指针,STL容器全部加了对应构造函数,支持一次性批量初始化容器。
2.3.2 示例
cpp
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
// vector直接批量初始化
vector<int> v = {1,2,3,4,5};
vector<int> v2({10,20,30});
// map批量键值对初始化
map<string, int> mp = {{"苹果",5},{"香蕉",3}};
// 列表赋值
v = {100,200,300};
return 0;
}
3 右值引用与移动语义(解决深拷贝效率问题)
3.1 左值、右值区分(初学者必懂)
3.1.1 左值
解释:有名字、能取地址,能放在=左边。变量、解引用指针、数组元素都是左值。
3.1.2 右值
解释:没有持久内存、不能取地址,只能放在=右边。数字字面量、表达式结果、函数返回临时对象都是右值。
示例:
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a = 10;
// a是左值,可以取地址
cout << &a << endl;
// 10、a+20、string("test")都是右值,不能取地址
// cout << &10; 报错
// cout << &(a+20); 报错
// cout << &string("test"); 报错
return 0;
}
3.2 左值引用T&、右值引用T&&
3.2.1 基础规则
T&左值引用:只能绑定左值;const T&可以绑定左值+右值T&&右值引用:只能绑定右值;左值想要绑定必须用std::move(变量)转换- 重点坑:右值引用变量本身是左值,再次传递需要move
3.2.2 示例
cpp
#include <iostream>
using namespace std;
int main()
{
int x = 10;
// 左值引用绑定左值
int& r1 = x;
// const左值引用绑定右值
const int& r2 = 100;
// 右值引用绑定右值
int&& r3 = 200;
// 左值转右值再绑定
int&& r4 = move(x);
// 坑:r3是右值引用变量,自身是左值,直接绑定报错
// int&& r5 = r3; 报错
int&& r5 = move(r3); // 正确
return 0;
}
3.3 引用延长临时对象生命周期
解释:普通临时对象一行代码结束就销毁;const T&、T&&绑定临时变量,会延长临时对象生命周期到引用变量销毁。
示例:
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1 = "hello";
// 临时对象s1+s1生命周期延长
string&& r = s1 + s1;
r += " world"; // 非const右值引用可修改
cout << r << endl;
return 0;
}
3.4 重载函数自动匹配规则
解释:函数同时写三种重载,编译器自动根据实参类型匹配对应版本:左值匹配T&、const左值匹配const T&、右值匹配T&&。
示例:
cpp
#include <iostream>
using namespace std;
void func(int& x) { cout << "匹配左值引用" << endl; }
void func(const int& x) { cout << "匹配const左值引用" << endl; }
void func(int&& x) { cout << "匹配右值引用" << endl; }
int main()
{
int a = 10;
const int b = 20;
func(a); // 左值
func(b); // const左值
func(30); // 纯右值
func(move(a)); // move后右值
return 0;
}
3.5 移动构造、移动赋值(核心提效)
3.5.1 通俗解释
像string/vector这种内部开堆内存的类,拷贝构造会完整复制堆内存(慢);移动构造直接偷临时对象的堆内存指针,不用重新分配内存,速度大幅提升。
- 移动构造:
类名(类名&& 临时对象) - 移动赋值:
类名& operator=(类名&& 临时对象)
3.5.2 极简模拟string示例
cpp
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
private:
char* _str;
public:
// 普通构造
MyString(const char* s)
{
cout << "普通构造" << endl;
_str = new char[strlen(s)+1];
strcpy(_str, s);
}
// 拷贝构造(深拷贝,慢)
MyString(const MyString& s)
{
cout << "拷贝构造" << endl;
_str = new char[strlen(s._str)+1];
strcpy(_str, s._str);
}
// 移动构造(偷资源,快)
MyString(MyString&& s)
{
cout << "移动构造" << endl;
// 直接交换指针,不新开内存
swap(_str, s._str);
}
~MyString() { delete[] _str; }
};
MyString getStr()
{
return MyString("test"); // 返回临时右值,触发移动
}
int main()
{
MyString s1 = getStr();
MyString s2 = move(s1); // move转右值,调用移动构造
return 0;
}
3.6 值类别细分(了解即可)
- 纯右值prvalue:字面量、临时对象(C++98老式右值)
- 将亡值xvalue:
move(变量)、返回T&&函数结果 - 泛左值glvalue = 普通左值 + 将亡值
3.7 引用折叠 + 万能引用
3.7.1 折叠规则(死记)
只有 T&& && → T&&;其他所有引用组合全部折叠为左值引用T&。
3.7.2 万能引用:模板T&&
解释:模板参数写T&&不是单纯右值引用,实参传左值自动变成左值引用,传右值变成右值引用,叫万能引用。
示例:
cpp
#include <iostream>
using namespace std;
template<class T>
void test(T&& x) // 万能引用
{
}
int main()
{
int a = 10;
test(a); // 传左值 → T推导int& → 折叠int&
test(20); // 传右值 → T推导int → int&&
test(move(a));
return 0;
}
3.8 完美转发 std::forward
解释:万能引用接收右值后,变量本身是左值,向下传参会丢失右值属性;forward<T>(x)保留原本左/右值属性。
示例:
cpp
#include <iostream>
using namespace std;
void func(int&& x) { cout << "右值版本" << endl; }
template<class T>
void wrapper(T&& x)
{
// func(x); // 报错,x是左值
func(forward<T>(x)); // 正确,还原右值属性
}
int main()
{
wrapper(100);
return 0;
}
4 可变参数模板
4.1 基础语法
解释:template<class... Args>代表参数包 ,能接收任意数量、任意类型参数;sizeof...(args)编译期统计参数个数。
示例:
cpp
#include <iostream>
#include <string>
using namespace std;
template<class... Args>
void print(Args... args)
{
cout << "参数总个数:" << sizeof...(args) << endl;
}
int main()
{
print();
print(10);
print(10, "abc", 3.14);
return 0;
}
4.2 参数包递归展开
解释:不能用for循环遍历参数包,编译期递归拆解,需要一个无参终止函数。
示例:
cpp
#include <iostream>
using namespace std;
// 终止函数
void show() { cout << endl; }
// 递归拆解参数包
template<class T, class... Args>
void show(T val, Args... args)
{
cout << val << " ";
show(args...);
}
int main()
{
show(1,2,"hello",3.14);
return 0;
}
4.3 emplace系列容器接口(可变参数实际用途)
4.3.1 通俗解释
push_back:先创建临时对象,再移动/拷贝进容器;
emplace_back:直接把构造参数传给容器内部,原地构造对象,省去临时对象,效率更高。
4.3.2 示例
cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Student
{
public:
Student(string name, int age)
{
cout << "构造学生对象" << endl;
}
};
int main()
{
vector<Student> v;
// emplace直接传构造参数,不用构造临时Student
v.emplace_back("张三", 18);
// push_back需要先构造临时对象
v.push_back(Student("李四", 20));
return 0;
}
5 C++11 类新语法
5.1 自动生成移动构造/移动赋值
解释:C++98编译器默认生成4个函数:无参构造、析构、拷贝构造、拷贝赋值;C++11新增默认移动构造、移动赋值 。
生成条件:你没有手动写析构、拷贝构造、拷贝赋值任意一个,编译器自动生成移动函数。
5.2 类内成员缺省值
解释:声明成员变量时直接赋值,初始化列表没手动初始化时自动使用该默认值。
示例:
cpp
#include <iostream>
using namespace std;
class Person
{
private:
int age = 18; // 类内缺省值
public:
int getAge() { return age; }
};
int main()
{
Person p;
cout << p.getAge() << endl; // 输出18
return 0;
}
5.3 =default 和 =delete
=default:强制编译器生成默认函数(手动写了拷贝构造后,想要自动移动构造就用这个)=delete:直接禁用该函数,调用就编译报错,替代C++98私有禁用写法
示例:
cpp
class Person
{
public:
Person() = default; // 强制生成默认构造
Person(const Person&) = delete; // 禁用拷贝构造
};
int main()
{
Person p1;
// Person p2 = p1; 编译报错,拷贝构造被删除
return 0;
}
5.4 override / final
override:修饰虚函数,强制编译器检查是否正确重写父类虚函数,函数名写错直接报错final:修饰类→禁止被继承;修饰虚函数→禁止子类重写
示例:
cpp
#include <iostream>
using namespace std;
class Base
{
public:
virtual void func() {}
};
class Son : public Base
{
public:
// 正确重写,编译器校验
void func() override {}
// void fun() override {} 报错,父类没有fun虚函数
};
// final类,不能继承
class A final {};
// class B : public A {}; 报错
6 STL 容器更新
6.1 新增容器
std::array(固定大小数组)、forward_list(单向链表)、unordered_map/unordered_set(哈希表,日常最常用)
6.2 通用新特性
- 全部支持
{}批量初始化(initializer_list) - 新增
emplace/emplace_back原地构造接口 push_back/insert支持右值引用版本,自动移动资源cbegin/cend常量迭代器,返回const迭代器- 支持范围for循环 ,简化遍历
范围for示例:
cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v = {1,2,3,4,5};
// 范围for遍历
for(auto x : v)
{
cout << x << " ";
}
return 0;
}
7 Lambda 匿名函数(初学者高频使用)
7.1 完整语法
[捕获列表](参数)->返回类型{函数体}
[]捕获列表:不能省略,抓取外层局部变量()参数:无参数可省略,用mutable时不能省->返回值:返回类型统一可省略,编译器自动推导{}函数体:不可省略
7.2 捕获规则(重点)
[a]值捕获:复制变量,lambda内是副本,不能修改[&a]引用捕获:直接操作外部原变量,可修改[=]隐式全部值捕获;[&]隐式全部引用捕获- 混合捕获:
[=, &x]其余值捕获,x引用;[&, x]其余引用,x值捕获 mutable:解除值捕获变量const限制,只能修改内部副本,不影响外部
7.3 完整示例
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v = {3,1,4,2};
int base = 10;
// lambda排序,[=]值捕获base
sort(v.begin(), v.end(), [=](int a, int b)
{
return a + base < b + base;
});
for(auto x : v) cout << x << " ";
// mutable修改副本
int num = 100;
auto f = [num]()mutable
{
num++;
cout << num << endl;
};
f();
cout << num << endl; // 外部num不变,依旧100
return 0;
}
7.4 底层原理
编译器自动生成一个匿名仿函数类,捕获的变量作为类成员;调用lambda等价于调用仿函数operator()。
8 可调用对象包装器 function & bind
8.1 std::function
8.1.1 解释
头文件<functional>,统一包装所有可调用对象(普通函数、lambda、仿函数、成员函数),统一类型方便存储、传参。
8.1.2 示例
cpp
#include <iostream>
#include <functional>
using namespace std;
int add(int a, int b) { return a+b; }
int main()
{
// 包装普通函数
function<int(int,int)> f1 = add;
// 包装lambda
function<int(int,int)> f2 = [](int a,int b){return a*b;};
cout << f1(2,3) << endl;
cout << f2(2,3) << endl;
return 0;
}
8.2 std::bind 函数适配器
8.2.1 解释
绑定固定参数、调整参数顺序、减少参数数量;_1/_2是占位符,代表新函数的参数。
8.2.2 示例
cpp
#include <iostream>
#include <functional>
using namespace std;
using placeholders::_1;
using placeholders::_2;
int sub(int a, int b) { return a - b; }
int main()
{
// 颠倒参数顺序
auto f1 = bind(sub, _2, _1);
cout << f1(10,5) << endl; // 5-10=-5
// 固定第一个参数为100,只传第二个
auto f2 = bind(sub, 100, _1);
cout << f2(10) << endl; // 100-10=90
return 0;
}
9 其余C++11基础特性(简要入门介绍)
9.1 auto / decltype
auto:自动推导变量类型,不用手写复杂类型(迭代器、lambda常用)decltype(表达式):获取表达式对应的类型
cpp
auto a = 10;
decltype(a) b = 20;
9.2 constexpr
修饰常量/函数,编译期就能计算结果,运行期零开销。
9.3 智能指针
unique_ptr/shared_ptr/weak_ptr,自动释放堆内存,不用手动delete,杜绝内存泄漏。
9.4 标准库拓展
原生多线程、正则表达式库,不用第三方库即可实现并发、字符串匹配。