一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

C++11核心特性全解:从C++98到现代C++的全面升级(万字总结)

时间:2026-08-11 09:31:52 编辑:袖梨 来源:一聚教程网

观众老爷们大家好 我是邪修KING本文属于系列C++ 进阶篇,欢迎来到C++进阶篇博客C++重点语法运用!

一、C++11简介:为什么需要C++11?

C++11 是 C++ 的第⼆个主要版本,并且是从 C++98 起的最重要更新。它引⼊了⼤量更改,标准化了既

有实践,并改进了对 C++ 程序员可⽤的抽象。在它最终由 ISO 在 2011 年 8 ⽉ 12 ⽇采纳前,⼈们曾使

⽤名称“C++0x”,因为它曾被期待在 2010 年之前发布。C++03 与 C++11 期间花了 8 年时间,故⽽这

是迄今为⽌最⻓的版本间隔。从那时起,C++ 有规律地每 3 年更新⼀次。

稍作了解即可

C++11 是 C++ 发展史上第二个里程碑式的标准,也是自 C++98 发布以来最重要的一次版本更新。从 C++03 到 C++11 整整间隔了 8 年,这次更新不仅标准化了大量业界实践,还引入了移动语义、Lambda 表达式、可变参数模板、智能指针等一系列革命性特性,彻底改变了 C++ 的编程范式。从 C++11 开始,C++ 正式进入了「现代 C++」时代。

二、统一列表初始化 {}

其实一般情况下在学习的过程中我们小比特都有在用,这里性能优化有很大的方便性,减少了不必要的临时构造所产生的垃圾内存

1.1 C++98 的初始化痛点

C++98 中,{} 初始化只能用于数组和普通结构体,自定义类、容器都不能直接用列表初始化,初始化方式非常混乱。想要初始化一个带多个元素的 vector,只能先创建空对象,再一个个 push_back,代码繁琐且可读性差。

// ===== C++98 支持的初始化 =====

int arr1[] = {1, 2, 3, 4, 5}; // 数组可以

int arr2[5] = {0}; // 数组可以

struct Point { int x; int y; };

Point p = {1, 2}; // 结构体可以

// ===== C++98 不支持的初始化 =====

// vector<int> v = {1,2,3}; // 编译报错:容器不能直接列表初始化

// Date d = {2025, 1, 1}; // 编译报错:自定义类不能直接列表初始化

1.2 C++11 统一列表初始化

C++11 扩大了 {} 的适用范围,所有内置类型、自定义类型、容器都可以用花括号进行初始化,并且可以省略赋值号 =,真正实现了「一套语法初始化所有对象」。

#include <iostream>

#include <vector>

#include <string>

using namespace std;

class Date {

public:

Date(int year = 1, int month = 1, int day = 1)

: _year(year), _month(month), _day(day)

{

cout << "Date(int, int, int) 构造函数" << endl;

}

private:

int _year;

int _month;

int _day;

};

int main() {

// 1. 内置类型初始化

int x1 = {2}; // 传统带=写法

int x2{2}; // C++11 新增:省略=的写法

// 2. 自定义类型初始化

// 底层原理:先用{2025,1,1}构造临时对象,再拷贝构造,编译器会优化为直接构造

Date d1 = {2025, 1, 1};

Date d2{2024, 7, 25}; // 省略=,更简洁

// 3. 容器初始化 + 容器插入,便利性大幅提升

vector<int> v1 = {1, 2, 3, 4, 5}; // 直接批量初始化容器

vector<Date> v2;

v2.push_back({2025, 1, 1}); // 不用写匿名对象,直接传构造参数

return 0;

}

相比 C++98 的优势:

统一了所有类型的初始化语法,不用再记忆不同类型的初始化规则

容器插入多参数对象时代码更简洁,减少冗余的匿名对象写法

1.3 底层支撑:std::initializer_list

容器之所以能支持 {1,2,3,4,5} 这种批量初始化,本质是因为 C++11 为所有 STL 容器新增了 std::initializer_list 版本的构造函数和赋值函数。

std::initializer_list 是一个轻量级的类,底层对应一段连续的常量数组,内部用两个指针指向数组首尾。它可以自动接收 {…} 形式的初始化列表,支持迭代器遍历。

#include <initializer_list>

#include <iostream>

using namespace std;

// 模拟 vector 支持 initializer_list 构造与赋值

