vector的emplace_back函数

std::vector::emplace_back() 是 C++11 中引入的一个成员函数,用于在向量的末尾直接构造一个新元素,而无需显式创建临时对象。

push_back() 函数不同,emplace_back() 接受的参数是用于构造新元素的实参列表,而不是一个已经构造的对象。它使用这些参数在向量的末尾就地构造一个新的元素。

emplace_back() 函数的优势在于避免了构造临时对象和复制/移动操作的开销,因为它直接在向量内存中构造元素。这对于构造成本较高的对象类型尤其有用。

以下是使用 emplace_back() 的示例:

#include <vector>

struct MyStruct {
    int value1;
    double value2;

    MyStruct(int v1, double v2) : value1(v1), value2(v2) {
        // 构造函数中的其他初始化操作...
    }
};

int main() {
    std::vector<MyStruct> myVector;

    // 使用 emplace_back() 直接构造新元素
    myVector.emplace_back(42, 3.14);
    myVector.emplace_back(17, 2.71);

    // myVector 现在包含两个 MyStruct 元素

    return 0;
}

在上面的示例中,我们使用 emplace_back() 函数向 myVector 向量中添加了两个 MyStruct 类型的元素。通过传递构造函数所需的参数,我们可以直接在向量中构造新元素,而不需要显式创建临时对象。

总而言之,emplace_back()std::vector 类模板提供的一个方便的成员函数,可以直接在向量末尾就地构造新元素。它可以减少构造和移动元素的开销,并提高代码的性能和效率。

### C++ 中 `std::vector` 的 `emplace_back` 方法详解 #### 什么是 `emplace_back` `emplace_back` 是 C++11 引入的一个成员函数,用于在 `std::vector` 容器的末尾直接构造一个新元素。它通过将参数转发给目标类型的构造函数来实现这一点。 与传统的 `push_back` 不同的是,`emplace_back` 避免了额外的复制或移动操作,因为它是在容器内部直接构造对象[^3]。 --- #### 函数签名 以下是 `emplace_back` 的标准定义: ```cpp template< class... Args > void emplace_back(Args&&... args); ``` 该模板允许传递任意数量和类型的参数到目标类型的构造函数中。这些参数会被完美转发(perfect forwarding),从而减少不必要的中间对象创建。 --- #### 使用场景分析 ##### 场景一:基本数据类型 对于简单的内置类型(如 `int`, `double` 等),`emplace_back` 和 `push_back` 行为几乎一致,因为它们都只是简单地存储值。 ```cpp #include <iostream> #include <vector> int main() { std::vector<int> vec; vec.emplace_back(42); // 构造 int 类型并添加到 vector 尾部 std::cout << vec[0] << "\n"; // 输出 42 } ``` ##### 场景二:复杂自定义类 当处理复杂的自定义类时,`emplace_back` 显示出了它的优势——减少了临时对象的创建次数。 假设有一个类 `Test` 如下所示: ```cpp class Test { public: explicit Test(int value) : data(value) { std::cout << "construct\n"; } ~Test() { std::cout << "destruct\n"; } private: int data; }; ``` 使用 `emplace_back` 添加对象时会直接调用其构造函数: ```cpp #include <vector> #include <iostream> int main() { std::vector<Test> vec; vec.emplace_back(42); // 调用了 Test 的构造函数 // 并未创建任何临时对象 } // 输出: construct // destruct (程序结束时销毁) ``` 而如果改用 `push_back`,则可能会涉及额外的一次拷贝或移动操作: ```cpp vec.push_back(Test(42)); // 创建了一个临时对象 Test(42),随后将其拷贝/移动至 vector 中 // 这里有两次构造过程以及一次析构过程 ``` ##### 场景三:潜在风险 尽管 `emplace_back` 提供了更高的效率,但在某些情况下也可能引入隐患。例如,在向含有特定约束条件的数据结构中插入元素时,错误的参数可能导致意外行为。 考虑下面的例子: ```cpp std::vector<std::regex> v; v.emplace_back(nullptr); // 编译可以通过,但运行时抛出异常[^1] ``` 上述代码试图将一个空指针作为正则表达式的初始化参数传入,这显然不符合预期逻辑,最终会在运行期触发异常。 因此,在实际开发过程中应当谨慎对待每种情况下的输入验证工作。 --- #### 性能对比 (`push_back` vs `emplace_back`) | **特性** | **`push_back`** | **`emplace_back`** | |--------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| | 参数传递方式 | 接收已存在的对象副本 | 直接利用原始参数构建所需实例 | | 是否支持原位构造 | 否 | 是 | | 复杂度(理想状态) | 可能存在额外的拷贝或者转移开销 | 更高效 | 具体表现取决于所管理的对象是否有有效的移动语义及其内部机制设计等因素影响[^4]。 --- #### 示例代码展示 以下是一些具体的例子说明两者的差异: ###### 测试案例 1 - 左值插入 ```cpp struct Data { Data(const char* str): name(str){ std::cout << "Constructing with string.\n";} Data(Data const& other):name(other.name+" copy"){std::cout<<"Copy constructing.\n";} ~Data(){if(!name.empty())std::cout<<name<<" destroyed.\n";} std::string name; }; int main(){ std::vector<Data> container; Data d("Original"); container.emplace_back(d); /* Output: Constructing with string. Copy constructing. */ return 0; } ``` 这里可以看到即使采用 `emplace_back` ,由于提供了左值仍需执行一次深拷贝动作。 ###### 测试案例 2 - 移动语义应用 ```cpp container.emplace_back(std::move(d));/* Only one construction happens here due to move semantics.*/ ``` 此时仅发生单次资源占有权转让而非真正意义上的内容迁移。 --- ### 结论 综上所述,虽然两者都能完成相似的任务即扩展动态数组长度的同时追加指定项进去;但是考虑到性能优化方面的需求的话,则推荐优先选用后者—`emplace_back()` 来达成目的[^2].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值