跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
|注册
博客列表

目录

  1. 一、列表初始化
  2. 1. { } 初始化
  3. 2. std::initializer_list
  4. 二、关键字:auto、decltype、nullptr
  5. 1. auto
  6. 2. decltype
  7. 3. nullptr
  8. 三、范围 for 循环
  9. 四、认识 STL 中的变化
  10. 五、右值引用和移动语义
  11. 1. 左值引用和右值引用
  12. (1)概念
  13. (2)区别
  14. 2. 移动语义(移动构造和移动赋值)
  15. 3. move 的作用
  16. 4. 应用场景
  17. 5. 万能引用
  18. 6. 完美转发
  19. 六、类的新功能
  20. 1. 默认成员函数
  21. 2. default、delete 关键字
  22. 七、可变参数模板
  23. 1. 概念
  24. 2. STL 中的 emplace 系列
C++

C++11 常见特性

C++11 引入了统一初始化、自动类型推导关键字(auto、decltype、nullptr)、范围 for 循环、STL 新容器及接口、右值引用与移动语义、类的新功能以及可变参数模板。这些特性提升了代码的安全性、可读性和执行效率,是 C++ 现代化开发的核心基础。

星河入梦发布于 2026/3/27更新于 2026/4/163 浏览
C++11 常见特性

一、列表初始化

1. { } 初始化

C++98 中的 { } 只能用于数组和C 风格结构体;而 C++11 引入了统一初始化,几乎可以用在任何地方,包括普通变量、类对象和 STL 容器。

在 C++98 中,花括号 { } 被称为,有许多限制,只能用于(不能有构造函数、不能有 private 成员、不能有虚函数),,如下所示:

聚合初始化
数组,简单的 C 风格结构体
STL 容器无法使用
// 数组 int arr1[5] = { 1, 2, 3, 4, 5 }; // 合法:定义数组并初始化
int arr2[] = { 1, 2, 3 }; // 合法:自动推断大小
int arr3[10] = { 0 }; // 合法:将第一个元素设为 0,其余自动初始化为 0
// int arr[3] {1, 2, 3}; // Error: C++98 不允许省略等号
// C 风格结构体(即:不能有构造函数、不能有 private 成员、不能有虚函数)
struct Point { int x, y; };
Point p = { 1, 2 }; // OK
// 容器
// std::vector<int> vec = {1, 2, 3}; // Error: C++98 没有 initializer_list
std::vector<int> vec;
vec.push_back(1); // 只能这样繁琐地添加
vec.push_back(2);

而在 C++11 中,就将 { } 提升为统一初始化,扩大了用大括号括起的列表 (初始化列表) 的使用范围,使其可用于所有的内置类型和用户自定义的类型。使用初始化列表时,可添加等号 (=),也可不添加。简单来说就是在 C++11 中一切都可以通过 { } 进行初始化。

  • 基本数据类型也可以使用 { } ,并且可以省略 = 。
  • 结构体与类对象:没有构造函数的简单结构体(聚合体)允许直接按成员声明顺序赋值;有构造函数的类创建对象时也会调用构造函数初始化。
  • STL 容器:允许直接通过 { } 初始化,也可以省略等号。

2. std::initializer_list

std::initializer_list 是 C++11 才增加的一个类型,用于表示和访问一组特定类型的常量值的数组,主要用于支持使用花括号 { } 进行的列表初始化。当我们使用 { } 初始化时,编译器会将数据列表转换成一个 initializer_list。

使用场景

std::initializer_list 一般是作为构造函数的参数。在 C++11 中,STL 的容器中增加了许多这样的构造函数,例如 vector 和 map。

对于其他 STL 容器也有一个 initializer_list 参数的构造函数。也正是因为有了这样的构造函数,我们才可以使用 { } 直接初始化各个容器对象,如:

// 初始化 vector
std::vector<int> v = { 1, 2, 3, 4, 5 }; // 调用了 initializer_list 的构造函数

除了构造时有 initializer_list 的使用,operator= 也重载了 initializer_list 参数的使用。

std::vector<int> v;
v = { 1, 2, 3, 4, 5 }; // 调用了 initializer_list 参数的 operator=

二、关键字:auto、decltype、nullptr

1. auto

在 C++98/03 标准中,auto 用于声明一个具有自动存储期的变量,实际用途几乎为零。在 C++11 中则彻底废弃了 auto 在 C++98 中的旧含义,赋予了它新的功能:自动类型推导。即可以自动推导变量类型,在处理复杂类型的时候,大大方便了我们在 C++ 中的使用。

std::vector<std::string> v = { "sort","insert","find"};
// C++98: 类型冗长,可读性差
std::vector<std::string>::iterator it = v.begin();
// C++11: 简洁明了
auto it2 = v.begin(); // 编译器自动推导为 vector<string>::iterator

2. decltype

