zoukankan      html  css  js  c++  java
  • careercup-C和C++ 13.6

    13.6 基类的析构函数为何要声明为virtual?

    解答:

    用对象指针来调用一个函数,有以下两种情况:

    1. 如果是虚函数,会调用派生类中的版本。
    2. 如果是非虚函数,会调用指针所指类型的实现版本。

    析构函数也会遵循以上两种情况,因为析构函数也是函数嘛,不要把它看得太特殊。 当对象出了作用域或是我们删除对象指针,析构函数就会被调用。

    当派生类对象出了作用域,派生类的析构函数会先调用,然后再调用它父类的析构函数, 这样能保证分配给对象的内存得到正确释放。

    但是,如果我们删除一个指向派生类对象的基类指针,而基类析构函数又是非虚的话, 那么就会先调用基类的析构函数(上面第2种情况),派生类的析构函数得不到调用。

    例如:

    class Foo
    {
    public:
        void f();
    };
    
    class Bar: public Foo
    {
    public:
        void f();
    }
    
    Foo *p=new Bar();
    p->f();

    调用p->f()最后将会调用Foo::f(),这是因为p是指向Foo的指针,而f()不是虚函数。

    为确保p->f()会调用继承关系最末端的子类的f()实现,我们需要将f()声明为虚函数。

    现在,回到前面的析构函数。析构函数用于释放内存和资源。Foo的析构函数若不是虚拟的,那么,即使p实际上是Bar类型的,还是会调用Foo的析构函数。

    这就是为何要将析构函数声明为虚拟的原因——确保正确调用继承关系最末端的子类的析构函数

    例如:

    class Base{
    public:
        Base() { cout<<"Base Constructor"<<endl; }
        ~Base() { cout<<"Base Destructor"<<endl; }
    };
    class Derived: public Base{
    public:
        Derived() { cout<<"Derived Constructor"<<endl; }
        ~Derived() { cout<<"Derived Destructor"<<endl; }
    };
    int main(){
        Base *p = new Derived();
        delete p;
        return 0;
    }

    输出为:

    Base Constructor
    Derived Constructor
    Base Destructor

    上面结果表明,虽然基类指向的是派生类,但是调用析构函数释放p时,仍然只调用了基类的析构函数。

    如果我们把基类的析构函数声明为虚析构函数,这会使得所有派生类的析构函数也为虚。 从而使析构函数得到正确调用。

    将基类的析构函数声明为虚的之后,得到的输出是:

    Base Constructor
    Derived Constructor
    Derived Destructor
    Base Destructor

    因此,如果我们可能会删除一个指向派生类的基类指针时,应该把析构函数声明为虚函数。 事实上,《Effective C++》中的观点是,只要一个类有可能会被其它类所继承, 就应该声明虚析构函数。

  • 相关阅读:
    ocos 信号量
    C 语言 输入字符串 并计算输入的字符的长度
    C 语言简历一个文件夹 并自己输入字符 来取文件夹名字
    C语言 创建一个 txt 文件 bin输入字符 保存文件在工作文件夹里
    C语言 之 printf () 函数你真的会用吗?
    Impala的命令COMPUTE STATS
    python 函数eval
    YouTube排名第一的励志英文演讲《Dream(梦想)》
    impala 表迁移方式 partquet数据文件移动方法
    Python中@staticmethod和@classmethod的作用和区别
  • 原文地址:https://www.cnblogs.com/wuchanming/p/4154435.html
Copyright © 2011-2022 走看看