面试题答案
一键面试- 调用顺序和内存管理分析
- 调用顺序:
- 当将
Derived
对象赋值给Base
对象指针所指向的对象时,如果使用的是默认的拷贝构造函数,首先会调用Base
类的拷贝构造函数,因为Base
类是Derived
类的基类,基类部分需要先被初始化。然后,如果Derived
类有自己的成员变量,会初始化Derived
类新增的成员变量。
- 当将
- 内存管理:
- 在
Base
类中有动态分配的内存,默认的拷贝构造函数会进行浅拷贝,即只是复制指针的值,而不是分配新的内存并复制内容。这样当两个对象(源对象和目标对象)都试图释放同一块内存时,就会导致内存泄漏和悬空指针问题。在Derived
类中,如果新增的成员变量也涉及动态分配内存,同样的浅拷贝问题也会出现。
- 在
- 调用顺序:
- 代码示例
#include <iostream>
#include <cstring>
class Base {
public:
Base(const char* str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
virtual ~Base() {
delete[] data;
}
// 虚函数示例
virtual void print() {
std::cout << "Base: " << data << std::endl;
}
private:
char* data;
};
class Derived : public Base {
public:
Derived(const char* str, int num) : Base(str), number(num) {}
void print() override {
std::cout << "Derived: " << number << " " << getBaseData() << std::endl;
}
private:
int number;
const char* getBaseData() {
return ((Base*)this)->data;
}
};
- 可能出现的问题
- 内存泄漏:如果使用默认的拷贝构造函数,如
Base b1("Hello"); Base b2 = b1;
,b1
和b2
会共享data
指针,当b1
和b2
析构时,会两次释放data
,导致内存泄漏。对于Derived
类对象之间的拷贝同样存在类似问题。 - 悬空指针:在浅拷贝后,如果一个对象析构,另一个对象的指针就会变成悬空指针,访问该指针会导致未定义行为。
- 内存泄漏:如果使用默认的拷贝构造函数,如
- 正确处理方式
- 深拷贝:
- 在
Base
类中,需要提供一个深拷贝构造函数:
- 在
- 深拷贝:
Base(const Base& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
Base& operator=(const Base& other) {
if (this != &other) {
delete[] data;
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
- 在`Derived`类中,同样需要提供深拷贝构造函数和赋值运算符重载:
Derived(const Derived& other) : Base(other), number(other.number) {}
Derived& operator=(const Derived& other) {
if (this != &other) {
Base::operator=(other);
number = other.number;
}
return *this;
}
- 使用智能指针:
- 可以将
Base
类中的data
改为std::unique_ptr<char[]>
,这样就不需要手动管理内存,智能指针会自动处理对象拷贝和析构时的内存管理。
- 可以将
class Base {
public:
Base(const char* str) {
data.reset(new char[strlen(str) + 1]);
strcpy(data.get(), str);
}
// 不需要手动定义析构函数,unique_ptr会自动处理
// 虚函数示例
virtual void print() {
std::cout << "Base: " << data.get() << std::endl;
}
private:
std::unique_ptr<char[]> data;
};
通过上述方法,可以确保在对象拷贝场景下程序的正确性和高效性,避免内存泄漏等问题。