21、【C++】C++11常用特性

目录

一、核心语言特性

1.1 自动类型推导(auto)

1.1.1 基本用法与类型推断规则

auto让编译器根据初始化表达式推断变量类型,必须初始化

auto i = 10; // i为int
auto d = 3.14; // d为double
auto s = "hello"; // s为const char*
auto v = std::vector<int>{1,2,3}; // v为std::vector<int>

推断规则

  • 若表达式为引用,忽略引用,推断为引用类型的原始类型。
  • 若表达式为const,顶层const被忽略(底层const保留)。
1.1.2 在复杂类型中的应用(迭代器、函数返回值)

简化冗长类型声明,如迭代器:

// C++98
std::map<std::string, int>::iterator it = map.begin();
// C++11
auto it = map.begin(); // 自动推导为迭代器类型
1.1.3 auto的限制
  • 不能用于函数参数:void func(auto param) {}(C++14支持auto参数)。
  • 不能用于数组类型:auto arr[5] = {1,2,3,4,5};(错误)。
  • 不能用于非静态成员变量。

1.2 decltype关键字

1.2.1 获取表达式类型

decltype(expression)返回表达式的类型,不执行表达式:

int x = 10;
decltype(x) y = 20; // y为int
decltype(x + 3.14) z = 3.14; // z为double
1.2.2 与auto结合使用(decltype(auto))

C++14引入decltype(auto),结合auto的推导和decltype的规则:

template <typename T>
decltype(auto) func(T&& t) {
    return t; // 返回类型根据t的类型推断(保留引用属性)
}
1.2.3 在模板中的应用

用于声明与表达式类型相同的变量:

template <typename Container>
auto get_element(Container& c, size_t idx) -> decltype(c[idx]) {
    return c[idx]; // 返回类型为c[idx]的类型(可能为引用)
}

1.3 范围for循环(range-based for loop)

1.3.1 基本语法与使用条件

语法for (元素类型 元素变量 : 容器/数组) { ... }
使用条件:容器需支持begin()end()方法,返回迭代器。

1.3.2 遍历容器与数组
std::vector<int> v = {1,2,3,4};
for (auto elem : v) { // 遍历容器
    std::cout << elem << " ";
}

int arr[] = {1,2,3,4};
for (auto elem : arr) { // 遍历数组
    std::cout << elem << " ";
}
1.3.3 遍历修改元素(引用捕获)

使用引用捕获修改元素:

std::vector<int> v = {1,2,3,4};
for (auto& elem : v) { // 引用捕获
    elem *= 2; // 修改容器元素
}

1.4 nullptr空指针常量

1.4.1 替代NULL的必要性

C++98中NULL是宏定义为0,可能导致歧义:

void func(int x) {} // 1
void func(char* p) {} // 2

func(NULL); // 调用1(NULL被解析为int 0),而非预期的2
func(nullptr); // C++11:调用2(nullptr类型为nullptr_t,匹配char*)
1.4.2 nullptr与NULL的区别
  • 类型nullptr类型为nullptr_t,可隐式转换为任意指针类型。
  • 安全性nullptr仅能用于指针上下文,避免整数与指针混淆。
1.4.3 类型安全的空指针检查
char* p = nullptr;
if (p == nullptr) { // 类型安全的检查
    // ...
}

1.5 constexpr编译期常量

1.5.1 constexpr变量(编译期初始化)

constexpr变量在编译期初始化,存储在只读内存:

constexpr int MAX_SIZE = 1024; // 编译期常量
std::array<int, MAX_SIZE> arr; // 编译期确定数组大小
1.5.2 constexpr函数(编译期计算)

constexpr函数在编译期计算结果:

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

constexpr int sum = add(1, 2); // 编译期计算sum=3
1.5.3 C++11 vs C++14/17的 constexpr扩展
  • C++11:constexpr函数体只能有一条return语句。
  • C++14:允许循环、条件语句,支持更多语法。
  • C++17:支持lambda、if constexpr等。

1.6 右值引用与移动语义

1.6.1 右值引用语法(&&)

右值引用绑定到右值(临时对象、字面量、std::move转换的左值):

int&& rref = 10; // 右值引用绑定字面量
std::string&& sref = std::string("hello"); // 绑定临时对象
1.6.2 移动构造与移动赋值

通过右值引用实现资源转移,避免深拷贝:

class String {
public:
    // 移动构造函数
    String(String&& other) noexcept {
        _data = other._data; // 转移资源
        other._data = nullptr; // 源对象释放资源
    }

    // 移动赋值运算符
    String& operator=(String&& other) noexcept {
        if (this != &other) {
            delete[] _data;
            _data = other._data;
            other._data = nullptr;
        }
        return *this;
    }

private:
    char* _data;
};
1.6.3 std::move:将左值转换为右值引用

std::move不移动资源,仅转换类型:

std::string s = "hello";
std::string s2 = std::move(s); // 调用移动构造,s变为空

1.7 完美转发(Perfect Forwarding)

1.7.1 问题引入:模板参数类型丢失

模板中传递参数时,右值可能被转为左值:

template <typename T>
void forward_func(T param) {
    target_func(param); // param为左值,即使传入右值
}
1.7.2 std::forward的实现原理