template<class T>

class my_vector {

public:

typedef T* iterator;

// initializer_list 构造函数:接收{}列表,逐个插入

my_vector(initializer_list<T> il) {

for (auto e : il) {

push_back(e);

}

}

// initializer_list 版本的赋值重载

my_vector& operator=(initializer_list<T> il) {

clear();

for (auto e : il) {

push_back(e);

}

return *this;

}

void push_back(const T& val) { /* 尾插实现省略 */ }

void clear() { /* 清空实现省略 */ }

private:

iterator _start = nullptr;

iterator _finish = nullptr;

iterator _endofstorage = nullptr;

};

int main() {

my_vector<int> v = {1,2,3,4,5}; // 自动调用 initializer_list 构造

v = {10,20,30,40,50}; // 自动调用 initializer_list 赋值

// 实际STL用法:map也支持initializer_list初始化

map<string, string> dict = {

{"sort", "排序"},

{"string", "字符串"},

{"insert", "插入"}

};

return 0;

}

使用场景:批量初始化容器、批量赋值、容器插入多元素时,用 initializer_list 语法最简洁直观。

二、右值引用与移动语义:从根源减少拷贝开销

右值引用是 C++11 最核心的性能优化特性,它彻底解决了 C++98 中深拷贝带来的不必要性能损耗,是现代 C++ 效率提升的关键。

2.1 先搞懂:左值和右值

区分左值和右值的核心标准:能不能取地址。

左值(lvalue):有持久存储状态、可以取地址的对象。比如普通变量、指针解引用、数组元素等。左值可以出现在赋值号左边或右边。

右值(rvalue):临时的、无法取地址的对象。比如字面常量、表达式结果、匿名临时对象等。右值只能出现在赋值号右边。

int main() {

// ===== 左值:可以取地址 =====

int a = 10;

int* p = new int(0);

string s("hello");

&a; &p; &s; // 都能取地址,都是左值

// ===== 右值:不能取地址 =====

10; // 字面常量

a + 1; // 表达式计算结果

string("hi");// 匿名临时对象

// &10; &(a+1); &string("hi"); // 编译报错:右值无法取地址

return 0;

}

2.2 左值引用 vs 右值引用

C++98 只有左值引用(Type&),C++11 新增右值引用(Type&&),二者本质都是给对象取别名,底层都是用指针实现。

引用类型可引用左值可引用右值说明左值引用 Type&YesNo只能绑定左值const 左值引用 const Type&YesYes通用引用,但不能修改对象右值引用 Type&&(move 后才可以)Yes只能绑定右值
int main() {

int a = 10;

// 左值引用:只能引用左值

int& r1 = a;

// int& r2 = 10; // 报错:左值引用不能直接绑定右值

// const 左值引用:左值右值都能绑定,但不能修改对象

const int& cr1 = a;

const int& cr2 = 10;

// 右值引用:只能绑定右值

int&& rr1 = 10;

int&& rr2 = a + 1;

// int&& rr3 = a; // 报错:右值引用不能直接绑定左值

// std::move:将左值强制转为右值,让右值引用可以绑定

int&& rr4 = move(a);

// 重要细节:右值引用变量本身是左值(可以取地址)

&rr1; // 合法,rr1是变量,是左值

// int&& rr5 = rr1; // 报错:rr1是左值,不能绑定右值引用

int&& rr5 = move(rr1); // 必须再move一次才能继续绑定右值引用

return 0;

}

2.3 移动构造与移动赋值:窃取资源代替深拷贝

C++98 中,类的拷贝构造和拷贝赋值都是深拷贝,对于 string、vector 这种管理堆资源的类,拷贝时需要重新申请内存、拷贝数据,代价非常大。

C++11 新增移动构造和移动赋值:参数是右值引用,本质是「窃取」临时对象 / 将亡对象的资源,不用重新申请内存、拷贝数据,性能提升显著。

下面以自定义 string 为例,完整对比拷贝与移动的实现:

#include <iostream>

#include <string.h>

#include <algorithm>

using namespace std;

