资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

C++virtualdestructor-创新互联

what happen when a derived class constructor is called?

我们先了解derived class在调用constructor的时候,首先先调用的是其父类的构造函数
why?
首先我们的derived class obj中有所有的父类class的成员,在derived clas obj中其父类成员的可访问程度根据父类成员所在位置所定(子类对象可以访问父类的public和protected成员),所以在我们的子类构造的时候要先构造父类class(主要将其父类成员初始化)

宾县ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为创新互联的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:13518219792(备注:SSL证书合作)期待与您的合作!

没有构造函数编译器会默认分配一个编译器给你,其功能仅仅是初始化成员而已

如下图、

#includeclass Base{public:
    Base(){std::cout<< "constructor of Base "<<'\n'; }
};

class Derived: public Base{public:
    Derived(){std::cout<< "constructor of derived "<< '\n'; }
};

int main(){Derived* d1 = new Derived();
    return 0; 
}

打印如下

constructor of Base 
constructor of derived

当然我们构造子类的时候也可以选择性的调用父类构造函数

#includeclass Base{public:
    Base(){std::cout<< "constructor of Base "<<'\n'; }
    Base(int a) : Base_value(a) {std::cout<< "constructor of base and init base_value and base_value is "<< Base_value<< '\n';}
    int Base_value;
};

class Derived: public Base{public:
    Derived(): Base(){std::cout<< "constructor of derived "<< '\n'; }
    Derived(int a): Base(a){std::cout<< "call second constructor of Base_Value"<< '\n';}
};

int main(){Derived* d1 = new Derived(100);
   // Derived obj;
    return 0; 
}
Class继承中的析构函数

我们上面讲到了子类中其实是有父类的成员,所以在构造子类的时候先调用父类的构造函数构造父类,有构造那么就有析构函数,析构的顺序正好相反,先析构子类,再析构父类如下

#includeclass Base{public:
    Base(){std::cout<< "constructor of Base "<<'\n'; }
    Base(int a) : Base_value(a) {std::cout<< "constructor of base and init base_value and base_value is "<< Base_value<< '\n';}
    ~Base(){std::cout<< "destructor of Base"<<'\n';}
    int Base_value;
};

class Derived: public Base{public:
    Derived(): Base(){std::cout<< "constructor of derived "<< '\n'; }
    Derived(int a): Base(a){std::cout<< "call second constructor of Base_Value"<< '\n';}
    ~Derived(){std::cout<< "destructor of Derived"<< '\n';}
};

int main(){Derived* d1 = new Derived(100);
   // Derived obj;
   std::cout<< "---------------"<< '\n';
   delete d1;
   return 0; 
}

打印如下

constructor of base and init base_value and base_value is 100
call second constructor of Base_Value
---------------
destructor of Derived
destructor of Base

我们再看下面代码在deletederived class的时候只有base的destructor被执行,而derived class的destructor并没有被执行

#includeclass Base{public:
    virtual void fun() {std::cout<< "base fun"<< std::endl; }
    ~Base() {std::cout<< "base destructor"<< std::endl;}
};

class Derive: public Base{public:
    virtual void fun() {std::cout<< "derived fun"<< '\n';}
    ~Derive() {std::cout<< "Derive destructor"<<'\n'; }
};

int main(){Base *b1 = new Base();
    Base *b2 = new Derive();

    b1->fun();
    b2->fun();

    delete b1;
    delete b2;

}

打印如下

base fun
derived fun
base destructor
base destructor

为什么会这样?我们注意我们new完Derived class后将其赋值给Base class指针,此时我们new完Derived class生成的obj1虽然有derived class自有的成员变量,但是obj1归根结底是Base class的obj,所以不可访问Derived Class的额外成员这叫做object slicing
此时又有一个问题诞生,因为我们将子类的对象指针obj1赋值给父类,那么意味着obj1只能使用父类部分的成员,我们直接实例化父类这样实例化后的对象又比obj1占用的空间小,又简单不是挺好吗?为什么还要将子类对象obj1赋值给父类?这里有一个用处,就是父类中的一些成员是virtual,那么子类对象中父类vptr指向的函数为子类override后的地址,如下图

class X {int     x;
    string str;

public:
    X() {}
    virtual ~X() {}

    virtual void printAll() {}
};

class Y : public X {int     y;

public:
    Y() {}
    ~Y() {}

    void printAll() {}
};

Y memory layout

  |                              |          
      |------------------------------|<------ Y class object memory layout
      |          int X::x            |
stack |------------------------------|
  |   |              int string::len |
  |   |string X::str ----------------|
  |   |            char* string::str |         
 \|/  |------------------------------|      |-------|--------------------------|
      |           X::_vptr           |------|       |       type_info Y        |
      |------------------------------|              |--------------------------|
      |          int Y::y            |              |    address of Y::~Y()    |
      |------------------------------|              |--------------------------|
      |               o              |              | address of Y::printAll() |
      |               o              |              |--------------------------|
      |               o              |              
------|------------------------------|--------
      |           X::X()             | 
      |------------------------------|       |   
      |           X::~X()            |       |
      |------------------------------|       | 
      |         X::printAll()        |      \|/ 
      |------------------------------|  text segment
      |           Y::Y()             |
      |------------------------------|
      |           Y::~Y()            |
      |------------------------------|
      |         Y::printAll()        |
      |------------------------------|
      |      string::string()        |
      |------------------------------|
      |      string::~string()       |
      |------------------------------|
      |      string::length()        |
      |------------------------------|
      |               o              |
      |               o              |
      |               o              |
      |                              |

上图中Y继承了X,那么Y对象中应该含有X的vptr,上图中Y的确含有X的vptr,但是X的vptr不再指向X的函数而是指向class Y override后的函数
所以我们在析构函数中加上virtual关键字后子类对象memory layout中对应的父类vptr指针(子类没有定义virtual函数,所以子类memory layout中没有关于子类的vptr指针,如果子类对象obj1赋值给父类,那么obj1就会使用其内存中x的vptr指向的虚函数)指向的析构函数变成了子类override的那一个(父类对象memory layout中vptr还是父类virtual function的地址),相反如果我们的子类中没有对父类的虚函数重载定义,那么子类中的vptr指向的是父类函数的地址,然后编译器执行的时候会先在vptr中找对应的函数地址,因为我们子类没有重新定义父类虚函数,那么vptr中指向的还是父类对应的虚函数,则最后编译器执行的是父类的函数而不是子类的函数.

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享文章:C++virtualdestructor-创新互联
文章网址:http://cdkjz.cn/article/dohepo.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

业务热线:400-028-6601 / 大客户专线   成都:13518219792   座机:028-86922220