目录

一、语法增强

(一)auto

(二)范围 for

(三)Lambda 表达式

(四)初始化列表

(五)nullptr

(六)强类型枚举

(七)类型推导与显式转换

二、智能指针

(一)RAII 思想

(二)std::auto_ptr(已废弃)

(三)std::unique_ptr

(四)std::shared_ptr

(五)std::weak_ptr

(六)循环引用与解决

(七)定制删除器

三、右值引用与移动语义

(一)左值与右值

(二)移动构造函数与移动赋值运算符

(三)完美转发

(四)push 和 emplace 系列的区别

四、函数对象与绑定器

(一)std::function

(二)std::bind

五、多线程支持

(一)std::thread

(二)线程同步

六、异常处理

(一)try/catch

(二)std::exception

(三)noexcept

七、类型转换

(一)static_cast

(二)dynamic_cast

(三)const_cast

(四)reinterpret_cast

八、新增库特性

(一)新容器

(二)新算法

(三)新工具


一、语法增强

(一)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 引入了 decltypeauto,用于类型推导,同时提供了 static_castdynamic_castconst_castreinterpret_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_ptrstd::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_ptrlock() 方法可以获取一个 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_guardstd::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

功能:用于添加或移除变量的 constvolatile 修饰符。

语法

const int x = 10;
int& y = const_cast<int&>(x);

适用场景

添加或移除变量的 constvolatile 修饰符。

用于与非 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_mapstd::unordered_set

需要单向链表时,使用 std::forward_list

需要存储多个不同类型的数据时,使用 std::tuple

需要表示可选值时,使用 std::optional

需要表示多个可能的类型时,使用 std::variant

(二)新算法

功能:新增了多个标准库算法,支持更高效的数据处理。

新增算法

std::move:移动元素。

std::copy_if:条件拷贝。

std::find_if:条件查找。

std::all_ofstd::any_ofstd::none_of:条件检查。

std::transform_if:条件转换。

适用场景

需要移动元素时,使用 std::move

需要条件拷贝时,使用 std::copy_if

需要条件查找时,使用 std::find_if

需要条件检查时,使用 std::all_ofstd::any_ofstd::none_of

需要条件转换时,使用 std::transform_if

(三)新工具

功能:新增了多个标准库工具,支持更高效的数据操作。

新增工具

std::function:封装可调用对象。

std::bind:绑定函数或函数对象的参数。

std::thread:创建和管理线程。

std::mutexstd::condition_variable:线程同步。

std::unique_ptrstd::shared_ptrstd::weak_ptr:智能指针。

std::tuple:支持多个不同类型的数据。

std::optional:表示可选值。

std::variant:表示多个可能的类型之一。

适用场景

封装可调用对象时,使用 std::function

绑定函数或函数对象的参数时,使用 std::bind

创建和管理线程时,使用 std::thread

线程同步时,使用 std::mutexstd::condition_variable

管理动态分配的内存时,使用 std::unique_ptrstd::shared_ptrstd::weak_ptr

存储多个不同类型的数据时,使用 std::tuple

表示可选值时,使用 std::optional

表示多个可能的类型时,使用 std::variant

Logo

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

更多推荐