判断题:对于派生类的构造函数构造函数对基类构造函数的调用顺序就是基类构造函数的执行顺序.

关于派生类构造函数调用基类构造函数的问题
[问题点数:40分,结帖人usa_5678]
关于派生类构造函数调用基类构造函数的问题
[问题点数:40分,结帖人usa_5678]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2013年12月 C/C++大版内专家分月排行榜第二2013年12月 Linux/Unix社区大版内专家分月排行榜第二2013年11月 C/C++大版内专家分月排行榜第二2013年10月 C/C++大版内专家分月排行榜第二
2016年2月 C/C++大版内专家分月排行榜第三2016年1月 C/C++大版内专家分月排行榜第三
2016年2月 C/C++大版内专家分月排行榜第三2016年1月 C/C++大版内专家分月排行榜第三
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。&&/&&&&/&&&&/&&&&/&&
基类的构造函数不能被继承,在声明派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数来完成。所以在设计派生类的构造函数时,不仅要考虑派生类新增的成员变量,还要考虑基类的成员变量,要让它们都被初始化。
解决这个问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。
下面的例子展示了如何在派生类的构造函数中调用基类的构造函数。
#include&iostream&
class People{
protected:
People(char*, int);
People::People(char *name, int age): name(name), age(age){}
class Student: public People{
Student(char*, int, float);
void display();
//调用了基类的构造函数
Student::Student(char *name, int age, float score): People(name, age){
this-&score =
void Student::display(){
cout&&name&&&的年龄是&&&age&&&,成绩是&&&score&&
int main(){
Student stu(&小明&, 16, 90.5);
stu.display();
运行结果为:
小明的年龄是16,成绩是90.5
请注意代码第23行:
这是派生类 Student 的构造函数的写法。冒号前面是派生类构造函数的头部,这和我们以前介绍的构造函数的形式一样,但它的形参列表包括了初始化基类和派生类的成员变量所需的数据;冒号后面是对基类构造函数的调用,这和普通构造函数的参数初始化表非常类似。
实际上,你可以将对基类构造函数的调用和参数初始化表放在一起,如下所示:
基类构造函数和初始化表用逗号隔开。
需要注意的是:冒号后面是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数总参数表中的参数,还可以是局部变量、常量等。如下所示:
基类构造函数调用规则
事实上,通过派生类创建对象时必须要调用基类的构造函数,这是语法规定。也就是说,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数(不带参数的构造函数);如果没有默认构造函数,那么编译失败。
请看下面的例子:
#include&iostream&
class People{
protected:
People(char*, int);
People::People(){
this-&name = &xxx&;
this-&age = 0;
People::People(char *name, int age): name(name), age(age){}
class Student: public People{
Student();
Student(char*, int, float);
void display();
Student::Student(){
this-&score = 0.0;
Student::Student(char *name, int age, float score): People(name, age){
this-&score =
void Student::display(){
cout&&name&&&的年龄是&&&age&&&,成绩是&&&score&&
int main(){
Student stu1;
stu1.display();
Student stu2(&小明&, 16, 90.5);
stu2.display();
运行结果:
xxx的年龄是0,成绩是0
小明的年龄是16,成绩是90.5
创建对象 stu1 时,执行派生类的构造函数 Student::Student(),它并没有指明要调用基类的哪一个构造函数,从运行结果可以很明显地看出来,系统默认调用了不带参数的构造函数,也就是 People::People()。
创建对象 stu2 时,执行派生类的构造函数&Student::Student(char *name, int age, float score),它指明了基类的构造函数。
在第31行代码中,如果将 People(name, age) 去掉,也会调用默认构造函数,stu2.display() 的输出结果将变为:
xxx的年龄是0,成绩是90.5
如果将基类 People 中不带参数的构造函数删除,那么会发生编译错误,因为创建对象 stu1 时没有调用基类构造函数。
总结:如果基类有默认构造函数,那么在派生类构造函数中可以不指明,系统会默认调用;如果没有,那么必须要指明,否则系统不知道如何调用基类的构造函数。
构造函数的调用顺序
为了搞清这个问题,我们不妨先来看一个例子:
#include&iostream&
class People{
protected:
People(char*, int);
People::People(): name(&xxx&), age(0){
cout&&&PeoPle::People()&&&
People::People(char *name, int age): name(name), age(age){
cout&&&PeoPle::People(char *, int)&&&
class Student: public People{
Student();
Student(char*, int, float);
Student::Student(): score(0.0){
cout&&&Student::Student()&&&
Student::Student(char *name, int age, float score): People(name, age), score(score){
cout&&&Student::Student(char*, int, float)&&&
int main(){
Student stu1;
cout&&&--------------------&&&
Student stu2(&小明&, 16, 90.5);
运行结果:
PeoPle::People()
Student::Student()
--------------------
PeoPle::People(char *, int)
Student::Student(char*, int, float)
从运行结果可以清楚地看到,当创建派生类对象时,先调用基类构造函数,再调用派生类构造函数。如果继承关系有好几层的话,例如:
那么则创建C类对象时,构造函数的执行顺序为:
构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的。& 鸡啄米:C++编程入门系列之四十二(继承与派生:虚基类及其派生类的构造函数)
鸡啄米:C++编程入门系列之四十二(继承与派生:虚基类及其派生类的构造函数)
&&&&&& 鸡啄米在编程入门系列的上一讲中讲了。今天主要讲解虚基类及其派生类的构造函数。&&&&&& 1.虚基类的概念及用法&&&&&&&上一讲中说过,如果派生类的全部或者部分基类有共同的基类,那么派生类的这些直接基类从上一级基类继承的成员都具有相同的名称,定义了派生类的对象后,同名数据成员就会在内存中有多份拷贝,同名函数也会有多个映射。访问这些同名成员时,为了唯一标识它们可以使用上一讲中的作用域分辨符,也可以使用虚基类技术。我们将派生类直接基类的共同基类声明为虚基类后,派生类从不同的直接基类继承来的同名数据成员在内存中就会只有一份拷贝,同名函数也会只有一个映射,这样不仅实现了唯一标识同名成员,而且也节省了内存空间,可见虚基类技术是很实用的。&&&&&& 在派生类声明时除外还使用关键字virtual限定基类的话,此基类就是虚基类。虚基类声明的语法形式为:&&&&&& class 派生类名:virtual 继承方式 基类名&&&&&&&这里关键字virtual跟继承方式一样,只限定紧跟在它后面的基类。比如,声明了类A为虚基类,类B为A的派生类,类C也是A的派生类,类D是由类B和C共同继承而来,则类B和类C从A继承的同名数据成员在类D的对象中只有一份拷贝,同名函数成员也只有一个函数体。&&&&&& 鸡啄米讲上一讲中的第二个例子做下修改,将Base0声明为虚基类来说明下虚基类的用法:我们先声明一个基类Base0,Base0中有数据成员x和函数成员show,再声明类Base1和Base2,它们都由Base0公有继承而来,与上一讲中不同的是派生时声明Base0为虚基类,最后从Base1和Base2共同派生出类Child。这时Base0的成员经过到Base1和Base2再到Child的两次派生过程,出现在Child类中时,数据成员x在内存中也只有一份拷贝,函数成员show也只有一个映射。&&&&&&&&& #include &iostream&&&&&&&&&&&&&&& class Base0&&&&&&&&&&&&&&&&&&& // 基类Base0的声明&&&&&&& {&&&&&&& public:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& void show()&&&&& { cout&&&x of Base0: &&&x&& }&&&&&&& };&&&&&&& class Base1 : virtual public Base0&&&& // Base0为虚基类,公有派生Base1类&&&&&&& {&&&&&&& };&&&&&&& class Base2 : virtual public Base0&&&& // Base0为虚基类,公有派生Base2类&&&&&&& {&&&&&&& };&&&&&&& class Child : public Base1, public Base2&&&&&&& {&&&&&&& };&&&&&&& int main()&&&&&&& {&&&&&&&&&&&&&&& C&&&&&&&&&&&&&&& child.x = 5;&&&&&&&&&&&&&&& child.show();&&&&&&&&&&&&&&& return 0;&&&&&&& }&&&& &&程序运行结果为:&&& &&& x of Base0: 5&&&&& &大家可以看到,声明虚基类只需要在它的派生类声明时使用关键字virtual修饰。&&&&&&&我们对作用域分辨符和虚基类技术进行对比分析可知,使用作用域分辨符唯一标识同名成员时,派生类中有同名成员的多个拷贝,可以存放不同的数据,进行不同的操作,而使用虚基类时派生类的同名成员只有一份拷贝,更节省内存。我们在软件开发中可以根据实际情况自己做出选择。&&&&&& 2.虚基类&&&&&& 上面例子中各个类都没有定义,而是使用的默认构造函数。如果虚基类定义了带参数表的非默认构造函数,没有定义默认形式的构造函数,那么情况会有些复杂。因为由虚基类直接或间接继承的所有派生类,都必须在构造函数的成员初始化列表中给出对虚基类成员的初始化。鸡啄米这里再讲上面的例子做进一步修改,为虚基类添加带参数表的构造函数,那么整个程序就要改成以下形式:&&&&&&& #include &iostream&&&&&&&&&&&&&&& class Base0&&&&&&&&&&&&&&&&&&& // 基类Base0的声明&&&&&&& {&&&&&&& public:&&&&&&&&&&&&&&&&& Base0(int y)&&&& { x=y; }&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& void show()&&&&& { cout&&&x of Base0: &&&x&& }&&&&&&& };&&&&&&& class Base1 : virtual public Base0&&&& // Base0为虚基类,公有派生Base1类&&&&&&& {&&&&&&& public:&&&&&&&&&&&&&&&&& Base1(int y):Base0(y)&&& { }&&&&&&& };&&&&&&& class Base2 : virtual public Base0&&&& // Base0为虚基类,公有派生Base2类&&&&&&& {&&&&&&& public:&&&&&&&&&&&&&&&&& Base2(int y):Base0(y)&&& { }& &&&&&&& };&&&&&&& class Child : public Base1, public Base2&&&&&&& {&&&&&&& public:&&&&&&&&&&&&&&&&& Child(int y):Base0(y),Base1(y),Base2(y)&& { }&&&&&&& };&&&&&&& int main()&&&&&&& {&&&&&&&&&&&&&&& Child child(3);&&&&&&&&&&&&&&& child.show();&&&&&&&&&&&&&&& return 0;&&&&&&& }&&&&& &程序运行结果为:&&&&&& &x of Base0: 3&&&&&&&主函数中定义了派生类Child的对象child,在构造对象child时调用了child的构造函数,其初始化列表中不只调用了虚基类Base0的构造函数对从它继承的成员x进行初始化,而且还调用了基类Base1和Base2的构造函数Base1()和Base2(),而Base1()和Base2()的初始化列表中又有对虚基类Base0成员x的初始化。这么说,从虚基类Base0继承来的成员x初始化了三次,其实不然,因为编译器在遇到这种情况时会进行特殊处理:如果构造的对象中有从虚基类继承来的成员,那么虚基类成员的初始化由而且只由最远派生类的构造函数调用虚基类的构造函数来完成。最远派生类就是声明对象时指定的类,上面例子中构造对象child时,类Child就是最远派生类。除了最远派生类,它的其他基类对虚基类构造函数的调用会被忽略。上例中就只会由Child类的构造函数调用虚基类Base0的构造函数完成成员x的初始化,而Child类的基类Base1和Base2对虚基类Base0构造函数的调用会被忽略。&&&&& 鸡啄米对虚基类及其构造函数的内容就讲到这里了。有任何问题欢迎到鸡啄米博客留言讨论。&
除非特别注明,文章均为原创
转载请标明本文地址:
作者:鸡啄米
&&( 22:22:20)&&( 21:39:22)&&( 23:24:41)&&( 23:12:31)&&( 22:38:42)&&( 22:26:24)&&( 22:38:40)&&( 22:12:1)&&( 22:23:44)&&( 22:10:22)
C++是面向对象的基础
很专业。。
非常的重要呀,
落伍了,看不懂了哦鸡啄米 于
21:17:18 回复只是每个人擅长的东西不一样。
专业性很强!学到了不少东西!鸡啄米 于
21:17:45 回复谢谢
看着这个头疼呢鸡啄米 于
21:18:12 回复不做这个可能会觉得有点枯燥,呵呵
博主真是专业人物呀 鸡啄米 于
21:18:38 回复你写的东西也很专业。
先占位支持了,谢谢分享顶起
曾經的夢想,已經遠離了我,現在的我又有夢想了。鸡啄米 于
22:25:57 回复只要有梦想就好
过来学习一下子了啊
过来看一下学习了啊鸡啄米 于
22:53:06 回复欢迎常来啊
尽管都还给老师了不过还是觉得很强大深奥的学问鸡啄米 于
22:54:00 回复呵呵,不做这个的总是觉得很深奥。
业性很强!学到了不少东西!
22:54:55 回复一直做这个,所以有些经验。
我过来支持博主了鸡啄米 于
20:10:40 回复谢谢支持
这个太专业,看不懂
一年的最后一天,看看博主在奋斗什么!鸡啄米 于
20:12:01 回复上班了,呵呵
完全随机文章共有 4652 人关注过本帖
标题:C++中派生类能继承基类的构造函数和析构函数吗?
等 级:论坛游民
帖 子:104
专家分:65
结帖率:31.58%
&&问题点数:0&&回复次数:13&&&
C++中派生类能继承基类的构造函数和析构函数吗?
如题!能否举例体现一下?
搜索更多相关主题的帖子:
等 级:业余侠客
帖 子:85
专家分:248
#include &iostream&
class Person
&&& Person()
&&&&&&&&cout && &Person's constructor is invoked& &&
&&& ~Person()
&&&&&&&&cout && &Person's destructor is invoked& &&
class Employee:public Person
&&& Employee()
&&&&&&&&cout && &Employee's constructor is invoked& &&
&&& ~Employee()
&&&&&&&&cout && &Employee's destructor is invoked& &&
int main()
&&& return 0;
等 级:ID已被封
威 望:30
帖 子:2976
专家分:7697
木有这种说法。
基类是基类的,派生类是派生类的。
你可以去读读《深入C++对象模型》一书。
我们都在路上。。。。。
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
3楼正解,2楼纯属乱讲,之所以会打印出基类构造函数里的字符串,是因为子类构造函数会调用基类的构造函数。
[ 本帖最后由 lz 于
21:24 编辑 ]
My life is brilliant
等 级:业余侠客
帖 子:85
专家分:248
回复 4楼 lz
嗯,受教了。。
来 自:北京
等 级:贵宾
威 望:94
帖 子:6779
专家分:16751
确实是很容易令人混乱的概念。感觉楼主问的好多问题都很犀利。
等 级:业余侠客
帖 子:85
专家分:248
说明LZ看书很认真啊,我就没发现这个问题。
等 级:业余侠客
帖 子:86
专家分:232
构造函数和析构函数不能被继承。
等 级:新手上路
不能,多重继承派生类的构造函数只是在初始化表中包含多个基类构造函数,析构函数更加不能继承
等 级:论坛游民
帖 子:19
专家分:18
当然不能被继承,上课的时候老师还特意强调了!
版权所有,并保留所有权利。
Powered by , Processed in 0.029542 second(s), 8 queries.
Copyright&, BCCN.NET, All Rights ReservedC++虚基类构造函数详解(调用顺序)之一 - 推酷
C++虚基类构造函数详解(调用顺序)之一
解释某个函数,我通常的讲解不会先去长篇大论去空谈,先整个例子来看看!!走起....
#include &iostream&
#include &string&
using namespace
A(const char*s)
class B:virtual public A
B(const char*s1,const char*s2):A(s1)
cout &&s2&&
class C:virtual public A
C(const char*s1,const char*s2):A(s1)
cout&&s2&&
class D:public B,C
D(const char *s1,const char *s2,const char*s3,const char*s4):B(s1,s2),C(s1,s3),A(s1)
cout &&s4&&
int main(int argc, char* argv[])
D *ptr = new D(&class A&,&class B&,&class C&,&class D&);
ptr = NULL;
先不要忙着去执行代码!!
来看几个基本概念:
一、虚基类的作用:
当一个类的部分或者全部基类来自另一个共同的基类时,这些直接基类中从上一级共同基类继承来的 &就拥有相同的名称。在派生类的对象中,这些同名数据成员在内存中同时拥有多个拷贝,同一个函数名会有多个映射。我们可以使用作用域分蝙蝠来唯一标识并分别访问他们,
也可以将共同基类设置为虚基类,这时从不同的路径继承过来的同名数据成员在内存中就只用一个拷贝,同一个函数名也只有一个映射。
二、虚基类的声明 &语法形式:
class 派生类名:virtual &继承方式 &基类名
三、使用虚基类时应该注意:
1&一个类可以在一个类族中用作虚基类,也可以用作非虚基类。
2&在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的对象。
3&虚基类子对象是由最派生类(最后派生出来的类)的构造函数通过调用虚基类的构造函数进行初始化。
4&最派生类是指在继承类结构中建立对象时所指定的类。
5&在派生类的构造函数的成员初始化列表中,必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数。
6&在虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中,都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。
7&在一个成员初始化列表中,同时出现对虚基类和非虚基类构造函数的调用时,基类的构造函数先于非虚基类的构造函数执行。
8&虚基类并不是在声明基类时声明的,而是在声明派生类是,指定继承方式时声明的。因为一个基类可以在生成一个派生类作为虚基类,而在生成另一个派生类时不作为虚基类。
温馨提示:使用多重继承时要十分小心,经常会出现二义性。许多专业人员认为:不要提倡在程序中使用多重继承,只有在比较简单和不易出现二义性的情况或是在必要时才使用多重继承,能用单一继承解决的问题就不要使用多重继承,也是由于这个原因,有些面向对象的程序设计语言,并不支持多重继承。
现在对虚基类构造函数了解了没??如果还不了解那么咱们就继续深入研究.....
首先,要知道虚拟继承与普通继承的区别:
假设derived继承自base类,那么derived与base是一种“is a”的关系,即derived类是base类,而反之错误;
假设derived虚继承自base类,那么derived与base是一种“has a”的关系,即derived类有一个指向base类的vptr。
因此虚继承可以认为不是一种继承关系,而可以认为是一种组合的关系。因为虚继承有着“继承”两个关键字,那么大部分人都认为虚继承与普通继承的用法没有什么太大的不同,由此用在继承体系中,这种将虚继承认为是普通继承的危害更加大!先用一个例子来说明问题:
#include &iostream&
using namespace
class base
cout &&&base::base()!&&&
void printBase()
cout&&&base::printBase()!&&&
class derived:public base
cout&&&derived::derived()!&&&
void printDerived()
cout&&&derived::printDerived()!&&&
int main(int argc, char* argv[])
base oo1(static_cast&base&(oo));
oo1.printBase();
cout &&&---------------------&&&
derived oo2= static_cast&derived&&(oo1);
oo2.printDerived();
运行结果:
对前面的例子稍加修改......................
#include &iostream&
using namespace
class base1
cout&&&base::base()!&&&
void printBase()
cout&&&base::printBase()!&&&
class derived1:virtual public base1
derived1()
cout&&&derived::derived()!&&&
void printDerived()
cout &&&derived::printDerived()!&&&
int main(int argc, char* argv[])
base1 oo1(static_cast&base1&(oo));
oo1.printBase();
derived1 oo2 = static_cast&derived1&&(oo1);
oo2.printDerived();
会发现编译错误:error C2635: cannot convert a 'base1*' to a 'derived1*'; conversion from a virtual base class is implied(代码中红色部分出错)
可以看到不能将基类通过static_cast转换为继承类。我们知道c++提供的强制转换函数static_cast对于继承体系中的类对象的转换一般是可行的。那么这里为什么不可以呢??
virtual base class的原始模型是在class object中为每一个有关联的virtual base class加上一个指针vptr,该指针指向virtual基类表。有的编译器是在继承类已存在的virtual table直接扩充导入一个virtual base class table。不管怎么样由于虚继承已完全破坏了继承体系,不能按照平常的继承体系来进行类型转换。
我们清楚了虚基类构造函数是怎么回事,那么接下来讲解一下
虚基类构造函数调用顺序
我们下来了解虚拟继承中遇到最广泛的菱形结构:
#include &iostream&
using namespace
class stream
cout &&&stream::stream()!&&&
class iistream:virtual stream
iistream()
cout &&&istream::istream()!&&&
class oostream:virtual stream
oostream()
cout &&&ostream::ostream()!&&&
class iiostream:public iistream,oostream
iiostream()
cout&&&iiostream::iiostream()!&&&
int main(int argc, char* argv[])
运行结果:
本来虚拟继承的目的就是当多重继承出现重复的基类时,其只保存一份基类,减少内存开销。
这样子的菱形结构,使公共基类只产生一个拷贝。
从基类stream派生新类时,使用virtual将类stream说明为虚基类,这时派生类istream、ostream包含一个指向虚基类的vptr,而不会产生实际的stream空间。所以最终iiostream也含有一个指向虚基类的vptr,调用stream中的成员方法时,通过vptr去调用,不会产生二义性!
现在我们换种方式使用虚继承:
#include &iostream&
using namespace
class stream
cout &&&stream::stream()!&&&
class iistream:public stream
iistream()
cout &&&istream::istream()!&&&
class oostream:public stream
oostream()
cout &&&ostream::ostream()!&&&
class iiostream:virtual iistream,oostream
iiostream()
cout&&&iiostream::iiostream()!&&&
int main(int argc, char* argv[])
运行结果:
从结果上可以看到,其构造过程中重复出现基类的stream的构造过程,这样就完全没有达到虚拟继承的目的。其继承结构为:
从继承结构可以看出,如果iiostream对象调用基类stream重的成员方法,会导致方法的二义性。因为iiostream含有指向其虚继承基类istream,ostream的vptr。而istream,ostream包含了stream的空间,所以导致iiostream不知道导致时调用那个stream的方法。要解决该问题,即在调用成员方法时需要加上作用域!
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致}

我要回帖

更多关于 基类 派生类 构造函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信