emplace_back
的工作机制
- 构造过程:
emplace_back
使用传入的参数直接调用目标类型的构造函数,在容器预分配的内存中构造对象。例如:std::vector<std::string> vec; vec.emplace_back("hello", 5); // 构造 std::string("hello", 5)
- 内存管理:容器负责管理这些对象的内存,
emplace_back
只是将对象“放入”容器,对象的销毁由容器的后续行为决定。
对象销毁的详细时机
以下是 emplace_back
构造的对象在哪些情况下会被销毁的全面分析:
1. 容器销毁时
- 时机:当容器对象超出作用域或被显式销毁时,容器会调用其析构函数,销毁所有元素,包括通过
emplace_back
添加的对象。 - 机制:容器的析构函数会逐个调用元素的析构函数(通常按照逆序销毁)。
- 示例:
输出:#include <vector> #include <iostream> struct MyClass { int value; MyClass(int v) : value(v) { std::cout << "Constructed: " << value << "\n"; } ~MyClass() { std::cout << "Destroyed: " << value << "\n"; } }; int main() { { std::vector<MyClass> vec; vec.emplace_back(42); // 构造对象 } // vec 超出作用域,销毁所有元素 return 0; }
Constructed: 42 Destroyed: 42
2. 元素被移除时
- 时机:当通过容器的方法(如
pop_back
、erase
等)移除元素时,相应的对象会被销毁。 - 机制:
- 对于
std::vector
的pop_back
,最后一个元素被销毁,析构函数被调用。 - 对于
erase
,移除指定位置的元素,其析构函数被调用,剩余元素可能被移动。
- 对于
- 示例:
输出:std::vector<MyClass> vec; vec.emplace_back(42); // 构造对象 vec.pop_back(); // 移除并销毁最后一个元素
Constructed: 42 Destroyed: 42
3. 容器重新分配内存时(特定于 std::vector
等动态数组容器)
-
时机:对于
std::vector
,如果当前容量(capacity
)不足以容纳新元素,emplace_back
会触发内存重新分配。这会导致现有元素被移动(或拷贝,如果没有移动构造函数)到新内存,旧内存中的对象被销毁。 -
机制:
- 分配新的更大内存块。
- 将现有元素移动到新内存(调用移动构造函数)。
- 销毁旧内存中的对象(调用析构函数)。
- 在新内存中构造新对象。
-
示例:
std::vector<MyClass> vec; vec.reserve(1); // 容量为 1 vec.emplace_back(42); // 构造对象 vec.emplace_back(43); // 容量不足,触发重新分配
输出可能为:
Constructed: 42 Constructed: 43 Destroyed: 42 // 旧对象被销毁(移动后) Destroyed: 43 // vec 销毁时 Destroyed: 42 // 新位置的对象销毁
- 注意:如果类型实现了移动构造函数,销毁的是移动后的“空壳”对象;否则会拷贝并销毁原对象。
-
避免重新分配:使用
reserve
预分配足够容量,可以避免这种销毁:vec.reserve(2); // 预留空间,避免重新分配
4. 容器清空时
- 时机:调用
clear()
会移除并销毁容器中的所有元素。 - 机制:
clear()
调用每个元素的析构函数,并将容器大小(size
)置为 0。 - 示例:
输出:std::vector<MyClass> vec; vec.emplace_back(42); vec.clear(); // 销毁所有元素
Constructed: 42 Destroyed: 42
5. 异常情况
- 构造失败:
- 如果
emplace_back
在构造对象时抛出异常,且对象未构造完成,则不会加入容器,也不会有对象需要销毁。 - 示例:
输出:struct MyClass { MyClass(int x) { if (x < 0) throw std::runtime_error("Negative!"); std::cout << "Constructed: " << x << "\n"; } ~MyClass() { std::cout << "Destroyed\n"; } }; int main() { std::vector<MyClass> vec; try { vec.emplace_back(-1); // 抛出异常 } catch (...) { std::cout << "Exception caught\n"; } }
Exception caught
- 如果
- 内存重新分配失败:
- 如果
emplace_back
触发重新分配,但新内存分配失败(抛出std::bad_alloc
),现有元素保持不变,新对象不会构造完成,不影响已有对象的销毁。
- 如果
不同容器的差异
std::vector
:因动态数组特性,可能因重新分配内存导致对象销毁。std::list
:基于节点,无重新分配,对象仅在移除或容器销毁时销毁。std::deque
:分块存储,emplace_back
通常只影响新块,现有元素不会因重新分配而销毁。
与 push_back
的对比
push_back
:传入已有对象(可能是临时对象),将其拷贝或移动到容器中。临时对象在push_back
后立即销毁,而容器内的对象销毁时机与emplace_back
类似。emplace_back
:直接构造,无临时对象,销毁完全由容器管理。
总结
emplace_back
构造的对象在以下时机销毁:
- 容器销毁:超出作用域或析构时。
- 元素移除:
pop_back
、erase
等操作。 - 内存重新分配:
std::vector
容量不足时,旧对象被移动后销毁。 - 容器清空:
clear()
调用时。 - 异常:构造失败不影响已有对象,重新分配失败保持状态。