关键字 decltype 可以将变量的类型声明为表达式指定的类型。比如,我们有一个 int 类型的变量,现在我们想定义一个与 a 相同类型的变量 b,要求不使用 int 关键字,则就可以通过 decltype 来帮助我们完成定义。

它也适用于表达式,比如,我们要定义一个与一个表达式结果相同的变量,则就可以使用 decltype 来解决。

3. nullptr

由于 C++ 中 NULL 被定义成字面量 0,这样就可能会带来一些问题,因为 0 既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11 中新增了 nullptr,用于表示空指针,不会有歧义了。

三、范围 for 循环

C++11 中,引入了基于范围的 for 循环,它适用于对一个有范围的集合进行遍历。

int main() {
    int arr[] = { 1, 2, 3, 4, 5 };
    for (int e : arr) { // 使用原本的类型 int 接收
        std::cout << e << " "; // 输出当前元素
    }
    return 0;
}

这里的意思就是:每次循环,arr 的当前元素会被复制到 e 中(值传递),然后循环会依次访问数组中每一个元素。并且它会自动判断结束。

四、认识 STL 中的变化

在 C++11 中的变化有一些几点:

1、增加了几个新容器

  • std::array:静态数组,通过 T 控制类型,N 控制数组的大小。相比 C 风格数组,std::array 内部会进行严格的检查,安全性能更高。
  • std::forward_list:底层是一个单链表,使用方法和 list 很类似。
  • unordered_map 和 unordered_set:前面已经讲过,使用方法和 set、map 类似。

2、增加了新接口

它们其实就是 const 迭代器和 const 反向迭代器,实际意义不大,一般使用 begin、end、rbegin、rend 就够了。

3、所有容器都增加了 emplace 系列

这里的 emplace 需要我们理解了右值引用和可变模板参数才能理解。

4、容器增加了移动构造和移动赋值

移动构造和移动赋值可以大大节省我们使用容器的效率。

五、右值引用和移动语义

这是我们学习 C++11 中的一个难点。

1. 左值引用和右值引用

传统的 C++ 语法中就有引用的语法,而 C++11 中新增了的右值引用语法特性,而我们之前使用到的引用叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。

(1)概念
  • 左值:就是一个我们可以对它取地址,一般可以对它赋值的可以表示数据的表达式,可以出现在赋值符号的左边。
  • 右值:则是一个我们不可以对它取地址,也不可以修改的可以表示数据的表达式,只能出现在赋值符号的右边。通常是临时的,生命周期很短。

区分左值与右值关键在于这个数据可不可以取地址。

  • 左值引用:就是对左值去取别名。
  • 右值引用:其实就是对右值取别名,写法不同,使用 &&。
(2)区别
  • 左值引用:只能引用左值,不能引用右值。但是 const 左值引用既可引用左值,也可引用右值。
  • 右值引用:只能引用右值,不能引用左值。但是右值引用可以 move 以后的左值。可以取地址和被修改。

2. 移动语义(移动构造和移动赋值)

以下代码是一个简单的 string 的模拟实现:

namespace MyCreate {
class string {
public:
    string(const char*) :_size(strlen(str)) , _capacity(_size) {
        std::cout << "string(char* str)" << std::endl;
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }
    void swap(string& s) {
        ::swap(_str, s._str);
        ::swap(_size, s._size);
        ::swap(_capacity, s._capacity);
    }
    // 拷贝构造
    string(const string& s) :_str(nullptr) {
        std::cout << "string(const string& s) -- 深拷贝" << std::endl;
        string tmp(s._str);
        swap(tmp);
    }
    // 赋值重载
    string& operator=(const string& s) {
        std::cout << "string& operator=(string s) -- 深拷贝" << std::endl;
        string tmp(s);
        swap(tmp);
        return *this;
    }
    ~string() {
        delete[] _str;
        _str = nullptr;
    }
    void reserve(size_t n) {
        if (n > _capacity) {
            char* tmp = new char[n + 1];
            strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _capacity = n;
        }
    }
    const char* c_str() const { return _str; }
private:
    char* _str;
    size_t _size;
    size_t _capacity;
};
}

移动构造

移动构造就是用一个**右值(通常是临时对象)**来构造一个新的对象,即:将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了。移动构造的代码如下所示:

// 移动拷贝
string(string&& s) noexcept :_str(nullptr) ,_size(0) ,_capacity(0) {
    std::cout << "string(string&& s) -- 移动拷贝构造" << std::endl;
    swap(s);
}

移动赋值

移动赋值就是用一个右值来给一个已经存在的对象赋值,也就是窃取别人的资源来赋值自己。

// 移动赋值
string& operator=(string&& s) noexcept {
    std::cout << "string& operator=(string&& s) -- 移动赋值重载" << std::endl;
    swap(s);
    return *this;
}

3. move 的作用

move 函数的作用为:将一个左值转换为右值引用。从而让编译器认为这个对象可以被'移动'(即可以调用移动构造或移动赋值),而不是被'拷贝'。

