c++小知识点,noexcept,explicit,vector.emplace_back特点

发布时间:2024年01月05日

1. noexcept

noexcept 是 C++ 中的一个关键字,用于指示一个函数或者表达式是否会抛出异常。它有两个主要的用途:

  1. 在函数声明中表示不抛出异常:

    myFunction() noexcept {
        // 函数体
    }
    

    在这个例子中,noexcept 表明 myFunction 不会抛出异常。如果在函数声明中使用了 noexcept,而在实际的函数调用中发生了异常,程序会调用 std::terminate 来终止程序。

  2. 在运行时检查是否抛出异常:

    try {
        // 一些可能抛出异常的代码
    } catch (...) {
        // 异常处理代码
    }
    

    如果你在 try 块内使用了 noexcept 的函数,而在这个函数中抛出了异常,那么程序会立即调用 std::terminate,而不是执行 catch 块内的代码。这有助于在编译时和运行时都更好地处理异常。

noexcept 对于移动构造函数和移动赋值运算符的使用也很常见,例如:

class MyClass {
public:
    // 移动构造函数,标记为 noexcept
    MyClass(MyClass&& other) noexcept {
        // 实现移动构造逻辑
    }

    // 移动赋值运算符,标记为 noexcept
    MyClass& operator=(MyClass&& other) noexcept {
        // 实现移动赋值逻辑
        return *this;
    }
};

这有助于优化容器和算法的性能,因为容器和算法可以利用 noexcept 的信息来进行更有效的实现。

2. explicit

explicit 是 C++ 中的一个关键字,通常用于声明一个类的单参数构造函数,以防止编译器进行隐式类型转换。使用 explicit 关键字可以明确指定只能使用显式构造调用,而禁止隐式转换。

考虑下面的例子:

class MyClass {
public:
    // 隐式转换构造函数
    MyClass(int x) {
        // 构造逻辑
    }
};

int main() {
    MyClass obj = 42;  // 隐式转换
    return 0;
}

在这个例子中,MyClass 有一个接受整数参数的构造函数,这可能导致隐式类型转换。为了防止这种情况,可以使用 explicit

class MyClass {
public:
    // 显式构造函数
    explicit MyClass(int x) {
        // 构造逻辑
    }
};

int main() {
    MyClass obj = 42;  // 编译错误,因为构造函数是显式的
    MyClass obj2(42);  // 正确,使用显式构造
    return 0;
}

通过在构造函数前加上 explicit,我们告诉编译器只能使用显式构造函数调用,这样就避免了隐式类型转换。这可以帮助防止一些潜在的错误和提高代码的清晰度,因为它强调了在哪里发生了类型转换。

3.vector.emplace_back()

readers.emplace_back(readOperation, i); 这一句中,emplace_back 用于在 readers 向量的末尾构造一个 std::thread 对象。在这个过程中,实际上调用了 std::thread 的构造函数,它是通过可变参数模板来实现的。

在这种情况下,std::thread 的构造函数是通过以下的模板构造函数之一来完成的:

template <class F, class... Args>
explicit thread(F&& f, Args&&... args);

其中,F 是可调用对象(函数指针、函数对象等),而 Args 是可调用对象 f 的参数。

具体到你的例子,emplace_back 会根据参数类型来实例化 std::thread 的构造函数。假设 readOperation 是一个函数,i 是一个整数,那么构造函数会被实例化为:

std::thread::thread(void(*f)(int), int);

这个构造函数接受一个函数指针(指向 void readOperation(int) 类型的函数)和一个整数作为参数。std::thread 的构造函数会将 readOperation 函数和 i 参数传递给新创建的线程对象,以便线程在执行时调用这个函数,并传递参数 i

这样,std::thread 的构造函数会在内部启动一个新的线程,并执行 readOperation 函数,将 i 作为参数传递给它。这就是这个构造函数的基本工作原理。

emplace_back 相对于 push_back 的优点主要在于避免了额外的拷贝或移动操作,特别是对于容器中存储的复杂对象或者用户自定义类型而言。以下是 emplace_back 的一些优点:

  1. 避免了多余的拷贝或移动:

    • emplace_back 允许在容器中直接构造对象,而不是先创建一个临时对象再将其拷贝或移动到容器中。这在性能上可以带来显著的优势,尤其是对于大型、复杂的对象。
  2. 减少了额外的内存分配和释放:

    • 对于容器中的元素类型,emplace_back 可以直接在容器的内存中构造对象,而不需要为临时对象分配额外的内存。这有助于减少内存管理的开销。
  3. 支持完美转发:

    • emplace_back 使用了完美转发,允许将参数直接传递给元素类型的构造函数,而不需要显式地创建一个对象。这提供了更大的灵活性,特别是当元素类型的构造函数有多个参数时。
  4. 对于不可拷贝、不可移动类型的支持:

    • 对于某些类型,可能没有合适的拷贝构造函数或移动构造函数。在这种情况下,使用 emplace_back 可能是唯一的选择,因为它允许直接在容器中构造对象。
  5. 更好的性能:

    • 在某些情况下,使用 emplace_back 可以显著提高性能,尤其是当插入大量元素时。这是因为它避免了不必要的对象创建和拷贝/移动操作。

综上所述,emplace_back 提供了一种更加灵活和高效的元素插入方式,特别适用于处理复杂对象或者用户自定义类型的容器。

文章来源:https://blog.csdn.net/MrGong123/article/details/135371162
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。