namespace bit {

class string {

public:

// 普通构造函数

string(const char* str = "")

: _size(strlen(str))

, _capacity(_size)

{

_str = new char[_capacity + 1];

strcpy(_str, str);

cout << "string(const char*) -- 普通构造" << endl;

}

// ===== C++98 就有:拷贝构造(深拷贝,代价大)=====

string(const string& s)

: _size(s._size)

, _capacity(s._capacity)

{

_str = new char[_capacity + 1];

strcpy(_str, s._str);

cout << "string(const string&) -- 拷贝构造【深拷贝,O(n)开销】" << endl;

}

// ===== C++11 新增:移动构造(交换资源,代价极小)=====

// 参数是右值引用,接收临时对象/将亡对象

string(string&& s)

: _str(nullptr)

, _size(0)

, _capacity(0)

{

// 直接交换资源,把s的内存偷过来,不用深拷贝

swap(s);

cout << "string(string&&) -- 移动构造【交换指针,O(1)开销】" << endl;

}

// ===== C++98 就有:拷贝赋值(深拷贝)=====

string& operator=(const string& s) {

if (this != &s) {

char* tmp = new char[s._capacity + 1];

strcpy(tmp, s._str);

delete[] _str;

_str = tmp;

_size = s._size;

_capacity = s._capacity;

}

cout << "string& operator=(const string&) -- 拷贝赋值" << endl;

return *this;

}

// ===== C++11 新增:移动赋值=====

string& operator=(string&& s) {

if (this != &s) {

swap(s); // 交换资源

}

cout << "string& operator=(string&&) -- 移动赋值" << endl;

return *this;

}

// 交换成员

void swap(string& s) {

std::swap(_str, s._str);

std::swap(_size, s._size);

std::swap(_capacity, s._capacity);

}

~string() {

delete[] _str;

_str = nullptr;

}

const char* c_str() const { return _str; }

private:

char* _str = nullptr;

size_t _size = 0;

size_t _capacity = 0;

};

}

场景 1:用临时对象初始化

int main() {

bit::string s1("hello");

// 用左值s1初始化s2 → 调用拷贝构造(深拷贝,完整复制数据)

bit::string s2 = s1;

// 用匿名临时对象初始化s3 → 调用移动构造(交换指针,零深拷贝)

bit::string s3 = bit::string("world");

// 用move后的左值初始化s4 → 调用移动构造(s1的资源被转移,变成空串)

bit::string s4 = move(s1);

return 0;

}

场景 2:函数传值返回(最常用的性能优化场景)

C++98 中,函数返回局部对象时,会发生两次深拷贝(局部对象→临时对象→接收对象),即使编译器优化也至少有一次深拷贝。

有了移动语义后,返回局部对象会自动调用移动构造,几乎零开销,再也不用靠输出型参数规避拷贝了。

bit::string addStrings(bit::string num1, bit::string num2) {

bit::string str;

// ... 省略字符串相加逻辑 ...

return str; // str是局部对象,出作用域就销毁,属于将亡值

}

int main() {

// C++98:至少1次深拷贝,大对象性能损耗严重

// C++11:自动调用移动构造,只交换指针,性能大幅提升

bit::string ret = addStrings("111", "222");

return 0;

}

相比 C++98 的优势:

对于管理堆内存的类(string、vector、map 等),移动语义将拷贝的 O (n) 开销降为 O (1)

函数返回大对象时,不再需要用输出型参数规避拷贝,代码更优雅自然

2.4 引用折叠与万能引用

模板中的 T&& 不总是右值引用,它会根据实参的类型发生引用折叠

传入左值 → T 推导为 int& → int& && 折叠为 int&(左值引用)

传入右值 → T 推导为 int → int&& 就是右值引用

这种既能接收左值又能接收右值的模板参数,叫做万能引用(Universal Reference)

// 万能引用模板:传左值实例化为左值引用,传右值实例化为右值引用

template<class T>

void Func(T&& t) {

// ...

}

int main() {

int a = 10;

Func(a); // 左值 → T=int& → t是int&类型

Func(10); // 右值 → T=int → t是int&&类型

return 0;

}

2.5 完美转发:保持参数的值属性

万能引用有个问题:右值引用变量本身是左值,把它传给下一层函数时,会丢失右值属性,永远匹配左值引用版本。

完美转发 std::forward 可以保持参数原本的左 / 右值属性:左值传进去还是左值,右值传进去还是右值。

#include <iostream>

#include <utility>

using namespace std;

void Fun(int& x) { cout << "左值引用重载" << endl; }

void Fun(const int& x) { cout << "const 左值引用重载" << endl; }

