【问题标题】:Does pointer to derived class create the Base class first or not?指向派生类的指针是否首先创建基类?
【发布时间】:2015-08-26 10:36:20
【问题描述】:

我有以下代码

#include <iostream>
using namespace std;

class B{
    int i;
    public:
    B(){
        cout << "Constructing B\n";
    }
    void print(){
        cout << "Printing from B with size : "<<sizeof(*this)<<endl;
    }
};



class D:public B{
    int i;
    public:
    D(){
        cout << "Constructing D\n";
    }
    void print(){
        cout << "Printing from D with size : "<<sizeof(*this)<<endl;
    }
};


int main(){

    B b;
    b.print();
    D d;
    d.print();

    D* dp;
    dp->print();
}

这给了我以下输出:

Constructing B
Printing from B with size : 4
Constructing B
Constructing D
Printing from D with size : 8
Printing from D with size : 8

那么,当您创建指向派生类的指针时,它不会首先创建基类的实例,这是真的吗?我不认为这是真的,因为 D 类的大小就是证明。但它甚至没有调用基类构造函数。谁能解释一下?

【问题讨论】:

  • 这里,D* dp; dp-&gt;print(); 是未定义的行为。它可以打印任何内容,也可以不打印任何内容。
  • 当您“创建指向派生类的指针”时,它不会创建任何任何对象(不包括指针本身)。
  • 那么 dp 指向的内存大小是 8。这就是我想知道的。
  • 相关问题:为什么不格式化你的硬盘?
  • @davidhigh 哈哈,虽然我知道指针的力量.. 它们非常棒。

标签: c++ pointers inheritance derived-class base-class


【解决方案1】:

指针不会创建任何东西。指针只是指针——包含地址的标量对象。让指针指向内存中的正确位置是您的责任。

在您的示例中,指针 dp 未初始化。你从来没有让它指向任何地方,所以它没有指向任何地方。您尝试调用 dp-&gt;print() 会产生未定义的行为。故事结束。

【讨论】:

    【解决方案2】:

    现在你的指针根本没有被初始化,所以尝试使用它会产生未定义的行为。尝试类似:

    D *dp = new D; 
    dp->print();
    delete dp;
    

    或者为了做得更好,比如:

    std::unique_ptr<D> dp = std::make_unique<D>();
    dp->print();
    

    ...unique_ptr 超出范围时会自动删除D

    但是请注意,您已将 print 定义为非虚拟函数,因此调用的函数将取决于所使用的指针(或引用)的类型,而不是类型它所指的对象。此外,您还没有定义虚拟 dtor。

    因此,如果您要执行以下操作:

    B *p = std::make_unique<D>();
    p->print(); // would invoke B::print, even though the object is a D
    

    ...当它超出范围时,它会被错误地销毁,所以你会得到未定义的行为。要更正此问题,您需要将 B 更改为以下内容:

    class B{
        int i;
        public:
        B(){
            cout << "Constructing B\n";
        }
        virtual void print(){
            cout << "Printing from B with size : "<<sizeof(*this)<<endl;
        }
        virtual ~B() = default;
    };
    

    当您这样做时,答案是“是”——当您创建派生对象时,它将首先调用基类的 ctor,然后调用派生类的 ctor。当派生对象被销毁时,情况正好相反:首先调用派生类的 dtor,然后在它完成时执行基类的 dtor。

    【讨论】:

      【解决方案3】:

      您可以使用强大的多态性Derived 对象分配Base 的指针。这是可能的,因为Derived 实现了Base 包含的所有内容。因此,Derived 底层在其自己的实例化过程中实例化了Base 是隐含的。

      class Base
      {
      public:
          Base()
          {}
      }
      
      class Derived : public Base
      {
      public:
          Derived()
          {}
      }
      
      Derived *derived = new Derived();
      Base *base = derived;
      

      【讨论】:

        【解决方案4】:

        它在派生类之前构造基类。您的输出反映了这一点。看看这个

        //Constructs Base class
        Constructing B
        //Constructs Derived class
        Constructing D
        //Prints from derived
        Printing from D with size : 8
        

        您的代码在没有调用构造函数的情况下打印两次Printing from D with size : 8 的原因是您的代码永远不会创建D 的第二个实例。

        D* d;
        ^^^^ Declares a pointer to a D, Does not create a D!
        

        当您调用d-&gt;print(); 时,这是未定义的行为,因为d 不指向D 的实例。最后,您的代码会打印在编译时确定的值(sizeof(D) 是编译时值)并且不会触及您的代码运行的 this 指针。

        请参阅sizeof here 的文档。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-16
          • 2022-01-03
          • 1970-01-01
          • 2016-02-19
          • 1970-01-01
          • 2017-07-18
          • 2020-02-11
          • 1970-01-01
          相关资源
          最近更新 更多