C++ 理解虚拟析构函数

C++ 理解虚拟析构函数,c++,gcc,C++,Gcc,我试图熟悉OOP的概念,但不太理解virtual的概念 可以创建虚拟析构函数,但不能创建虚拟构造函数。为什么? 如何在内部处理虚拟析构函数?我的意思是链接说明了这个概念,但我的问题是如何调用vtables(派生和基本)的vptr?(对于虚拟成员函数,当这种情况发生时,通常只调用派生类的vptr指向的函数) 是否有其他可能需要使用虚拟析构函数的场景 有人能通过链接/示例帮助我理解上述概念吗?我假设我们有一个基类a,它是派生的B 1.:您可以通过A指针删除B,然后正确的方法是调用B析构函数。 然而,

我试图熟悉OOP的概念,但不太理解
virtual
的概念

  • 可以创建
    虚拟析构函数
    ,但不能创建
    虚拟构造函数
    。为什么?
  • 如何在内部处理
    虚拟析构函数
    ?我的意思是链接说明了这个概念,但我的问题是如何调用
    vtable
    s(派生和基本)的
    vptr
    ?(对于虚拟成员函数,当这种情况发生时,通常只调用派生类的
    vptr
    指向的函数)
  • 是否有其他可能需要使用
    虚拟析构函数的场景

  • 有人能通过链接/示例帮助我理解上述概念吗?

    我假设我们有一个基类a,它是派生的B

    1.:您可以通过A指针删除B,然后正确的方法是调用B析构函数。 然而,您不能说,当您实际上只是调用a构造函数时,应该创建一个B对象。根本没有这种情况。 你可以说:

    A* a = new B ();
    

    但两者都直接调用B的构造函数

    2.:嗯,我不完全确定,但我猜它将遍历类层次结构的相关部分,并搜索最接近的函数调用。如果函数不是虚拟函数,它将停止迭代并调用它

    3.:如果要从该类继承某些内容,则应始终使用虚拟析构函数。如果是最后一节课,你不应该

    可以创建虚拟析构函数,但不能创建虚拟构造函数。为什么?

    虚拟函数根据调用它们的对象的类型进行调度。调用构造函数时,没有对象——创建一个对象是构造函数的工作。没有对象,就不可能进行虚拟分派,因此构造函数不能是虚拟的

    如何在内部处理虚拟析构函数

    虚拟调度的内部细节由实现定义;该语言没有指定实现,只指定行为。通常,析构函数是通过vtable调用的,就像任何虚拟函数一样

    如何调用vtable(派生和基本)的vptr

    只有最派生的析构函数才会被虚拟调用。所有析构函数,无论是否为虚拟的,都将隐式调用所有成员和直接基类子对象的析构函数。(虚拟继承的情况稍微复杂一些;但这超出了这个问题的范围)

    有没有其他可能需要使用虚拟析构函数的场景

    为了支持多态删除,您需要一个;也就是说,能够通过指向基类型的指针删除派生类型的对象。如果基类型没有虚拟析构函数,这是不允许的,并且会给出未定义的行为

  • 因为虚拟函数是在运行时阶段调用的,而构造函数是在初始化阶段调用的,所以对象并没有被构造。因此,拥有一个虚拟构造函数是没有意义的

  • a。链接中仅调用基类析构函数的原因是,析构函数未标记为虚拟,因此析构函数地址在编译/链接时链接到基类析构函数,显然指针的类型是基类,而不是在编译时派生的

    b。了解为什么在将虚拟构造函数添加到基Destructor后同时调用基构造函数和派生构造函数。其行为如下所示: 派生d;//当d退出生命周期时,将调用派生的和基本的Destructor

  • 假设当您至少有一个虚拟函数时,您应该有一个虚拟Destructor


  • 首先,谈谈虚拟函数和非虚拟函数之间的区别:

    代码中的每个非虚拟函数调用都可以在编译或链接期间解析

    通过解析,我们的意思是可以由编译器或链接器计算函数的地址

    因此,在创建的目标代码中,函数调用可以替换为跳转到内存中该函数地址的操作代码

    使用虚拟函数,您可以调用只能在运行时解析的函数

    与其解释它,不如让我们运行一个简单的场景:

    class Animal
    {
        virtual void Eat(int amount) = 0;
    };
    
    class Lion : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    class Tiger : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    class Tigon : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    class Liger : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    void Safari(Animal* animals[], int numOfAnimals, int amount)
    {
        for (int i=0; i<numOfAnimals; i++)
            animals[i]->Eat(amount);
        // A different function may execute at each iteration
    }
    
    可以创建虚拟析构函数,但不能创建虚拟构造函数。 为什么?

    我会试着用外行的话来解释这件事。 C++中的类只在构造函数完成后才存在。每个基类在派生类及其成员(包括vtable链接)初始化之前都存在。因此,拥有虚拟构造函数是没有意义的(因为要构造,您需要知道类型)。此外(在c++中),从构造函数调用虚函数不起作用(因为派生类的vtable部分尚未设置)。如果仔细考虑一下,允许从构造函数调用虚拟函数会打开一个蠕虫程序罐(例如,如果在成员初始化之前调用派生类的虚拟函数会怎么样)

    就析构函数而言,在销毁点上,vtable是“完整的”,我们(c++运行时)完全知道该类型(可以这么说)。找到类型的最派生部分的析构函数(如果是虚拟的,则通过vtable),因此可以调用该析构函数,当然也可以调用所有基的析构函数

    如何在内部处理虚拟析构函数?我指的是链接 虚拟析构函数说明了这个概念,但我的问题是 VTable(派生和基本)的vptr都被调用

    析构函数的处理方式与普通虚拟函数相同(也就是说,如果地址是虚拟的,则会在vtable中查找地址,但代价是额外的一级(可能是2?)间接寻址)。此外,C++的保证
    class Animal
    {
        virtual void Eat(int amount) = 0;
    };
    
    class Lion : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    class Tiger : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    class Tigon : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    class Liger : public Animal
    {
        virtual void Eat(int amount) { ... }
    };
    
    void Safari(Animal* animals[], int numOfAnimals, int amount)
    {
        for (int i=0; i<numOfAnimals; i++)
            animals[i]->Eat(amount);
        // A different function may execute at each iteration
    }
    
    class A
    {
        A() {...}
        ~A() {...}
    };
    
    class B: public A
    {
        B() {...}
        ~B() {...}
    };
    
    void func()
    {
        A* b = new B(); // must invoke the destructor of class 'B' at some later point
        ...
        delete b; // the destructor of class 'B' is never invoked
    }