面试题答案
一键面试原理阐述
- 移动语义和右值引用基础:
- C++11 引入右值引用
&&
,用于标识临时对象(右值)。移动语义利用右值引用,使得资源所有权可以高效地从一个对象转移到另一个对象,而不是进行深度拷贝。例如,对于一个包含动态分配内存的对象,移动操作只需转移指针和重置源对象,而拷贝操作需要分配新内存并复制数据。 - 移动构造函数和移动赋值运算符的声明形式如下:
class MyClass { public: MyClass(MyClass&& other) noexcept; // 移动构造函数 MyClass& operator=(MyClass&& other) noexcept; // 移动赋值运算符 };
- C++11 引入右值引用
- 多态与虚函数:
- 多态的必要条件是:基类中定义虚函数,派生类重写这些虚函数,通过基类指针或引用调用虚函数。
- 当一个类含有虚函数时,它会有一个虚函数表指针(vptr),用于在运行时确定实际调用的函数。这增加了对象的大小和一定的开销。
- 优化含有虚函数类的移动操作性能:
- 在移动含有虚函数的类对象时,关键是要高效地转移资源而不影响虚函数机制。由于虚函数表指针是对象的一部分,移动时直接转移 vptr 即可,它指向的虚函数表在整个程序中是共享的,不需要额外操作。对于其他资源(如动态分配的内存),按照常规移动语义处理,即转移指针并重置源对象。
代码示例
#include <iostream>
#include <memory>
class Base {
public:
virtual ~Base() = default;
virtual void print() const {
std::cout << "Base" << std::endl;
}
};
class Derived : public Base {
private:
std::unique_ptr<int[]> data;
int size;
public:
Derived(int s) : size(s), data(std::make_unique<int[]>(s)) {
for (int i = 0; i < s; ++i) {
data[i] = i;
}
}
// 移动构造函数
Derived(Derived&& other) noexcept : size(other.size), data(std::move(other.data)) {
other.size = 0;
other.data = nullptr;
}
// 移动赋值运算符
Derived& operator=(Derived&& other) noexcept {
if (this != &other) {
size = other.size;
data = std::move(other.data);
other.size = 0;
other.data = nullptr;
}
return *this;
}
void print() const override {
std::cout << "Derived" << std::endl;
}
};
void test_move(Base&& base) {
Base* ptr = &base;
ptr->print();
}
int main() {
Derived d1(5);
test_move(std::move(d1));
return 0;
}
在上述代码中:
Base
类是一个含有虚函数的基类。Derived
类继承自Base
并含有动态分配的内存(通过std::unique_ptr<int[]>
)。Derived
类提供了移动构造函数和移动赋值运算符,高效地转移data
指针,同时不影响虚函数机制。test_move
函数接受一个右值引用的Base
对象,通过基类指针调用虚函数print
,展示了移动语义在多态场景下的正常工作。