void Fun(int&& x) { cout << "右值引用重载" << endl; }

template<class T>

void Function(T&& t) {

// Fun(t); // 永远调用左值版本,因为t是变量,表达式属性是左值

Fun(forward<T>(t)); // 完美转发:保持t原本的左右值属性

}

int main() {

int a = 10;

Function(a); // 实参是左值 → 调用左值版本

Function(10); // 实参是右值 → 调用右值版本

Function(move(a)); // 实参是右值 → 调用右值版本

return 0;

}

使用场景:写模板函数、封装接口时,需要把参数原封不动地转发给下层函数,就用完美转发。

三、可变参数模板:让参数数量和类型都能泛化

C++98 的模板只能固定参数个数,C++11 新增可变参数模板,支持任意个数、任意类型的参数,是 STL emplace 系列接口的底层支撑。

3.1 基本语法

用 … 表示参数包:

class… Args:模板参数包,表示 0 个或多个类型

Args… args:函数参数包,表示 0 个或多个参数对象

#include <iostream>

using namespace std;

// sizeof... 计算参数包中参数的个数

template<class ...Args>

void Print(Args... args) {

cout << "参数包个数:" << sizeof...(args) << endl;

}

int main() {

Print(); // 0个参数

Print(1); // 1个参数

Print(1, string("hi")); // 2个参数

Print(1, "hello", 2.2); // 3个参数

return 0;

}

3.2 参数包的展开:递归方式

可变参数包不能直接遍历,通常用递归方式逐层展开:先取出第一个参数处理,剩下的参数包继续递归,直到参数包为空匹配终止函数。

#include <iostream>

#include <string>

using namespace std;

// 递归终止函数:参数包为空时调用

void ShowList() {

cout << endl;

}

// 递归展开函数:取出第一个参数val,剩下的args继续递归

template<class T, class ...Args>

void ShowList(T val, Args... args) {

cout << val << " ";

ShowList(args...); // 剩下的参数包继续传递

}

template<class ...Args>

void Print(Args... args) {

ShowList(args...);

}

int main() {

Print(1, "hello", 2.2, 'a'); // 输出:1 hello 2.2 a

return 0;

}

编译时编译器会自动推导出对应参数个数的函数,本质还是生成多个重载函数。

3.3 实际应用:emplace 系列接口

C++11 为所有 STL 容器新增了 emplace_back、emplace 接口,底层就是可变参数模板 + 完美转发。

相比 push_back,emplace 可以直接把构造参数传给元素的构造函数,在容器节点上直接构造对象,减少一次拷贝 / 移动开销。

#include <list>

int main() {

list<bit::string> lt;

bit::string s1("hello");

// 1. 传左值 → 和push_back效果一致,调用拷贝构造

lt.emplace_back(s1);

// 2. 传右值 → 和push_back效果一致,调用移动构造

lt.emplace_back(move(s1));

// 3. 直接传构造参数 → 在节点上直接构造,连移动都省了!

// 这是push_back做不到的,也是emplace最大的优势

lt.emplace_back("world");

list<pair<bit::string, int>> lt2;

// 直接传pair的构造参数,不用先构造pair对象再插入

lt2.emplace_back("苹果", 5);

return 0;

}

优势:

减少了一次临时对象的构造 + 析构,性能更优

日常开发推荐优先用 emplace 替代 push/insert

四、类的新功能:更灵活的默认函数控制

4.1 默认生成的移动构造和移动赋值

C++98 类有 4 个默认成员函数(构造、析构、拷贝构造、拷贝赋值),C++11 新增 2 个:移动构造、移动赋值。

编译器自动生成默认移动构造 / 赋值的条件:

你没有自己实现移动构造、移动赋值

你也没有自己实现析构函数、拷贝构造、拷贝赋值

默认生成的移动构造:内置类型逐字节拷贝,自定义类型调用它的移动构造(没有就调用拷贝构造)

4.2 default 与 delete

= default:显式要求编译器生成默认版本的函数

= delete:禁止编译器生成该函数,比 C++98 设为 private 更简洁直观

class Person {

public:

Person(const char* name = "", int age = 0)

: _name(name), _age(age)

{}

// 自己写了拷贝构造,编译器就不会自动生成移动构造

Person(const Person& p)

: _name(p._name), _age(p._age)

{}

// 显式要求编译器生成默认版本的移动构造

Person(Person&& p) = default;

// 禁止拷贝赋值(C++11 写法,比C++98设private更清晰)

Person& operator=(const Person& p) = delete;

private:

bit::string _name;

int _age;

};

