Filthy-ice 发表于 2023-5-7 15:50:48

对于是否需要虚析构函数和原因,这样理解有问题吗?

假设对于
class Base{
public:
    Base(void);
    ~Base();
    virtual void something() = 0;
};
class Derived:public Base{
public:
    Derived(void);
    ~Derived();
    void Dosomething();
};
//行为实现就不写了,假设都在这写了
int main()
{
    Derived* p = new Derived;
// Base* p = new Derived;
    p->Dosomething();
    delete p;
    return 0;
}

    首先,派生类不是说你实例化一个基类指针指向子类的对象才叫派生类。
    我在B站看了一个视频,说上面的代码改成"Base*指向子类对象,是派生类的一个条件" ,用类和对象比较,这明显不是一个层级,这个说法就是错的。
    派生是个过程,多态是派生的结果。
    在你对基类进行继承时,它的子类中只要有新的属性,和在基类同名的行为上更加细化的行为(基类的行为无法描述子类行为),那么这个子类就算是派生类。

    例: 一个基类 Human ,它有一个行为 makelove,你肯定不想每次创建对象时,还要输入两个数据:名字和性别。而且编程时,难以用命名空间区分。
         所以应该创建一个man派生类,和一个woman派生类。给这两个类自己独有的sex特征,和细致化的makelove行为实现,互为友元。即可makelove,嘿嘿。

    我的理解是:派生类包括继承类,但继承类不一定是派生类。类似于继承类是派生类的 充分不必要条件。


    我对虚析构的理解:
    在我们创建一个子派生类对象时,编译器扫描代码时,编译器会和cpu读取内存一样,采用就近原理偷懒。
int main()
{
    Derived* p = new Derived;
// Base* p = new Derived;
    p->Dosomething();
    delete p;
    return 0;
}


当用基类指针 Derived* 指向派生类时:
编译器读取派生类的结构应该是:
       {
       ↓ 基类属性;
       ↓ 基类行为;
       ↓    {
   →   子类属性;//从这里开始读代码,调用自己需要的
       ↓   子类行为;
             }
       }
因此,不需要虚函数也可以:构造子类属性 ->构造基类属性->基类Dosomething(没实现)->找到了子类Dosomething->子类析构(编译器发现销毁的对象不是基类)->基类析构。完工。
在 delete 对象时,编译器也知道先调子类的析构方法,还要调用基类的析构方法。例如你想kill一个人,肯定也知道只摧毁他的性特征是不行的。斩草除根。

当用基类指针 Base* 指向派生类时:
编译器读取派生类的结构应该是:
      {
   → 基类属性;//从这里开始读代码,调用自己需要的
       ↓ 基类行为;
       ↓    {
       ↓   子类属性;
       ↓   子类行为;
       }
因此,用无虚函数的方法调用时:构造基类属性->构造子类属性->基类Dosomething(没实现)->找到了子类Dosomething->基类析构。
在 delete 对象时,先调用的是基类的析构。编译器就会认为,基类都被消除了,子类能不消除? 和human一样,都不是人了,肯定消失了啊?!

在我看来,virtual作用像是改变接口被调用的优先级。将基类析构降了一个优先级,当编译器读一遍发现没有别的析构函数时,才会去调用虚方法区的接口。

这个理解怎么说??

歌者文明清理员 发表于 2023-5-7 15:52:42

一看不是求助帖

赚小钱 发表于 2023-5-7 16:32:18

你这个理解是错误的。

赚小钱 发表于 2023-5-7 16:34:50

1. 文中 当用基类指针 Derived* 指向派生类时:,这个表述有问题
2. 需要再学习一下继承关系的构造与析构顺序
3. 补充学习虚函数表
4. 太长,别的没注意看
加油
页: [1]
查看完整版本: 对于是否需要虚析构函数和原因,这样理解有问题吗?