C++11 新特性速查手册
目录
一、语法增强
(一)auto
功能:自动类型推导,简化变量声明,减少冗余代码。
适用场景:
迭代器声明:
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
函数返回值:
auto result = someFunction();
复杂类型变量:
auto complexObject = std::make_shared<std::vector<std::string>>();
注意事项:
使用 auto 时,编译器会根据初始化表达式推导类型,但不会改变变量的实际类型。
为了代码可读性,应避免过度使用 auto,特别是在类型明确且较短时。
(二)范围 for
功能:简化容器遍历,自动迭代并判定结束,提高代码可读性。
语法:
for (auto& element : container) {
// 对 element 进行操作
}
适用场景:
遍历数组、容器等:
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto& e : vec) {
std::cout << e << " ";
}
遍历字符串:
std::string str = "Hello";
for (auto& c : str) {
std::cout << c << " ";
}
注意事项:
使用范围 for 时,element 是容器中元素的引用,可以直接修改元素。
如果不需要修改元素,可以使用 const auto& 以避免不必要的拷贝。
(三)Lambda 表达式
功能:匿名函数对象,简化函数定义,提高代码复用性。
语法:
[capture](parameters) { body }
capture:捕获列表,指定在 Lambda 表达式中使用的变量。
[=]:捕获所有外部变量的值。
[&]:捕获所有外部变量的引用。
[x]:捕获特定变量 x。
[=, &y]:捕获所有变量的值,同时捕获 y 的引用。
parameters:形参列表,定义 Lambda 表达式的参数。
body:函数体,定义 Lambda 表达式的逻辑。
示例:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });
注意事项:
Lambda 表达式本质上是一个匿名的函数对象,编译器会为其生成一个唯一的类,并在类中实现 operator()。
捕获列表中的变量必须在 Lambda 表达式的上下文中可见。
Lambda 表达式可以有返回值,也可以没有返回值。
(四)初始化列表
功能:使用大括号 {} 初始化容器和数组,提高代码可读性和安全性。
语法:
std::vector<int> vec = {1, 2, 3, 4, 5};
适用场景:
初始化容器:
std::map<int, std::string> map = {{1, "one"}, {2, "two"}, {3, "three"}};
初始化数组:
int arr[5] = {1, 2, 3, 4, 5};
统一初始化:
class MyClass {
public:
MyClass(int x, int y) : x(x), y(y) {}
int x, y;
};
MyClass obj{10, 20}; // 使用初始化列表
注意事项:
初始化列表可以用于任何支持列表初始化的类型。
如果容器或数组的大小是固定的,初始化列表可以提供更清晰的语法。
(五)nullptr
功能:C++中#define NULL 0 定义存在缺陷(空指针会访问0),nullptr明确表示空指针,替代 NULL,提高代码的类型安全性和可读性。
语法:
int* ptr = nullptr;
if (ptr == nullptr) {
std::cout << "Pointer is null";
}
适用场景:
检查空指针:
std::string* str = nullptr;
if (str == nullptr) {
std::cout << "String pointer is null";
}
初始化指针:
int* ptr = nullptr;
注意事项:
nullptr 是一个关键字,不能被赋值为其他类型的值。
使用 nullptr 可以避免将 NULL(通常是一个宏,值为 (void*)0)与指针类型混淆。
(六)强类型枚举
功能:防止枚举值之间的隐式转换,提高代码的安全性和可读性。
语法:
enum class Color { Red, Green, Blue };
适用场景:
定义枚举类型:
enum class Color { Red, Green, Blue };
Color c = Color::Red;
使用枚举值:
if (c == Color::Red) {
std::cout << "Color is Red";
}
注意事项:
强类型枚举值必须通过枚举类型访问,不能直接赋值。
使用强类型枚举可以避免枚举值之间的意外转换。
(七)类型推导与显式转换
功能:C++11 引入了 decltype 和 auto,用于类型推导,同时提供了 static_cast、dynamic_cast、const_cast 和 reinterpret_cast 等显式类型转换操作符。
语法:
auto x = 10; // x 是 int 类型
decltype(x) y = x; // y 也是 int 类型
适用场景:
类型推导:
auto result = someFunction();
decltype(result) value = result;
显式转换:
int x = 10;
double y = static_cast<double>(x);
注意事项:
decltype 可以推导出变量的类型,但不会改变变量的实际类型。
使用显式转换时,应根据需要选择合适的转换操作符。
二、智能指针
(一)RAII 思想
功能:利用对象生命周期管理资源,构造函数获取资源,析构函数释放资源,避免手动管理资源导致的错误。
示例:
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
};
适用场景:
管理文件句柄、网络连接、动态分配的内存等资源。确保资源在对象生命周期结束时自动释放。
注意事项:
RAII 是 C++ 中管理资源的核心思想,智能指针是 RAII 的具体实现。
(二)std::auto_ptr(已废弃)
功能:独占式管理动态分配的内存,但存在设计缺陷。
问题:
不支持数组。
拷贝语义可能导致野指针。
不支持自定义删除器。
示例:
std::auto_ptr<int> ptr(new int(10));
注意事项:
std::auto_ptr 在 C++11 中被废弃,不再推荐使用。
使用 std::unique_ptr 或 std::shared_ptr 替代。
(三)std::unique_ptr
功能:独占式管理动态分配的内存,支持移动语义,不支持拷贝语义。
语法:
std::unique_ptr<int> ptr(new int(10));
适用场景:
独占式管理动态分配的内存。
需要移动语义,但不需要拷贝语义。
示例:
std::unique_ptr<int> ptr(new int(10));
std::unique_ptr<int> ptr2 = std::move(ptr); // 移动语义
注意事项:
std::unique_ptr 不支持拷贝构造和拷贝赋值,但支持移动构造和移动赋值。
可以通过 std::unique_ptr<T[]> 管理数组。
(四)std::shared_ptr
功能:共享式管理动态分配的内存,通过引用计数管理对象生命周期。
语法:
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1; // 引用计数增加
适用场景:
共享式管理动态分配的内存。
多个指针共享同一个对象的所有权。
示例:
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1; // 引用计数增加
注意事项:
std::shared_ptr 通过引用计数管理对象生命周期,当引用计数为零时,对象会被自动释放。
支持自定义删除器,可以通过模板参数指定。
(五)std::weak_ptr
功能:解决 std::shared_ptr 的循环引用问题,提供弱引用。
语法:
std::shared_ptr<int> ptr1(new int(10));
std::weak_ptr<int> weakPtr = ptr1; // 弱引用
适用场景:
解决循环引用问题。
提供对 std::shared_ptr 管理对象的弱引用。
示例:
std::shared_ptr<int> ptr1(new int(10));
std::weak_ptr<int> weakPtr = ptr1; // 弱引用
std::shared_ptr<int> ptr2 = weakPtr.lock(); // 获取强引用
注意事项:
std::weak_ptr 不增加引用计数,不会阻止对象的析构。
使用 std::weak_ptr 的 lock() 方法可以获取一个 std::shared_ptr,如果对象已经被析构,则返回空指针。
(六)循环引用与解决
问题:两个或多个对象相互引用,导致无法自动销毁。
解决:使用 std::weak_ptr 打破循环引用。
示例:
class B;
class A {
public:
std::shared_ptr<B> bPtr;
~A() { std::cout << "A destroyed\n"; }
};
class B {
public:
std::weak_ptr<A> aPtr; // 使用 weak_ptr 打破循环引用
~B() { std::cout << "B destroyed\n"; }
};
int main() {
std::shared_ptr<A> aptr = std::make_shared<A>();
std::shared_ptr<B> bptr = std::make_shared<B>();
aptr->bPtr = bptr;
bptr->aPtr = aptr;
return 0;
}
注意事项:
循环引用是 std::shared_ptr 的常见问题,使用 std::weak_ptr 可以有效解决。
(七)定制删除器
功能:为智能指针指定自定义的资源释放逻辑。
语法:
std::unique_ptr<int, std::function<void(int*)>> ptr(new int(10), [](int* p) { delete p; });
适用场景:
管理非标准资源(如文件句柄、网络连接)。
需要自定义资源释放逻辑。
示例:
std::unique_ptr<int, std::function<void(int*)>> ptr(new int(10), [](int* p) { delete p; });
注意事项:
定制删除器可以是一个函数指针、Lambda 表达式或函数对象。
删除器的签名必须与智能指针的类型匹配。
三、右值引用与移动语义
(一)左值与右值
功能:区分持久对象(左值)和临时对象(右值),支持移动语义。
左值:可以多次使用的值,如变量名。
右值:即将被销毁的值,如临时对象。
示例:
int x = 10; // x 是左值
int&& y = 10; // y 是右值引用
适用场景:
区分对象的生命周期,支持移动语义。
注意事项:
左值引用(Type&)必须绑定到左值,右值引用(Type&&)必须绑定到右值。
右值引用可以用于移动语义,提高资源利用效率。
(二)移动构造函数与移动赋值运算符
功能:通过右值引用实现资源的高效转移,避免不必要的拷贝。
语法:
class Vector {
public:
// 移动构造函数
Vector(Vector&& other) noexcept { /* 资源转移逻辑 */ }
// 移动赋值运算符
Vector& operator=(Vector&& other) noexcept { /* 资源转移逻辑 */ }
};
适用场景:
管理动态分配的内存、文件句柄、网络连接等资源。
提高性能,避免不必要的拷贝。
示例:
class Vector {
private:
int* data; // 动态分配的数组
size_t size; // 数组大小
public:
// 默认构造函数
Vector() : data(nullptr), size(0) {}
// 移动构造函数
Vector(Vector&& other) noexcept {
data = other.data; // 直接转移资源
size = other.size;
other.data = nullptr; // 防止析构时释放
other.size = 0;
}
// 移动赋值运算符
Vector& operator=(Vector&& other) noexcept {
if (this != &other) {
delete[] data; // 释放当前对象的资源
data = other.data; // 转移资源
size = other.size;
other.data = nullptr; // 防止析构时释放
other.size = 0;
}
return *this;
}
// 析构函数
~Vector() {
delete[] data;
}
};
注意事项:
移动构造函数和移动赋值运算符必须声明为 noexcept,以避免异常安全问题。
使用 std::move 将左值转换为右值。
(三)完美转发
功能:在模板函数中保持参数的左值/右值属性,支持完美转发。
语法:
template <typename T>
void forwardFunction(T&& arg) {
FunctionName(std::forward<T>(arg));
}
适用场景:
在模板函数中转发参数,保持参数的左值/右值属性。
示例:
template <typename T>
void forwardFunction(T&& arg) {
FunctionName(std::forward<T>(arg));
}
注意事项:
使用 std::forward 时,必须指定模板参数类型。
完美转发可以用于模板函数,支持左值和右值的转发。
(四)push 和 emplace 系列的区别
功能:push 系列函数接受一个已经构造好的对象,进行拷贝或移动;emplace 系列函数直接在容器内部构造对象,避免额外的拷贝或移动。
语法:
std::vector<int> vec;
vec.push_back(10); // 拷贝或移动
vec.emplace_back(10); // 直接构造
适用场景:
在容器中添加元素时,emplace 系列函数可以提高性能,避免不必要的拷贝或移动。
示例:
std::vector<std::string> vec;
vec.push_back(std::string("Hello")); // 拷贝或移动
vec.emplace_back("Hello"); // 直接构造
注意事项:
emplace 系列函数直接在容器内部构造对象,需要提供构造参数。
使用 emplace 系列函数可以提高性能,但需要确保构造参数的正确性。
四、函数对象与绑定器
(一)std::function
功能:封装可调用对象,支持函数指针、Lambda 表达式、函数对象等。
语法:
std::function<int(int, int)> func = [](int a, int b) { return a + b; };
适用场景:
封装可调用对象,支持多种类型的调用。
用于回调函数、事件处理等场景。
示例:
std::function<int(int, int)> func = [](int a, int b) { return a + b; };
std::cout << func(3, 4); // 输出 7
注意事项:
std::function 可以封装任何可调用对象,包括 Lambda 表达式、函数指针、函数对象等。
使用 std::function 时,需要指定函数的签名。
(二)std::bind
功能:绑定函数或函数对象的参数,创建新的可调用对象。
语法:
auto add = [](int a, int b) { return a + b; };
auto addFive = std::bind(add, std::placeholders::_1, 5);
适用场景:
绑定函数参数,创建新的可调用对象。
用于回调函数、事件处理等场景。
示例:
auto add = [](int a, int b) { return a + b; };
auto addFive = std::bind(add, std::placeholders::_1, 5);
std::cout << addFive(3); // 输出 8
注意事项:
使用 std::bind 时,需要指定绑定的函数或函数对象。
使用 std::placeholders 来指定占位符,指定一个具体值固定在函数的参数上。
五、多线程支持
(一)std::thread
功能:创建和管理线程。
语法:
std::thread t(threadFunction);
适用场景:
创建并行任务,提高程序性能。
管理多线程程序。
示例:
void threadFunction() {
std::cout << "Thread function\n";
}
int main() {
std::thread t(threadFunction);
t.join();
return 0;
}
注意事项:
使用 std::thread 时,需要包含 <thread> 头文件。
使用 join 方法等待线程结束,或使用 detach 方法分离线程。
(二)线程同步
功能:使用互斥量(mutex)和条件变量(condition_variable)实现线程同步。
语法:
std::mutex mtx;
std::condition_variable cv;
适用场景:
管理线程之间的同步,避免数据竞争和死锁。
实现生产者-消费者模型等多线程同步场景。
示例:
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void producer() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
}
void consumer() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; });
std::cout << "Consumer\n";
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
注意事项:
使用 std::mutex 时,需要包含 <mutex> 头文件。
使用 std::condition_variable 时,需要包含 <condition_variable> 头文件。
使用 std::lock_guard 或 std::unique_lock 管理互斥量,避免忘记解锁。
六、异常处理
(一)try/catch
功能:捕获和处理异常。
语法:
try {
// 可能抛出异常的代码
} catch (const std::exception& e) {
// 处理异常
}
适用场景:
捕获和处理运行时错误。
确保程序的健壮性和可靠性。
示例:
try {
throw std::runtime_error("Error occurred");
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
注意事项:
使用 try 块捕获异常,使用 catch 块处理异常。
捕获异常时,可以指定异常类型或使用通用异常类型(如 std::exception)。
(二)std::exception
功能:标准异常基类,提供 what() 方法。
语法:
throw std::runtime_error("Error occurred");
适用场景:
抛出和捕获标准异常。
提供异常的描述信息。
示例:
try {
throw std::runtime_error("Error occurred");
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
注意事项:
使用 std::exception 时,需要包含 <exception> 头文件。
what() 方法返回异常的描述信息。
(三)noexcept
功能:标记函数不会抛出异常。
语法:
void function() noexcept {
// 函数体
}
适用场景:
标记函数不会抛出异常,提高程序的异常安全性。
用于性能敏感的代码。
示例:
void function() noexcept {
// 函数体
}
注意事项:
使用 noexcept 时,函数内部不能抛出异常。
如果函数内部抛出异常,程序将调用 std::terminate。
七、类型转换
(一)static_cast
功能:用于有明确类型关系的转换,如继承关系或基本类型转换。
语法:
int x = 10;
double y = static_cast<double>(x);
适用场景:
基本类型转换。
继承体系中的向上转换。
示例:
int x = 10;
double y = static_cast<double>(x);
注意事项:
static_cast 是编译时检查的类型转换,不涉及运行时类型信息(RTTI)。
使用 static_cast 时,需要确保转换的安全性。
(二)dynamic_cast
功能:用于类继承体系中的安全向下转换。
语法:
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base);
适用场景:
类继承体系中的向下转换。
需要运行时类型检查。
示例:
class Base {};
class Derived : public Base {};
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base);
注意事项:
dynamic_cast 依赖于运行时类型信息(RTTI),需要包含 <typeinfo> 头文件。
如果转换失败,dynamic_cast 将返回空指针(对于指针)或抛出异常(对于引用)。
(三)const_cast
功能:用于添加或移除变量的 const 或 volatile 修饰符。
语法:
const int x = 10;
int& y = const_cast<int&>(x);
适用场景:
添加或移除变量的 const 或 volatile 修饰符。
用于与非 const 接口交互。
示例:
const int x = 10;
int& y = const_cast<int&>(x);
注意事项:
const_cast 不会改变数据的实际类型,只是改变数据的访问权限。
使用 const_cast 时,需要确保操作的安全性。
(四)reinterpret_cast
功能:用于执行低级别的类型转换,基于位模式操作。
语法:
int x = 10;
char* ptr = reinterpret_cast<char*>(&x);
适用场景:
低级别的类型转换。
用于与硬件相关的操作。
示例:
int x = 10;
char* ptr = reinterpret_cast<char*>(&x);
注意事项:
reinterpret_cast 是基于位模式的转换,不会改变数据的实际内容。
使用 reinterpret_cast 时,需要非常小心,避免未定义行为。
八、新增库特性
(一)新容器
功能:新增了多个标准库容器,支持更高效的数据存储和操作。
新增容器:
std::array:固定大小的数组,支持 STL 算法。
std::unordered_map:基于哈希表的关联容器。
std::unordered_set:基于哈希表的关联容器。
std::forward_list:单向链表。
std::tuple:支持多个不同类型的数据。
std::optional:表示可选值。
std::variant:表示多个可能的类型之一。
适用场景:
使用固定大小的数组时,使用 std::array。
需要高效的查找操作时,使用 std::unordered_map 或 std::unordered_set。
需要单向链表时,使用 std::forward_list。
需要存储多个不同类型的数据时,使用 std::tuple。
需要表示可选值时,使用 std::optional。
需要表示多个可能的类型时,使用 std::variant。
(二)新算法
功能:新增了多个标准库算法,支持更高效的数据处理。
新增算法:
std::move:移动元素。
std::copy_if:条件拷贝。
std::find_if:条件查找。
std::all_of、std::any_of、std::none_of:条件检查。
std::transform_if:条件转换。
适用场景:
需要移动元素时,使用 std::move。
需要条件拷贝时,使用 std::copy_if。
需要条件查找时,使用 std::find_if。
需要条件检查时,使用 std::all_of、std::any_of、std::none_of。
需要条件转换时,使用 std::transform_if。
(三)新工具
功能:新增了多个标准库工具,支持更高效的数据操作。
新增工具:
std::function:封装可调用对象。
std::bind:绑定函数或函数对象的参数。
std::thread:创建和管理线程。
std::mutex、std::condition_variable:线程同步。
std::unique_ptr、std::shared_ptr、std::weak_ptr:智能指针。
std::tuple:支持多个不同类型的数据。
std::optional:表示可选值。
std::variant:表示多个可能的类型之一。
适用场景:
封装可调用对象时,使用 std::function。
绑定函数或函数对象的参数时,使用 std::bind。
创建和管理线程时,使用 std::thread。
线程同步时,使用 std::mutex 和 std::condition_variable。
管理动态分配的内存时,使用 std::unique_ptr、std::shared_ptr 和 std::weak_ptr。
存储多个不同类型的数据时,使用 std::tuple。
表示可选值时,使用 std::optional。
表示多个可能的类型时,使用 std::variant。
更多推荐


所有评论(0)