4.3 成员变量缺省值

C++11 允许在类内声明成员变量时直接给默认值,如果初始化列表没有显式初始化该成员,就会使用这个缺省值。

class A {

private:

// 声明时直接给缺省值

int _a = 0;

int* _p = nullptr;

bit::string _s = "default";

};

五、STL 的变化:新容器与新接口

5.1 新增容器

容器说明unordered_map / unordered_set哈希表实现,平均 O (1) 查找,无序,性能优于 map/setarray固定大小的静态数组,比原生数组更安全,支持迭代器forward_list单链表,比 list 更省内存,但只能单向遍历其中最常用的是 unordered_map/unordered_set,在不需要有序的场景下,性能比 map/set 高很多。

5.2 新增通用接口

所有容器都新增了右值引用版本的 push/insert、移动构造、移动赋值

所有容器都新增了 initializer_list 版本的构造和赋值

新增 emplace / emplace_back 系列接口

支持范围 for 遍历(底层封装迭代器,语法更简洁)

六、Lambda 表达式:写匿名函数的利器

6.1 为什么需要 Lambda

C++98 中,如果想给算法传一个比较规则,要么用函数指针,要么写仿函数类。仿函数需要单独定义类,代码分散、非常繁琐。

C++11 的 Lambda 表达式可以在函数内部定义匿名函数对象,写起来灵活轻便,是 STL 算法的最佳搭档。

6.2 语法格式+例子

[捕捉列表]:捕获外层作用域的变量供 Lambda 内部使用,不能为空

(参数列表):和普通函数参数一样,没有参数可以省略 ()

mutable:取消值捕捉的 const 属性,让值捕捉的变量可以修改

-> 返回值类型:追踪返回类型,一般可以省略,编译器自动推导

{函数体}:实现逻辑,不能为空

[捕捉列表] (参数列表) mutable -> 返回值类型 { 函数体 }

int main() {

int a = 0, b = 1, c = 2, d = 3;

// 1. 值捕捉a,引用捕捉b

auto func1 = [a, &b]() {

// a++; // 报错:值捕捉的变量默认是const的,不能修改

b++; // 引用捕捉可以修改,直接影响外面的b

return a + b;

};

cout << func1() << endl;

// 2. 加mutable:值捕捉的变量也能改(改的是内部拷贝,不影响外面)

auto func2 = [=]() mutable {

a++; b++; c++;

return a + b + c;

};

cout << func2() << endl;

// 3. 隐式引用捕捉:所有用到的变量都引用捕捉

auto func3 = [&]() {

a++; b++; c++; d++; // 都能改,都影响外部变量

};

func3();

return 0;

}

6.4 典型应用:sort 自定义排序

C++98 要为每种排序规则写一个仿函数类,C++11 直接写 Lambda 即可,代码集中、可读性强。

#include <vector>

#include <algorithm>

#include <string>

using namespace std;

struct Goods {

string _name;

double _price;

int _evaluate;

Goods(const char* name, double price, int evaluate)

: _name(name), _price(price), _evaluate(evaluate)

{}

};

int main() {

vector<Goods> v = {

{"苹果", 2.1, 5},

{"香蕉", 3.0, 4},

{"橙子", 2.2, 3},

{"菠萝", 1.5, 4}

};

// 按价格升序

sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {

return g1._price < g2._price;

});

// 按评价降序

sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {

return g1._evaluate > g2._evaluate;

});

return 0;

}

6.5 Lambda 底层原理

Lambda 本质就是仿函数:编译器会为每个 Lambda 生成一个唯一的匿名类,重载了 operator()。捕捉列表的变量就是这个类的成员变量,构造 Lambda 对象时把捕捉的变量初始化进去。

所以 Lambda 对象的大小、行为和仿函数完全一致,只是语法更简洁。

七、包装器 function 与 bind:统一可调用对象

7.1 std::function:统一类型

C++ 中可调用对象有很多种:普通函数、仿函数、Lambda、成员函数…… 它们类型各不相同,无法统一管理。

std::function 是一个包装器,可以把所有签名相同的可调用对象包装成同一个类型,方便统一存储、传递和管理。

头文件:

