面试题答案
一键面试分析
当从 std::vector<MyClass>
中移除一个元素时,std::vector
通常会使用拷贝构造函数来移动后续元素以填补移除元素的位置。如果 MyClass
包含动态资源,使用拷贝构造函数可能会导致不必要的资源复制。通过重载 MyClass
的移动构造函数和移动赋值运算符,可以让 std::vector
在移除元素时使用移动语义,从而提高资源转移效率。
代码示例
#include <iostream>
#include <vector>
#include <memory>
class MyClass {
public:
std::unique_ptr<int> data;
// 构造函数
MyClass() : data(std::make_unique<int>(0)) {
std::cout << "MyClass constructor" << std::endl;
}
// 拷贝构造函数
MyClass(const MyClass& other) : data(std::make_unique<int>(*other.data)) {
std::cout << "MyClass copy constructor" << std::endl;
}
// 移动构造函数
MyClass(MyClass&& other) noexcept : data(std::move(other.data)) {
std::cout << "MyClass move constructor" << std::endl;
}
// 拷贝赋值运算符
MyClass& operator=(const MyClass& other) {
if (this != &other) {
data = std::make_unique<int>(*other.data);
}
std::cout << "MyClass copy assignment operator" << std::endl;
return *this;
}
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) {
data = std::move(other.data);
}
std::cout << "MyClass move assignment operator" << std::endl;
return *this;
}
~MyClass() {
std::cout << "MyClass destructor" << std::endl;
}
};
int main() {
std::vector<MyClass> vec;
vec.emplace_back();
vec.emplace_back();
auto it = vec.begin();
vec.erase(it);
return 0;
}
在上述代码中:
MyClass
包含一个std::unique_ptr<int>
来管理动态资源。- 定义了拷贝构造函数、移动构造函数、拷贝赋值运算符和移动赋值运算符。
- 在
main
函数中,创建了一个std::vector<MyClass>
,插入两个元素,然后移除第一个元素。当移除元素时,如果移动构造函数和移动赋值运算符定义正确,std::vector
将使用移动语义。
性能陷阱
- 未定义移动操作:如果没有定义移动构造函数和移动赋值运算符,
std::vector
将使用拷贝操作,这可能导致性能下降,尤其是在动态资源较大时。 - 异常安全:移动操作通常标记为
noexcept
,以告知编译器该操作不会抛出异常。如果移动操作可能抛出异常,std::vector
的某些操作(如erase
)可能无法提供强异常安全保证。 - 容量变化:当
std::vector
的容量不足时,插入或移除元素可能导致内存重新分配。这会涉及到元素的拷贝或移动,如果元素数量较多,可能会带来性能开销。在移除元素后,如果预计不会再添加大量元素,可以考虑调用vec.shrink_to_fit()
来减少不必要的内存占用。