huihut / interview

📚 C/C++ 技术面试基础知识总结,包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。This repository is a summary of the basic knowledge of recruiting job seekers and beginners in the direction of C/C++ technology, including language, program library, data structure, algorithm, system, network, link loading library, interview experience, recruitment, recommendation, etc.
https://interview.huihut.com
Other
34.8k stars 7.97k forks source link

C++小白请教问题。 #59

Closed ChineseBoyLY closed 4 years ago

ChineseBoyLY commented 4 years ago

// 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。 delete ptr; ptr = nullptr;

对于这里不是很理解。意思是实际调用的是delete ptr; 内部却自动析构了2个对象?

lz5921 commented 4 years ago

派生类会先调用基类构造函数的。既然调用了基类的构造函数,当然要析构。 某些情况下,我们通过基类指针指向派生类,这个时候, 析构函数如果不是虚函数,并且通过基类的指针来销毁对象,那么delete 的时候就只会调用基类的构造函数。这就出问题了。

huihut commented 4 years ago
  1. delete ptr; 只析构 ptr 指向的一个对象,但这一个对象的析构可能会有很多清理工作要做。
  2. 为什么派生类和基类的析构函数都要调? 因为 ptr 指向的对象在构造的时候,基类可能开辟了一些内存,需要调用基类的析构释放基类自己开辟的那部分内存;派生类可能也开辟了另一些内存,需要调用派生类的析构释放派生类自己开辟的另一部分内存。基类与派生类互相不知道对方做了哪些工作,所以需要谁申请,谁释放,因此都要调。
ChineseBoyLY commented 4 years ago

如果发生了多态,又没有用 virtual 来修饰 ~Base(),那是不是意味着必然就会发生泄漏了。这种code的写法就是错误的?还是有其他办法来释放Base产生的内存呢?

huihut commented 4 years ago

Q:如果发生了多态,又没有用 virtual 来修饰 ~Base(),那是不是意味着必然就会发生泄漏了。这种code的写法就是错误的?
A:不一定,如果没申请堆内存、系统资源等,就不需要释放了,也就可以不需要虚析构

Q:还是有其他办法来释放Base产生的内存呢?
A:一般 Base 的析构都会自动调用的

关于多态可以看下:https://www.runoob.com/cplusplus/cpp-polymorphism.html

关于虚析构可以跑下下面的代码:

#include <iostream>
class Base
{
public:
    Base() { std::cout << "Base::Base()" << std::endl; }
    virtual ~Base() { std::cout << "Base::~Base()" << std::endl; }
    virtual void DoSomething() { std::cout << "Base::DoSomething()" << std::endl; };
};

class Derived : public Base
{
public:
    Derived() { std::cout << "Derived::Derived()" << std::endl; };
    ~Derived() { std::cout << "Derived::~Derived()" << std::endl; };
    void DoSomething() { std::cout << "Derived::DoSomething()" << std::endl; };
};
int main()
{
    Base *p = new Derived;
    p->DoSomething();
    delete p;
    return 0;
}

然后删掉基类析构的 virtual,再跑下,看看调用的方法与顺序就清楚了。

huihut commented 4 years ago

@ChineseBoyLY 如果没有其他问题,我将关闭这个 issue