zoukankan      html  css  js  c++  java
  • 编译器在构造函数里都做了些什么?

      我们都知道,C++是一种面向对象的语言,其中一个重要特性是多态性。多态性是通过基类指针指向子类对象,并通过这个基类指针调用子类函数(虚函数)来实现的。但是,看下面这个例子,我可以通过在构造函数里增加一行代码,从而使得这个多态不起作用!

      看下面例子:

    class Base
    {
    public:
        Base()
        {
            cout<<"Base::Base()"<<endl;
        }
    
        virtual ~Base()
        {
            cout<<"Base::~Base()"<<endl;
        }
    
        virtual void print()
        {
            cout<<"Base::print()"<<endl;
        }
    };
    
    
    class Derived: public Base
    {
    public:
        Derived()
        {
            //memcpy(this, 0, sizeof(Derived));
            cout<<"Derived::Derived()"<<endl;
            memset(this, 0, sizeof(Derived));
        }
    
        ~Derived()
        {
            cout<<"Derived::~Derived()"<<endl;
        }
    
        void print()
        {
            cout<<"Derived::print()"<<endl;
        }
    };
    
    
    
    int main()
    {
        Base *b = new Derived();
        b->print();
        return 0;
    }

      如果按照C++的多态特性,它应该输出:Derived::print(),但是真是如此吗?

      下面,我们编译,运行,看看输出什么东西:

    $ ./nopoly 
    Base::Base()
    Derived::Derived()
    Segmentation fault (core dumped)

      令人大跌眼镜,居然段错误!什么也没能输出,是什么原因导致的呢?其实是因为这么一行代码:

    memset(this, 0, sizeof(Derived));

      我们将vpt清为零了,因此,找不到Deried类的print函数,因此,便出现段错误了。

      那么,我们回想一下,编译器到底在我们的构造函数里都干了些什么。

    1. 记录在 member initialization list 中的 data members 初始化操作会被放进构造函数本身,并以members声明的顺序进行初始化;

    2. 如果有一个member并没有出现在member initialization list 中,但它有一个default constructor,那么,该default constructor 必须被调用;

    3. 在那之前,如果class object有virtual table pointer(s),它(们)必须被设定初值,并指向适当的virtual table(s)。

    4. 在那之前,所有上一层的 base class constructors 必须被调用,并以base class 的声明顺序为次序(与member initialization list 中的顺序没有关联):

      如果base class 被列于member initialization list中,那么任何明确指定的参数都应该传递过去;

      如果base class 没有被列于 member initialization list 中,而它有default constructor (或default memberwise copy constructor ),那么就调用之。

      如果base class 是多重继承下的第二或后继的base class ,那么this 指针必须有所调整。

    5. 在那之前,所有 virtual base class constructors 必须被调用,从左到右,从最深到最浅:

      如果class 被列于member initialization list 中,那么如果有任何明确指定的参数,都应该传递过去。若没有列于list 中,而class 有一个default constructor,也应该调用之。

      此外,class 中的每一个virtual base class subobejct 的偏移量必须在执行期可被存取;

      如果class object 是最底层的 class,其constructors 可能被调用;某些用以支持这个行为的机制必须被放进来。

  • 相关阅读:
    airtest-selenium
    window下使用Redis Cluster部署Redis集群
    调用webservice进行身份验证
    ETL数据从sqlserver到mysql之间迁移
    Sqlserver调用api
    EXCEL导入数据到SQLSERVER
    博客园开通的第一天
    Visual Studio 2017 离线安装包
    WPF学习笔记1---初接触
    Visual Studio 2008 + ObjectARX2012环境配置
  • 原文地址:https://www.cnblogs.com/wiessharling/p/4168733.html
Copyright © 2011-2022 走看看