[C++入门]万字长文梳理 C++11 核心特性

目录

[1. C++11 简介](#1. C++11 简介)

[为什么会有 C++11?](#为什么会有 C++11?)

[2. 统一列表初始化:{} 初始化直接完成](#2. 统一列表初始化:{} 初始化直接完成)

[2.1 基础 {} 初始化语法](#2.1 基础 {} 初始化语法)

[2.2 std::initializer_list 容器初始化](#2.2 std::initializer_list 容器初始化)

[2.3 自定义类支持 {} 初始化](#2.3 自定义类支持 {} 初始化)

[3. 变量类型推导:auto/decltype/nullptr 解放手写类型](#3. 变量类型推导:auto/decltype/nullptr 解放手写类型)

[3.1 auto:自动类型推导(最常用)](#3.1 auto:自动类型推导(最常用))

[3.2 decltype:表达式类型推导](#3.2 decltype:表达式类型推导)

[3.3 nullptr:安全的空指针](#3.3 nullptr:安全的空指针)

[4. 范围 for 循环:极简容器遍历语法](#4. 范围 for 循环:极简容器遍历语法)

[5. final 与 override:继承多态的安全防护](#5. final 与 override:继承多态的安全防护)

[5.1 final:禁止继承 / 重写](#5.1 final:禁止继承 / 重写)

[6. 智能指针:自动化内存管理,杜绝内存泄漏](#6. 智能指针:自动化内存管理,杜绝内存泄漏)

[7. C++11 新容器:更高效、更实用的 STL 组件](#7. C++11 新容器:更高效、更实用的 STL 组件)

array:静态数组(安全版普通数组)

[7.2 forward_list:单向链表](#7.2 forward_list:单向链表)

[8. 右值引用与移动语义:C++11 最核心的性能革命](#8. 右值引用与移动语义:C++11 最核心的性能革命)

[8.1 左值与右值](#8.1 左值与右值)

[9. Lambda 表达式:就地匿名函数,简化回调逻辑](#9. Lambda 表达式:就地匿名函数,简化回调逻辑)

[9.1 Lambda 基础语法](#9.1 Lambda 基础语法)

[9.2 Lambda 实战:排序自定义类型](#9.2 Lambda 实战:排序自定义类型)

[10. 包装器 function 与 bind:统一可调用对象](#10. 包装器 function 与 bind:统一可调用对象)

[10.1 function:包装可调用对象](#10.1 function:包装可调用对象)

[10.2 bind:绑定参数 / 调整顺序](#10.2 bind:绑定参数 / 调整顺序)

[11. C++11 线程库:跨平台多线程编程](#11. C++11 线程库:跨平台多线程编程)

[11.1 基础线程创建](#11.1 基础线程创建)

[12.2 原子操作 atomic:无锁线程安全](#12.2 原子操作 atomic:无锁线程安全)

[12.3 lock_guard:RAII 互斥锁](#12.3 lock_guard:RAII 互斥锁)


1. C++11 简介

为什么会有 C++11?

在 C++11 之前,工业界长期使用C++98/03 标准。其中 C++98 是 C++ 第一版正式标准,C++03 仅对 C++98 做了漏洞修复,核心语法无任何改动。这就导致 C++ 在近 10 年时间里,语法繁琐、效率受限、内存安全差、缺乏原生并发支持,逐渐被 Java、Python 等语言挤压生存空间。

C++ 标准委员会原本计划 2007 年发布新版本,命名为 C++07,后因技术难度一再延期,更名为 C++0x(x 代表未知年份),最终在2011 年正式定稿,得名 C++11。

特点:

  1. 语法更简洁:减少冗余代码,提升开发效率
  2. 效率更高:右值引用、移动语义大幅降低拷贝开销
  3. 内存更安全:智能指针、nullptr 杜绝空指针与内存泄漏
  4. 功能更强大:原生线程库、哈希容器、Lambda 表达式适配现代开发
  5. 兼容性更好:完全兼容 C++98/03,平滑升级无压力

2. 统一列表初始化:{} 初始化直接完成

在 C++98 中,花括号{}仅能用于数组、结构体的初始化,自定义类型、动态内存都无法使用,初始化语法混乱。C++11 提出统一列表初始化 ,让{}成为所有类型的通用初始化方式,彻底统一初始化语法。

2.1 基础 {} 初始化语法

C++11 中,内置类型、自定义类型、数组、动态内存都能使用{}初始化,等号=可写可不写。

cpp 复制代码
#include <iostream>
using namespace std;

struct Point {
    int _x;
    int _y;
};

class Date {
public:
    Date(int year, int month, int day) 
        : _year(year), _month(month), _day(day) {}

    void Print() const {
        cout << _year << "年" << _month << "月" << _day << "日" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    int a{10};                 // 等价于 int a = 10;
    double b{3.14};            // 等价于 double b = 3.14;
    cout << "内置类型初始化:a = " << a << ",b = " << b << endl;

    int arr1[]{1, 2, 3, 4, 5};        
    int arr2[5]{0};                    // 全部初始化为0
    cout << "数组初始化:";
    for (int i = 0; i < 5; ++i) {
        cout << arr1[i] << " ";
    }
    cout << endl;

    Point p{10, 20};
    cout << "结构体初始化:x = " << p._x << ",y = " << p._y << endl;

    // 4. 类对象初始化(调用构造函数)
    Date d1{2026, 4, 23};
    Date d2 = {2026, 1, 1};   
    cout << "类对象初始化:";
    d1.Print();
    d2.Print();

  
    int* pa = new int[4]{1, 2, 3, 4};
    cout << "动态数组初始化:";
    for (int i = 0; i < 4; ++i) {
        cout << pa[i] << " ";
    }
    cout << endl;

    delete[] pa;
    return 0;
}

2.2 std::initializer_list 容器初始化

C++11 为 STL 容器新增了std::initializer_list 类型的构造函数与赋值运算符,容器可以直接用{}批量初始化、赋值,无需逐个 push_back。、

cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <map>
using namespace std;

int main() {
    // 1. vector初始化
    vector<int> v = {1, 2, 3, 4, 5};
    cout << "vector初始化:";
    for (auto x : v) {
        cout << x << " ";
    }
    cout << endl;

    // 2. list初始化
    list<int> lt{10, 20, 30};
    cout << "list初始化:";
    for (auto x : lt) {
        cout << x << " ";
    }
    cout << endl;

    // 3. map初始化(键值对)
    map<string, string> dict = {
        {"insert", "插入"},
        {"delete", "删除"},
        {"update", "更新"}
    };
    cout << "map初始化:" << endl;
    for (auto& kv : dict) {
        cout << kv.first << " : " << kv.second << endl;
    }

    // 4. 容器赋值
    v = {100, 200, 300};
    cout << "vector赋值后:";
    for (auto x : v) {
        cout << x << " ";
    }
    cout << endl;

    return 0;
}

2.3 自定义类支持 {} 初始化

如果想让自己写的类支持{}初始化,只需添加std::initializer_list作为参数的构造函数即可。

这里我们自定义一个MyVector来演试一下

cpp 复制代码
#include <iostream>
#include <initializer_list>
using namespace std;

// 自定义vector,支持{}初始化
template<class T>
class MyVector {
public:
    // 构造函数:支持initializer_list
    MyVector(initializer_list<T> list) {
        _size = list.size();
        _data = new T[_size];
        int index = 0;
        for (auto& x : list) {
            _data[index++] = x;
        }
    }

    // 析构函数
    ~MyVector() {
        delete[] _data;
    }

    // 打印数据
    void Print() const {
        for (int i = 0; i < _size; ++i) {
            cout << _data[i] << " ";
        }
        cout << endl;
    }

private:
    T* _data;
    int _size;
};

int main() {
    // 自定义类使用{}初始化
    MyVector<int> mv{1, 2, 3, 4, 5};
    cout << "自定义vector初始化:";
    mv.Print();
    return 0;
}

这样即使是自定义的类,也可以使用{}了

3. 变量类型推导:auto/decltype/nullptr 解放手写类型

C++98 中,变量必须显式声明类型,遇到迭代器、函数指针等超长类型名时,代码极其繁琐。C++11 推出类型推导特性,让编译器自动推算变量类型,大幅简化代码。

3.1 auto:自动类型推导(最常用)

C++11 废弃了 auto 原本的 "自动存储类型" 含义,将其改为自动类型推导,编译器会根据变量的初始化值,自动推算变量类型。

核心规则 :auto 定义的变量必须初始化,否则编译器无法推导类型。下面我们举一个例子来直观地看一下

cpp 复制代码
#include <iostream>
#include <map>
#include <cstring>
using namespace std;

int main() {
    int a = 10;
    auto pa = &a;               
    auto pf = strcpy;            
    cout << "pa的类型:" << typeid(pa).name() << endl;
    cout << "pf的类型:" << typeid(pf).name() << endl;

    map<string, string> dict = {{"name", "张三"}, {"age", "20"}};
    // map<string, string>::iterator it = dict.begin();
    // C++11写法:auto一键推导
    auto it = dict.begin();
    cout << "迭代器遍历:" << it->first << " : " << it->second << endl;

    auto sum = a + 3.14;          // 推导为 double
    cout << "sum的类型:" << typeid(sum).name() << ",值 = " << sum << endl;

    return 0;
}

3.2 decltype:表达式类型推导

auto 用于推导变量 的类型,而decltype 用于推导表达式的类型,不会实际执行表达式,常用于模板编程、类型获取场景。

cpp 复制代码
#include <iostream>
using namespace std;

template<class T1, class T2>
void GetType(T1 t1, T2 t2) {
    // 推导t1*t2的类型
    decltype(t1 * t2) ret;
    cout << "t1*t2的类型:" << typeid(ret).name() << endl;
}

int main() {
    const int x = 1;
    double y = 2.2;

    decltype(x * y) ret;         // 推导为 double
    decltype(&x) p = &x;          // 推导为 const int*
    cout << "ret的类型:" << typeid(ret).name() << endl;
    cout << "p的类型:" << typeid(p).name() << endl;

    GetType(10, 3.14);            // int * double → double
    GetType('a', 100);            // char * int → int

    return 0;
}

3.3 nullptr:安全的空指针

C++98 中,NULL 本质是宏定义的0,既是整数又是空指针,极易引发函数重载歧义。C++11 推出nullptr,专门表示空指针,类型安全,无歧义。

4. 范围 for 循环:极简容器遍历语法

C++98 中遍历容器需要手写迭代器或下标,代码繁琐。C++11 推出范围 for 循环,无需关心迭代器、下标,直接遍历容器内所有元素,语法极简。

核心语法for (auto 变量 : 容器) { 循环体 }

这了我们依旧来句一个例子

cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
using namespace std;

int main() {
    // 1. vector遍历
    vector<int> v = {1, 2, 3, 4, 5};
    cout << "vector遍历:";
    for (auto x : v) {
        cout << x << " ";
    }
    cout << endl;

    // 2. list遍历
    list<string> lt{"hello", "c++", "11"};
    cout << "list遍历:";
    for (auto& s : lt) {
        cout << s << " ";
    }
    cout << endl;

    // 3. 数组遍历
    int arr[]{10, 20, 30};
    cout << "数组遍历:";
    for (auto x : arr) {
        cout << x << " ";
    }
    cout << endl;

    return 0;
}

5. final 与 override:继承多态的安全防护

在 C++ 继承与多态中,经常出现意外继承、虚函数重写写错签名 等问题,导致程序逻辑错误。C++11 推出finaloverride关键字,专门解决这些问题,提升代码安全性。

5.1 final:禁止继承 / 重写

final 有两个作用:

  1. 修饰:该类无法被继承
  2. 修饰虚函数:该虚函数无法被子类重写
cpp 复制代码
#include <iostream>
using namespace std;

// 1. final修饰类:禁止被继承
class Base final {
public:
    virtual void Show() {
        cout << "Base::Show" << endl;
    }
};

// 报错:Base被final修饰,无法被继承
// class Derive : public Base {};

// 2. final修饰虚函数:禁止重写
class A {
public:
    virtual void Func() final {
        cout << "A::Func(禁止重写)" << endl;
    }
};

class B : public A {
public:
    // 报错:Func被final修饰,无法重写
    // void Func() override {}
};

int main() {
    B b;
    b.Func();
    return 0;
}

6. 智能指针:自动化内存管理,杜绝内存泄漏

C++ 最经典的问题就是内存泄漏 ------ 手动 new/delete 容易忘记释放、重复释放、野指针等问题。C++11 推出智能指针,利用 RAII(资源获取即初始化)机制,自动管理内存,无需手动 delete。

C++11 核心智能指针:

  1. unique_ptr:独占式智能指针,同一时间只有一个指针管理对象
  2. shared_ptr:共享式智能指针,引用计数管理,计数为 0 自动释放
  3. weak_ptr:辅助 shared_ptr,解决循环引用问题
cpp 复制代码
#include <iostream>
#include <memory>
using namespace std;

int main() {
    // 1. 定义unique_ptr
    unique_ptr<int> up1 = make_unique<int>(10);
    cout << "up1指向的值:" << *up1 << endl;

    // 2. 独占式:不能拷贝,只能移动
    // unique_ptr<int> up2 = up1;  // 报错:不能拷贝
    unique_ptr<int> up2 = move(up1);  // 移动所有权
    if (!up1) {
        cout << "up1已空,所有权转移给up2" << endl;
    }
    cout << "up2指向的值:" << *up2 << endl;

    // 3. 管理自定义对象
    unique_ptr<string> up3 = make_unique<string>("hello c++11");
    cout << "字符串:" << *up3 << endl;

    // 无需手动delete,出作用域自动释放
    return 0;
}

7. C++11 新容器:更高效、更实用的 STL 组件

C++11 在原有 STL 容器基础上,新增array、forward_list、unordered_map、unordered_set 等容器,其中unordered 系列(哈希容器) 是工程开发中最常用的高效容器。

array:静态数组(安全版普通数组)

array 是固定大小的静态数组,相比普通数组,支持边界检查、迭代器、STL 接口,更安全。

cpp 复制代码
#include <iostream>
#include <array>
using namespace std;

int main() {
    // 定义array:类型 + 大小
    array<int, 5> arr = {1, 2, 3, 4, 5};
    cout << "array遍历:";
    for (auto x : arr) {
        cout << x << " ";
    }
    cout << endl;

    // 常用接口
    cout << "数组大小:" << arr.size() << endl;
    cout << "第一个元素:" << arr.front() << endl;
    cout << "最后一个元素:" << arr.back() << endl;
    cout << "数组是否为空:" << boolalpha << arr.empty() << endl;

    return 0;
}

7.2 forward_list:单向链表

forward_list 是单向链表,相比 list(双向链表),占用内存更少,仅支持正向遍历,适合内存敏感场景。

cpp 复制代码
#include <iostream>
#include <forward_list>
using namespace std;

int main() {
    forward_list<int> fl{1, 2, 3, 4};
    cout << "forward_list遍历:";
    for (auto x : fl) {
        cout << x << " ";
    }
    cout << endl;

    // 头部插入
    fl.push_front(0);
    cout << "头部插入后:";
    for (auto x : fl) {
        cout << x << " ";
    }
    cout << endl;

    return 0;
}

8. 右值引用与移动语义:C++11 最核心的性能革命

右值引用是 C++11最核心、最难、最重要 的特性,解决了临时对象拷贝开销大 的问题,通过移动语义实现 "资源转移",而非 "资源拷贝",大幅提升程序性能。

8.1 左值与右值

  1. 左值:可以取地址、可以放在等号左边的表达式(变量、指针解引用)
  2. 右值:临时值,不能取地址、不能放在等号左边(字面量、表达式返回值、函数临时返回值)

引用写法

  • 左值引用:int&
  • 右值引用:int&&

这里我们用一段例子来看一下

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    // 左值:能取地址
    int& ra = a;
    cout << "左值引用:ra = " << ra << endl;

    // 右值:临时值,不能取地址
    int&& rr = 10;
    // 右值引用可以修改
    rr = 20;
    cout << "右值引用:rr = " << rr << endl;

    return 0;
}

9. Lambda 表达式:就地匿名函数,简化回调逻辑

C++98 中,编写排序、查找等回调函数需要定义仿函数类,代码繁琐。C++11 推出Lambda 表达式,是就地定义的匿名函数,无需定义类,语法简洁,适合临时回调场景。

9.1 Lambda 基础语法

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

  • 捕获列表:获取外部变量
  • 参数列表:和普通函数一致
  • mutable:取消常量属性
  • 返回值:可省略,编译器自动推导

9.2 Lambda 实战:排序自定义类型

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 商品结构体
struct Goods {
    string _name;
    double _price;
    int _score;
};

int main() {
    vector<Goods> v = {
        {"苹果", 2.1, 5},
        {"香蕉", 3.0, 4},
        {"橙子", 1.5, 3},
        {"葡萄", 4.0, 5}
    };

    // 1. 按价格升序
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._price < g2._price;
    });
    cout << "按价格升序:" << endl;
    for (auto& g : v) {
        cout << g._name << " : " << g._price << endl;
    }

    // 2. 按评分降序
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._score > g2._score;
    });
    cout << "按评分降序:" << endl;
    for (auto& g : v) {
        cout << g._name << " : " << g._score << endl;
    }

    return 0;
}

10. 包装器 function 与 bind:统一可调用对象

C++ 中有多种可调用对象:普通函数、函数指针、仿函数、Lambda 表达式,类型不统一,模板中会多次实例化。C++11 推出functionbind,统一包装所有可调用对象,适配各种场景。

10.1 function:包装可调用对象

function 是可调用对象包装器,能包装普通函数、仿函数、Lambda、成员函数,统一类型。

cpp 复制代码
#include <iostream>
#include <functional>
using namespace std;

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

// 仿函数
struct Sub {
    int operator()(int a, int b) {
        return a - b;
    }
};

int main() {
    // 1. 包装普通函数
    function<int(int, int)> f1 = Add;
    cout << "1+2=" << f1(1, 2) << endl;

    // 2. 包装仿函数
    function<int(int, int)> f2 = Sub();
    cout << "3-1=" << f2(3, 1) << endl;

    // 3. 包装Lambda
    function<int(int, int)> f3 = [](int a, int b) {
        return a * b;
    };
    cout << "2*3=" << f3(2, 3) << endl;

    return 0;
}

10.2 bind:绑定参数 / 调整顺序

bind 用于绑定函数参数、调整参数顺序,生成新的可调用对象。

cpp 复制代码
#include <iostream>
#include <functional>
using namespace std;

int Func(int a, int b, int c) {
    return a + b + c;
}

int main() {
    // 1. 绑定固定参数
    auto f1 = bind(Func, 10, 20, placeholders::_1);
    cout << "绑定后:" << f1(30) << endl;    // 10+20+30=60

    // 2. 调整参数顺序
    auto f2 = bind(Func, placeholders::_3, placeholders::_1, placeholders::_2);
    cout << "调整顺序:" << f2(1, 2, 3) << endl;    // 3+1+2=6

    return 0;
}

11. C++11 线程库:跨平台多线程编程

C++98 无原生线程库,多线程依赖平台 API(Windows CreateThread、Linux pthread),代码不可移植。C++11 推出原生线程库,跨平台、简洁易用,支持线程、互斥锁、原子操作、条件变量。

11.1 基础线程创建

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

// 线程函数
void ThreadFunc(int x) {
    cout << "线程运行,参数:" << x << endl;
}

int main() {
    // 1. 线程函数+参数
    thread t1(ThreadFunc, 10);

    // 2. Lambda线程
    thread t2([]{
        cout << "Lambda线程运行" << endl;
    });

    // 等待线程结束
    t1.join();
    t2.join();

    cout << "主线程结束" << endl;
    return 0;
}

12.2 原子操作 atomic:无锁线程安全

多线程修改共享变量会引发数据竞争,C++11 推出atomic原子类型,无锁、线程安全,效率远高于互斥锁。

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

// 原子整数,线程安全
atomic<int> sum{0};

// 线程函数:累加
void AddSum(int n) {
    for (int i = 0; i < n; ++i) {
        sum++;
    }
}

int main() {
    // 两个线程累加100万次
    thread t1(AddSum, 1000000);
    thread t2(AddSum, 1000000);

    t1.join();
    t2.join();

    // 结果正确:2000000
    cout << "sum = " << sum << endl;
    return 0;
}

12.3 lock_guard:RAII 互斥锁

lock_guard 利用 RAII 机制,构造上锁、析构解锁,避免忘记 unlock 导致死锁。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

mutex mtx;
int count{0};

// 线程函数:加锁累加
void AddCount() {
    for (int i = 0; i < 1000; ++i) {
        // 自动上锁
        lock_guard<mutex> lock(mtx);
        count++;
        // 自动解锁
    }
}

int main() {
    thread t1(AddCount);
    thread t2(AddCount);

    t1.join();
    t2.join();

    cout << "count = " << count << endl;
    return 0;
}
相关推荐
郝学胜-神的一滴2 小时前
Linux 高并发基石:epoll 核心原理 + LT/ET 触发模式深度剖析
linux·运维·服务器·开发语言·c++·网络协议
Hello!!!!!!2 小时前
C++基础(六)——数组与字符串
c++·算法
天若有情6732 小时前
反向封神!C++ 全局单例不避反用,实现无锁多线程函数独占访问
java·javascript·c++
智者知已应修善业2 小时前
【51单片机调用__TIME__无法实时时间】2023-7-10
c++·经验分享·笔记·算法·51单片机
凤凰院凶涛QAQ3 小时前
《C++转JAVA快速入手系列》:基本通用语法篇
java·开发语言·c++
Shadow(⊙o⊙)3 小时前
C++常见错误解析2.0
开发语言·数据结构·c++·后端·学习·算法
欧米欧3 小时前
STRING的底层实现
前端·c++·算法
南境十里·墨染春水3 小时前
C++流类库 字符串流
开发语言·c++
code_whiter4 小时前
C++9(vector)
开发语言·c++