MST
星途 面试题库

面试题:C++中拷贝构造函数与多态性及内存管理的综合考察

假设有一个基类`Base`和一个派生类`Derived`,`Base`类中有虚函数和动态分配的内存。`Derived`类重写了部分虚函数并增加了自己的成员变量。在涉及对象拷贝(如将`Derived`对象赋值给`Base`对象指针所指向的对象)的场景下,详细分析拷贝构造函数的调用顺序和内存管理情况,包括可能出现的内存泄漏等问题。请给出代码示例并深入阐述如何正确处理这些情况,确保程序的正确性和高效性。
33.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 调用顺序和内存管理分析
    • 调用顺序
      • 当将Derived对象赋值给Base对象指针所指向的对象时,如果使用的是默认的拷贝构造函数,首先会调用Base类的拷贝构造函数,因为Base类是Derived类的基类,基类部分需要先被初始化。然后,如果Derived类有自己的成员变量,会初始化Derived类新增的成员变量。
    • 内存管理
      • Base类中有动态分配的内存,默认的拷贝构造函数会进行浅拷贝,即只是复制指针的值,而不是分配新的内存并复制内容。这样当两个对象(源对象和目标对象)都试图释放同一块内存时,就会导致内存泄漏和悬空指针问题。在Derived类中,如果新增的成员变量也涉及动态分配内存,同样的浅拷贝问题也会出现。
  2. 代码示例
#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;
    }
};
  1. 可能出现的问题
    • 内存泄漏:如果使用默认的拷贝构造函数,如Base b1("Hello"); Base b2 = b1;b1b2会共享data指针,当b1b2析构时,会两次释放data,导致内存泄漏。对于Derived类对象之间的拷贝同样存在类似问题。
    • 悬空指针:在浅拷贝后,如果一个对象析构,另一个对象的指针就会变成悬空指针,访问该指针会导致未定义行为。
  2. 正确处理方式
    • 深拷贝
      • 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;
};

通过上述方法,可以确保在对象拷贝场景下程序的正确性和高效性,避免内存泄漏等问题。