面试题答案
一键面试1. 赋值运算符和拷贝构造函数在复杂继承关系下的行为影响
在具有复杂继承关系且包含虚函数的C++类体系中:
- 拷贝构造函数:
- 当从派生类对象拷贝构造另一个派生类对象时,不仅要拷贝派生类自身的成员变量,还要调用基类的拷贝构造函数来拷贝基类部分。同时,虚函数表指针也会被正确复制,因为它是对象布局的一部分。
- 如果使用基类的拷贝构造函数来构造派生类对象(例如
Base b = d1;
,其中d1
是Derived1
对象),这会发生切片现象,派生类特有的成员变量和虚函数表指针会被截断,只剩下基类部分。
- 赋值运算符:
- 当进行派生类对象之间的赋值时,需要先调用基类的赋值运算符来处理基类部分的赋值,然后再处理派生类自身成员变量的赋值。同样,虚函数表指针会正确更新。
- 当使用基类对象给派生类对象赋值时(例如
d1 = b;
),也会发生切片现象,只更新派生类对象中的基类部分。
2. 代码示例
#include <iostream>
class Base {
public:
int baseData;
Base(int data) : baseData(data) {}
// 虚函数
virtual void print() const {
std::cout << "Base::print: baseData = " << baseData << std::endl;
}
// 拷贝构造函数
Base(const Base& other) : baseData(other.baseData) {
std::cout << "Base copy constructor" << std::endl;
}
// 赋值运算符
Base& operator=(const Base& other) {
if (this != &other) {
baseData = other.baseData;
std::cout << "Base assignment operator" << std::endl;
}
return *this;
}
virtual ~Base() {}
};
class Derived1 : public Base {
public:
int derivedData1;
Derived1(int base, int derived) : Base(base), derivedData1(derived) {}
// 重写虚函数
void print() const override {
std::cout << "Derived1::print: baseData = " << baseData << ", derivedData1 = " << derivedData1 << std::endl;
}
// 拷贝构造函数
Derived1(const Derived1& other) : Base(other), derivedData1(other.derivedData1) {
std::cout << "Derived1 copy constructor" << std::endl;
}
// 赋值运算符
Derived1& operator=(const Derived1& other) {
if (this != &other) {
Base::operator=(other);
derivedData1 = other.derivedData1;
std::cout << "Derived1 assignment operator" << std::endl;
}
return *this;
}
~Derived1() {}
};
class Derived2 : public Base {
public:
int derivedData2;
Derived2(int base, int derived) : Base(base), derivedData2(derived) {}
// 重写虚函数
void print() const override {
std::cout << "Derived2::print: baseData = " << baseData << ", derivedData2 = " << derivedData2 << std::endl;
}
// 拷贝构造函数
Derived2(const Derived2& other) : Base(other), derivedData2(other.derivedData2) {
std::cout << "Derived2 copy constructor" << std::endl;
}
// 赋值运算符
Derived2& operator=(const Derived2& other) {
if (this != &other) {
Base::operator=(other);
derivedData2 = other.derivedData2;
std::cout << "Derived2 assignment operator" << std::endl;
}
return *this;
}
~Derived2() {}
};
int main() {
Derived1 d1(1, 2);
Derived1 d2(d1); // 调用Derived1拷贝构造函数
d2.print();
Derived2 d3(3, 4);
d3 = d2; // 调用Base赋值运算符和Derived2赋值运算符,虽然类型不匹配,但这里为了展示赋值过程
d3.print();
Base b = d1; // 切片现象,调用Base拷贝构造函数
b.print();
return 0;
}
3. 确保正确行为的方法
- 显式定义拷贝构造函数和赋值运算符:在每个类(基类和派生类)中显式定义拷贝构造函数和赋值运算符,确保基类和派生类的成员变量都被正确复制。
- 调用基类的对应函数:在派生类的拷贝构造函数和赋值运算符中,要调用基类的拷贝构造函数和赋值运算符来处理基类部分。
- 使用虚析构函数:在基类中定义虚析构函数,确保在通过基类指针删除派生类对象时,能正确调用派生类的析构函数,避免内存泄漏。
通过以上方法,可以确保在具有复杂继承关系且包含虚函数的C++类体系中,对象赋值和拷贝构造操作的正确行为。