21、【C++】C++11常用特性
21、【C++】C++11常用特性
目录
- 一、核心语言特性
- 二、标准库新增特性
- 三、C++11特性的协同应用
- 四、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、右值引用,以及标准库的智能指针、线程库等。合理应用这些特性可显著提高代码的简洁性、性能和安全性。
更多推荐
所有评论(0)