zoukankan      html  css  js  c++  java
  • 基类与派生类,父类指针指向子类对象

    先来看一段程序(来自趋势科技笔试):

    #include <iostream>
    using namespace std;
    
    class Base_J 
    {
    public:
    	Base_J()
    	{
    		cout << "Base Created" << endl;
    	}
    	~Base_J()
    	{
    		cout << "Base Destroyed" << endl;
    	}
    };
    
    class Derived_J : public Base_J
    {
    public:
    	Derived_J()
    	{
    		cout << "Derived Created" << endl;
    	}
    	~Derived_J()
    	{
    		cout << "Derived Destroyed" << endl;
    	}
    };
    
    int main()
    {
    	Base_J *pB = new Derived_J();
    	delete pB;
    	pB = NULL;
    	system("pause");
    	return 0;
    }
    

    下面是它的运行结果:


    上述结果比较诡异,按理说每个创建的类中的内容都应该在不用时被销毁,但是上述只是销毁了子类对象中属于父类的部分,这是什么原因呢?

    先看一下几个结论:

    1,如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数(“实函数”暂且这么叫)(静态联翩

    2,如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很危险,也不符合生活习惯,在程序设计上也会给程序员带来困扰。(一般不会这么去定义)

    3,如果基础类和衍生类定义了相同名称的成员函数,那么通过对象指针调用成员函数时,到底调用那个函数要根据指针的原型来确定,而不是根据指针实际指向的对象类型确定

    虚拟函数就是为了对“如果你以一个基础类指针指向一个衍生类对象,那么通过该指针,你只能访问基础类定义的成员函数”这条规则反其道而行之的设计。

    如果你预期衍生类由可能重新定义一个成员函数,那么你就把它定义成虚拟函数( virtual )。
    polymorphism就是让处理基础类别对象的程序代码能够通透的继续适当地处理衍生类对象。

    纯虚拟函数:
    virtual void myfunc ( ) =0;
    纯虚拟函数不许定义其具体动作,它的存在只是为了在衍生类钟被重新定义。只要是拥有纯虚拟函数的类,就是抽象类,它们是不能够被实例化的只能被继承)。如果一个继承类没有改写父类中的纯虚函数,那么他也是抽象类,也不能被实例化。
    抽象类不能被实例化,不过我们可以拥有指向抽象类的指针,以便于操纵各个衍生类。
    虚拟函数衍生下去仍然是虚拟函数,而且还可以省略掉关键字“virtual”。
    引自http://blog.csdn.net/taoyingzhushui/article/details/8100434。

    由上述可见,程序错误出在:析构函数本应该设置成虚函数。否则由上述第1条可知,在父类指针指向子类对象做对象销毁时,由于析构函数不是虚函数,则delete时,父类指针只能调用父类自己的析构函数,这就造成了上述对象部分销毁的错误状况。


    《Effective C++》条款 07 p40:为多态基类声明virtual析构函数

    《C++ Primer 5》 p527:在C++语言中,当我们使用基类的引用(或指针)调用一个虚函数时将发生动态绑定。

    另外,Effective C++中有关于动态多态(运行时的多态:虚函数)和静态多态(编译时的多态:模板)的说明。


    现在我们将基类的析构改成虚函数试试:

    class Base_J 
    {
    public:
    	Base_J()
    	{
    		cout << "Base Created" << endl;
    	}
    	virtual ~Base_J()
    	{
    		cout << "Base Destroyed" << endl;
    	}
    };
    
    

    结果便按照我们设想的执行了:



    派生类构造函数和析构函数的执行顺序

    当创建对象时,编译系统会自动调用构造函数。当撤销对象时,编译系统会自动调用析构函数。当创建派生类的对象时,首先执行基类的构造函数,然后执行派生类的构造函数。当撤销对象时,则先执行派生类的析构函数,然后再执行基类的析构函数。


    《Effective C++》条款 07 p44:


    • 带多态性质的base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数
    • Classes的设计目的如果不是作为base classes 使用,或不是为了具备多态性,就不该声明virtual析构函数。

        父类指针指向子类对象,而子类对象却经由父类指针被删除,当父类有个non-virtual析构函数是,就会引起灾难。

        C++明确指出,当子类对象经由一个父类指针被删除,而该父类带有一个non-virtual析构函数,其结果未定义--实际执行时通常发生的是对象的derived成分没有被销毁。子类的析构函数也未能被执行。然而其base class成分通常会被销毁,于是造成了诡异的“局部销毁”对象。这可能造成资源泄露,败坏数据结构等

  • 相关阅读:
    struts2知识系统整理
    JavaScript onload
    百度云如何为用户分配内存空间
    集合运算
    [hdu3530]单调队列
    [hdu4911]逆序对相关
    [hdu5199]统计数据的水题
    [hdu5200]离线+标记
    [hdu5204]水题
    [hdu5203]计数水题
  • 原文地址:https://www.cnblogs.com/cnsec/p/3789796.html
Copyright © 2011-2022 走看看