#include <functional>

#include <iostream>

#include <string>

using namespace std;

// 普通函数

int add(int a, int b) { return a + b; }

// 仿函数

struct Add {

int operator()(int a, int b) { return a + b; }

};

class Calc {

public:

static int plus_static(int a, int b) { return a + b; } // 静态成员函数

int plus_normal(int a, int b) { return a + b; } // 普通成员函数

};

int main() {

// 包装普通函数

function<int(int, int)> f1 = add;

// 包装仿函数

function<int(int, int)> f2 = Add();

// 包装Lambda

function<int(int, int)> f3 = [](int a, int b) { return a + b; };

// 包装静态成员函数

function<int(int, int)> f4 = &Calc::plus_static;

// 包装普通成员函数(需要绑定对象)

Calc c;

function<int(int, int)> f5 = bind(&Calc::plus_normal, &c, placeholders::_1, placeholders::_2);

cout << f1(1, 2) << endl;

cout << f2(1, 2) << endl;

cout << f3(1, 2) << endl;

return 0;

}

经典应用:命令映射表

用 map + function 实现字符串到函数的映射,替代冗长的 switch-case,扩展性极强,符合开闭原则。

// LeetCode 150. 逆波兰表达式求值

#include <vector>

#include <stack>

#include <map>

#include <functional>

#include <string>

using namespace std;

class Solution {

public:

int evalRPN(vector<string>& tokens) {

stack<int> st;

// 用map映射操作符和对应的运算函数

map<string, function<int(int, int)>> opMap = {

{"+", [](int x, int y) { return x + y; }},

{"-", [](int x, int y) { return x - y; }},

{"*", [](int x, int y) { return x * y; }},

{"/", [](int x, int y) { return x / y; }}

};

for (auto& s : tokens) {

if (opMap.count(s)) {

int right = st.top(); st.pop();

int left = st.top(); st.pop();

st.push(opMap[s](left, right));

} else {

st.push(stoi(s));

}

}

return st.top();

}

};

优势:新增运算只需要在 map 里加一行映射,不用修改核心逻辑,扩展性远优于 switch。

7.2 std::bind:调整参数

std::bind 是函数适配器,可以调整可调用对象的参数个数、参数顺序,还能绑死固定参数。

占位符 placeholders::_1、_2 表示新函数的第 1、2 个参数。

#include <functional>

#include <iostream>

using namespace std;

using namespace placeholders;

int Sub(int a, int b) {

return (a - b) * 10;

}

int main() {

// 1. 原封不动包装

auto sub1 = bind(Sub, _1, _2);

cout << sub1(10, 5) << endl; // 输出50

// 2. 调换参数顺序

auto sub2 = bind(Sub, _2, _1);

cout << sub2(10, 5) << endl; // 输出-50

// 3. 绑死第一个参数,只留一个参数

auto sub3 = bind(Sub, 100, _1);

cout << sub3(5) << endl; // 输出950

return 0;

}

使用场景:函数参数太多、需要固定部分参数简化调用时,用 bind 非常方便。

八、其他重要特性

8.1 智能指针

C++11 新增了 unique_ptr、shared_ptr、weak_ptr 三大智能指针,利用 RAII 机制自动管理堆内存,从根源解决内存泄漏问题。这部分内容较多,通常单独成章深入讲解。

8.2 constexpr 常量表达式

让函数、变量在编译期就能计算出结果,将运行时开销转移到编译期,提升程序运行性能。

8.3 范围 for

底层封装迭代器,让容器遍历更简洁直观

vector<int> v = {1,2,3,4,5};

for (auto e : v) { // 自动遍历所有元素

cout << e << " ";

}

九、总结

C++11 不是简单的语法糖堆砌,而是一次全方位的革命性升级:

性能层面:移动语义从根源减少了深拷贝开销,让 C++ 程序性能再上一个台阶

开发效率层面:列表初始化、Lambda、可变参数模板、范围 for 等特性,大幅简化了代码,提升了开发效率

安全层面:智能指针解决了内存泄漏难题,=delete 等语法让类的设计更安全

泛型能力层面:可变参数模板、完美转发让泛型编程更灵活、更强大

掌握了 C++11,才算真正踏入现代 C++ 的大门。后续的 C++14/17/20 都是在这个基础上的迭代和完善。

标签 #c++ #开发语言

热门栏目