std::forward<T>(param)根据T的类型条件转发:

template <typename T>
T&& forward(typename std::remove_reference<T>::type& t) {
    return static_cast<T&&>(t);
}
1.7.3 在函数模板中的应用
template <typename T>
void wrapper(T&& arg) {
    func(std::forward<T>(arg)); // 完美转发,保留arg的左值/右值属性
}

1.8 lambda表达式

1.8.1 基本语法
[capture-list](params) mutable -> return-type { body }
  • 捕获列表:捕获外部变量([]空捕获,[=]值捕获,[&]引用捕获,[x, &y]混合捕获)。
  • 参数列表:与函数参数相同。
  • mutable:允许修改值捕获的变量。
  • return-type:返回类型,可省略让编译器推导。
1.8.2 捕获方式示例
int x = 10, y = 20;
auto add = [x, &y]() mutable {
    x++; // 修改值捕获变量(需mutable)
    y++; // 修改引用捕获变量
    return x + y;
};
std::cout << add() << std::endl; // 11 + 21 = 32
1.8.4 在算法中的应用
std::vector<int> v = {3,1,2};
std::sort(v.begin(), v.end(), [](int a, int b) {
    return a < b; // 升序排序
});

1.9 委托构造与继承构造

1.9.1 委托构造

构造函数调用同一类的其他构造函数:

class A {
public:
    A(int a) : _a(a) {}
    A() : A(0) {} // 委托A(int)构造函数
private:
    int _a;
};
1.9.2 继承构造

派生类继承基类构造函数,使用using声明:

class Base {
public:
    Base(int a) {}
    Base(int a, double b) {}
};

class Derived : public Base {
public:
    using Base::Base; // 继承Base的所有构造函数
};

二、标准库新增特性

2.1 智能指针

2.1.1 unique_ptr:独占所有权

不可拷贝,支持移动,管理独占资源:

std::unique_ptr<int> up = std::make_unique<int>(10);
auto up2 = std::move(up); // 所有权转移,up为空
2.1.2 shared_ptr:共享所有权

通过引用计数管理资源,计数为0时释放:

auto sp = std::make_shared<int>(10);
auto sp2 = sp; // 引用计数=2
2.1.3 weak_ptr:解决循环引用

不增加引用计数,从shared_ptr创建:

struct Node { std::weak_ptr<Node> next; };
auto n1 = std::make_shared<Node>();
auto n2 = std::make_shared<Node>();
n1->next = n2; // weak_ptr不增加计数,避免循环引用

2.2 新增容器

2.2.1 unordered_map/unordered_set(哈希表)

平均O(1)查找,基于哈希函数:

std::unordered_map<std::string, int> um = {{"a",1}, {"b",2}};
std::cout << um["a"] << std::endl; // 1
2.2.2 std::array:固定大小数组

结合数组性能和容器接口:

std::array<int, 3> arr = {1,2,3};
arr[0] = 0;

2.3 线程库

2.3.1 创建线程(std::thread)
void func() { /* ... */ }
std::thread t(func);
t.join(); // 等待线程结束
2.3.2 互斥锁(std::mutex与lock_guard)

保护共享数据:

std::mutex mtx;
int shared = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx); // RAII加锁
    shared++;
}

2.4 原子操作(std::atomic)

线程安全的基本类型操作:

std::atomic<int> cnt = 0;
cnt++; // 原子自增,无锁操作

2.5 时间库(std::chrono)

处理时间间隔和时间点:

using namespace std::chrono;
auto start = high_resolution_clock::now();
// ... 耗时操作 ...
auto end = high_resolution_clock::now();
duration<double> elapsed = end - start;
std::cout << "耗时:" << elapsed.count() << "秒" << std::endl;

2.6 元组(std::tuple)

存储多个不同类型的值:

std::tuple<int, std::string, double> t(1, "hello", 3.14);
auto [i, s, d] = t; // C++17结构化绑定

三、C++11特性的协同应用

3.1 右值引用+移动语义+智能指针优化资源管理

std::unique_ptr<std::string> create_string() {
    return std::make_unique<std::string>("hello"); // 返回局部对象,自动移动
}

3.2 lambda+std::function实现回调机制

std::function<void(int)> callback = [](int x) {
    std::cout << "回调:" << x << std::endl;
};
callback(10); // 调用lambda

3.3 变参模板+完美转发实现通用函数包装

template <typename F, typename... Args>
auto wrap(F&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...)) {
    return f(std::forward<Args>(args)...); // 完美转发变参
}

四、C++11迁移与最佳实践

4.1 优先使用auto推导类型

简化代码,避免类型拼写错误,如auto it = container.begin()

4.2 用nullptr代替NULL

避免NULL的二义性,增强类型安全。

4.3 避免使用原始指针,优先智能指针

防止内存泄漏,使用unique_ptr管理独占资源,shared_ptr管理共享资源。

4.4 合理使用lambda表达式

简化回调和算法参数,但避免复杂逻辑(可读性差)。

以上内容,全面覆盖了C++11的核心特性,包括语言层面的auto、lambda、右值引用,以及标准库的智能指针、线程库等。合理应用这些特性可显著提高代码的简洁性、性能和安全性。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