面试题答案
一键面试1. 调用顺序、作用及可能引发的问题
- 调用顺序:
- 拷贝构造函数:当用一个已存在对象创建新对象时调用。在多继承和虚继承体系下,先调用基类(包括虚基类)的拷贝构造函数,顺序按照继承列表中声明的顺序,然后调用自身类的拷贝构造函数。
- 赋值运算符:当对已存在对象进行赋值操作时调用。先调用目标对象的析构函数释放原有资源(如果有),然后调用赋值运算符函数,同样先处理基类(包括虚基类)的赋值,再处理自身类的赋值。
- 作用:
- 拷贝构造函数:用于创建一个与已有对象内容相同的新对象,它负责初始化新对象的成员变量,包括从基类继承而来的成员变量。
- 赋值运算符:用于将一个已存在对象的内容赋值给另一个已存在对象,在赋值过程中需要正确处理资源的释放和重新分配,以保证对象状态的一致性。
- 可能引发的问题:
- 数据重复:在菱形继承结构中,如果不使用虚继承,可能会导致从公共基类继承的成员变量在派生类中出现多份拷贝,造成数据冗余。
- 内存管理问题:如果对象包含动态分配的资源(如指针指向的内存),不正确的拷贝构造函数和赋值运算符可能导致内存泄漏(如没有释放原有资源就重新分配)或悬空指针(如释放资源后未将指针置空)。
2. 菱形继承结构下的设计思路
- 虚继承:使用虚继承确保公共基类在最终派生类中只有一份拷贝。这样在赋值和拷贝构造时,不会出现数据重复的问题。
- 合理实现拷贝构造函数:
- 首先调用虚基类的拷贝构造函数,确保虚基类部分被正确初始化。
- 然后按照继承列表顺序调用非虚基类的拷贝构造函数。
- 最后初始化自身类的成员变量。
- 合理实现赋值运算符:
- 首先检查是否是自赋值,如果是则直接返回。
- 调用虚基类的赋值运算符进行虚基类部分的赋值。
- 按照继承列表顺序调用非虚基类的赋值运算符。
- 释放自身类动态分配的资源(如果有),并重新分配资源进行赋值。
3. 示例代码
#include <iostream>
#include <cstring>
class Base {
public:
int value;
Base(int v = 0) : value(v) {}
Base(const Base& other) : value(other.value) {}
Base& operator=(const Base& other) {
if (this != &other) {
value = other.value;
}
return *this;
}
};
class Derived1 : virtual public Base {
public:
Derived1(int v = 0) : Base(v) {}
Derived1(const Derived1& other) : Base(other) {}
Derived1& operator=(const Derived1& other) {
if (this != &other) {
Base::operator=(other);
}
return *this;
}
};
class Derived2 : virtual public Base {
public:
Derived2(int v = 0) : Base(v) {}
Derived2(const Derived2& other) : Base(other) {}
Derived2& operator=(const Derived2& other) {
if (this != &other) {
Base::operator=(other);
}
return *this;
}
};
class FinalDerived : public Derived1, public Derived2 {
public:
char* str;
FinalDerived(int v = 0, const char* s = "") : Derived1(v), Derived2(v) {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
FinalDerived(const FinalDerived& other) : Derived1(other), Derived2(other) {
str = new char[strlen(other.str) + 1];
strcpy(str, other.str);
}
FinalDerived& operator=(const FinalDerived& other) {
if (this != &other) {
Derived1::operator=(other);
Derived2::operator=(other);
delete[] str;
str = new char[strlen(other.str) + 1];
strcpy(str, other.str);
}
return *this;
}
~FinalDerived() {
delete[] str;
}
};
int main() {
FinalDerived obj1(10, "Hello");
FinalDerived obj2(obj1);
FinalDerived obj3;
obj3 = obj1;
return 0;
}
在上述代码中,通过虚继承 Base
类,避免了在 FinalDerived
类中 Base
类成员变量的重复。同时,合理实现了拷贝构造函数和赋值运算符,确保了对象复制过程中的数据一致性和正确的内存管理。