MyCreate::string copy2 = move(s1); // 构造
MyCreate::string copy3;
copy3 = move(s2); // s2 的资源会被夺取

4. 应用场景

对于左值引用的使用场景就是:做函数参数,或则做函数返回值,这样可以减少拷贝,提高效率。

对右值引用,它的价值可以总结为:进一步减少拷贝,弥补左值引用没有解决的场景。

右值引用的常见使用场景:

  1. 函数返回时,对于深拷贝的类必须传值返回的情况。
  2. 在容器的插入接口中,如果插入的对象是右值,则可以使用移动构造来转移资源给数据结构中的对象。

5. 万能引用

在模板中的 && 代表了万能引用,它既可以引用左值,也可以引用右值。

template<typename T> void PerfectForward(T&& t) { // ... }

6. 完美转发

完美转发则可以在传参的过程中保留对象原生类型属性,如果是左值引用那就是左值引用,如果是右值引用,那就是右值引用。它必须在模板中使用,即必须配合万能引用一起使用。

六、类的新功能

1. 默认成员函数

原来 C++ 类中,有 6 个默认成员函数。而 C++11 则新增了两个:移动构造函数和移动赋值运算符重载。

  1. 如果你没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动构造。
  2. 如果你没有自己实现移动赋值重载函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。
  3. 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

2. default、delete 关键字

default 关键字可以显示指定对应的默认成员函数生成。

delete 关键字则和 default 相对,delete 可以指示编译器不生成对应函数的默认版本,使用方法和 default 一样,只需要在该函数声明加上 =delete 即可。

七、可变参数模板

1. 概念

在 C++11 以前,我们的模板中类模版和函数模版只能含固定数量的模版参数。而在 C++11 中增加了可变参数模板的概念,它可以让我们的参数可以传递很多个。

下面就是一个基本可变参数的函数模板:

// Args 是一个模板参数包,args 是一个函数形参参数包
template <class ...Args> void ShowList(Args... args) { // ... }

如果我们要获取一个参数包的值,这里有两种方法:递归函数方式展开和逗号表达式展开。

递归函数方式展开

void _ShowList() { std::cout << std::endl; }
template <class T, class ...Args> void _ShowList(T value, Args... args) {
    std::cout << value << " ";
    _ShowList(args...);
}
template <class ...Args> void PrintList(Args... args) { _ShowList(args...); }

逗号表达式展开

template <class T> void PrintArg(T t) { std::cout << t << " "; }
template <class ...Args> void ShowList(Args... args) {
    int arr[] = { (PrintArg(args), 0)... };
    std::cout << std::endl;
}

2. STL 中的 emplace 系列

其中 emplace 对应 insert,都是插入的意思,emplace_back 对应 push_back 都是尾插的意思。可以看到,它们都是使用可变参数模板实现的,并且是万能引用。

那么,我们为什么需要 emplace 接口呢?

在 C++11 之前,我们通常使用 push_back 等接口向容器添加对象,但这种方法只能通过传递对象来添加对象。

  1. 如果使用 push_back 来添加数据,需要先构造,再拷贝构造,此时就需要两次资源的拷贝。
  2. 而对于具有移动构造的就会先构造,再移动构造,此时拷贝资源也只需要一次。
  3. 而 emplace 只需要将参数一直向下传递,直接一次构造就可以完成插入了,效率比较高。

而 C++11 中一般都是移动构造,所以一般来说 emplace 系列和常规的插入(insert,push_back 等)的效率其实都差不多。

极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • Node.js 下载安装及环境配置教程
  • 六自由度机器人逆运动学详解及 MATLAB 实现
  • 2026 传媒行业变革:Agent 成新入口,AIGC 引爆内容产能
  • PostgreSQL 插件 pgvector 核心功能与版本演进总结
  • Coze扣子「百套AI工作流」模板合集
  • Python 面试核心知识点汇总:装饰器与数据结构
  • C++26 新特性解析:线程亲和性与性能优化
  • KaiwuDB 3.1.0 在 Ubuntu 22.04 部署实战:TLS 配置与性能测试
  • 企业级 Python 反爬实战:JS 逆向、APP 抓包与验证码破解
  • IDEA 报警:未注解方法重写@NonNullApi 注解方法
  • Python webbrowser 库:跨平台浏览器控制接口
  • Ubuntu 24 安装 Claude Code
  • 基于 OpenClaw 的 AI 自动化创作与公众号发布全流程
  • 微信小程序案例 - 自定义 tabBar
  • OpenWebUI 对外 HTTP 接口配置与使用指南
  • 利用文心一言设计智能体工作流调用的稳定提示词
  • Node.js 在线 Markdown 编辑器:支持表格、公式与代码高亮
  • 2026 传媒行业展望:Agent 定义入口,AIGC 重塑供给
  • Ollama 模型管理与删除及 Open-WebUI 大模型交互配置
  • MySQL 数据库约束详解:非空、主键、外键的作用

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online