面试题答案
一键面试移动语义在函数返回对象时的工作原理
在C++中,当函数返回一个对象时,传统的做法是进行拷贝构造。然而,移动语义允许将资源(如动态分配的内存)直接从返回的临时对象转移到接收对象,而不是进行深拷贝。
当函数返回一个对象时,如果满足移动语义的条件(例如返回的对象是右值),编译器会调用移动构造函数,将临时对象的资源“窃取”并转移给接收对象。移动构造函数通常会将源对象的指针等资源设为nullptr
,以确保源对象析构时不会释放已转移的资源。
利用移动语义提高性能的方式
- 避免不必要的拷贝:对于包含动态分配资源(如大数组或复杂数据结构)的对象,拷贝操作可能非常昂贵。移动语义可以避免这种深拷贝,直接转移资源所有权,从而显著提高性能。
- 优化返回值优化(RVO):移动语义与RVO协同工作。在某些情况下,编译器可以直接在调用者的栈空间构造返回对象,避免任何拷贝或移动操作。即使RVO不可用,移动语义也能减少开销。
示例代码
#include <iostream>
#include <string>
class MyString {
private:
char* data;
size_t length;
public:
// 构造函数
MyString(const char* str) {
length = std::strlen(str);
data = new char[length + 1];
std::strcpy(data, str);
}
// 拷贝构造函数
MyString(const MyString& other) {
length = other.length;
data = new char[length + 1];
std::strcpy(data, other.data);
}
// 移动构造函数
MyString(MyString&& other) noexcept {
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
// 析构函数
~MyString() {
delete[] data;
}
// 赋值运算符重载
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
length = other.length;
data = new char[length + 1];
std::strcpy(data, other.data);
}
return *this;
}
// 移动赋值运算符重载
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
return *this;
}
void print() const {
std::cout << data << std::endl;
}
};
MyString createString() {
return MyString("Hello, World!");
}
int main() {
MyString str = createString();
str.print();
return 0;
}
在上述代码中,createString
函数返回一个MyString
对象。如果没有移动语义,返回时会调用拷贝构造函数。而有了移动语义,编译器会调用移动构造函数,将临时对象的资源转移给str
,避免了不必要的深拷贝,提高了性能。