C++ 派生类构造函数的调用顺序的一道题目
发布网友
发布时间:2023-04-12 01:13
我来回答
共5个回答
热心网友
时间:2023-09-29 03:47
答案:
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数
类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数
1,3,2
4,6,5
类B的析构函数
类A的析构函数
类A的析构函数
类B的析构函数
类A的析构函数
类A的析构函数
首先:“类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数”
来自B b1[2]; 的定义
“类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数"
是源自b1[0]=B(1,2,3); 的初始化,注意析构顺序与构造顺序相反。
同理b1[1]=B(4,5,6); 产生接下来的六行
最后输出out产生余下的输出
构造函数有两种形式:一种是由编译器自动生成的缺省构造函数;另一种是用户自定义构造函数。缺省构造函数用户不必定义函数体,编译器自动为对象进行初始化,即为每个数据成员赋值零或空值。而用户自定义构造函数的函数体需要用户自己定义,因此用户自定义构造函数不但可以初始化类中的数据成员,而且还可以设计一个程序段在对象创建时被执,如对象创建提示语句。
关于构造函数调用顺序的说明
我们来看下面一段代码:
class B1
{
public:
B1(int i) {cout<<"constructing B1 "<<i<<endl;}
};
class B2
{
public:
B2(int j) {cout<<"constructing B2 "<<j<<endl;}
};
class B3
{
public:
B3( ){cout<<"constructing B3 *"<<endl;}
};
class C: public B2, public B1, public B3
{
public:
C(int a, int b, int c, int d):B1(a),memberB2(d),memberB1(c),B2(b){}
private:
B1 memberB1;
B2 memberB2;
B3 memberB3;
};
void main( )
{ C obj(1,2,3,4); }
运行后的结果如下:
constructing B2 2
constructing B1 1
constructing B3 *
constructing B1 3
constructing B2 4
constructing B3 *
为什么会有以上的结果?
众所周知构造函数的执行次序如下:
调用基类构造函数,调用顺序按照他们的继承时声明的顺序。
调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序。
派生类的构造函数体中的内容。
析构函数的调用顺序相反。
那么再来看以上的例子就很容易理解了。B2、B1、B3是C的基类,按照上述的顺序,我们先要构造基类,然后才是子对象,最后是其本身的构造函数所以先要执行这三个类的构造函数。在构造时按照他们在类中的顺序,首先调用B2的构造函数
B2(int j) {cout<<"constructing B2 "<<j<<endl;}
由于在默认参数列表
C(int a, int b, int c, int d):B1(a),memberB2(d),memberB1(c),B2(b){}
中,将b的值传给了B2的构造函数,b为2,故打印出:
constructing B2 2
接下来要构造的是B1了。显然在C的默认参数构造列表中将a的值传给了B1,
所以打印出:
constructing B1 1
B3在构造时没有传递参数,调用B3( ){cout<<"constructing B3 *"<<endl;}
打印出:
cout<<"constructing B3 *
这时基类的构造函数已经执行完毕,接着该处理内嵌成员对象的构造函数了。
我们看到C类有三个对象:B1 memberB1;B2 memberB2;B3 memberB3;,按照构造函数的调用顺序,我们需要按照他们在类中声明的顺序来分别构造memberB1、memberB2、 memberB3。在默认的参数列表中,用c来构造了memberB1,用d来构造memberB2,
故打印出:
constructing B1 3
constructing B2 4
constructing B3 *
最后调用本身的构造函数,由于函数体为空,故什么也没有打印出来。
总结下来,我们必须明确的是当一个类继承与基类,并且自身还包含有其他类的成员对象的时候,构造函数的调用顺序为:调用基类的构造函数->调用成员对象的构造函数->调用自身的构造函数。构造函数的调用次序完全不受构造函数初始化列表的表达式中的次序影响,与基类的声明次数和成员对象在函数中的声明次序有关。
再如:
#include<iostream.h>
class A
{
protected:
char c;
public:
A(char ch)
{
c=ch;
cout<<"c="<<c<<endl;
cout<<"类A构造函数被调用"<<endl;
}
~A()
{
cout<<"类A析构函数被调用"<<endl;
}
};
class B
{
protected:
int i;
public:
B(int j)
{
i=j;
cout<<"i="<<i<<endl;
cout<<"类B构造函数被调用"<<endl;
}
~B()
{
cout<<"类B析构函数被调用"<<endl;
}
};
class C:public A,B
{
private:
int k;
public:
C(char ch,int ii,int kk):A(ch),B(ii),k(kk)
{
cout<<"k="<<k<<endl;
cout<<"类C构造函数被调用"<<endl;
}
~C()
{
cout<<"类C析构函数被调用"<<endl;
}
};
void main()
{
C A('B',10,15);
}
输出
c=B
类A构造函数被调用
i=10
类B构造函数被调用
k=15
类C构造函数被调用
类C析构函数被调用
类B析构函数被调用
类A析构函数被调用
热心网友
时间:2023-09-29 03:47
#include<iostream.h>
class A
{
public:
A()
{
a=0;
cout<<"类A的默认构造函数"<<endl;
}
A(int i)
{
a=i;
cout<<"类A的构造函数"<<endl;
}
~A()
{
cout<<"类A的析构函数"<<endl;
}
void out() const
{
cout<<a<<",";
}
int geta()
{
return a;
}
private:
int a;
};
class B:public A
{
public:
B()
{
b=0;
cout<<"类B的默认构造函数"<<endl;
}
B(int i,int j,int k);
~B()
{
cout<<"类B的析构函数"<<endl;
}
void out();
private:
int b;
A a1; //成员数据为A的对象,需要用初始化列表
};
B::B(int i,int j,int k):A(i),a1(j) //成员数据为A的对象,需要用初始化列表,此处就调用一次A的默认构造函数
{
b=k;
cout<<"类B的析构函数"<<endl; //最好改为cout<<"类B的构造函数"<<endl;,容易理解
}
void B::out()
{
A::out();
cout<<b<<","<<a1.geta()<<endl;
}
void main()
{
B b1[2]; //等同于B b1[2] = {B(),B()},调用默认的构造函数,基类A优先调用,后B
b1[0]=B(1,2,3); //赋值操作,先新建B(1,2,3),后销毁已给b1的数据,清除内存,再重新赋值。即B(1,2,3)就如同上步的操作(调用指定的构造函数),b1调用默认析构函数(派生类B优先,后基类A)
b1[1]=B(4,5,6); //同上
for(int i=0;i<2;i++)
b1[i].out();
}
具体答案如下:
热心网友
时间:2023-09-29 03:48
答案:
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数
类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数
1,3,2
4,6,5
类B的析构函数
类A的析构函数
类A的析构函数
类B的析构函数
类A的析构函数
类A的析构函数
首先:“类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数”
来自Bb1[2];的定义
“类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数"
是源自b1[0]=B(1,2,3);的初始化,注意析构顺序与构造顺序相反。
同理b1[1]=B(4,5,6);产生接下来的六行
最后输出out产生余下的输出
构造函数有两种形式:一种是由编译器自动生成的缺省构造函数;另一种是用户自定义构造函数。缺省构造函数用户不必定义函数体,编译器自动为对象进行初始化,即为每个数据成员赋值零或空值。而用户自定义构造函数的函数体需要用户自己定义,因此用户自定义构造函数不但可以初始化类中的数据成员,而且还可以设计一个程序段在对象创建时被执,如对象创建提示语句。
关于构造函数调用顺序的说明
我们来看下面一段代码:
classB1
{
public:
B1(inti){cout<<"constructingB1"<<i<<endl;}
};
classB2
{
public:
B2(intj){cout<<"constructingB2"<<j<<endl;}
};
classB3
{
public:
B3(){cout<<"constructingB3*"<<endl;}
};
classC:publicB2,publicB1,publicB3
{
public:
C(inta,intb,intc,intd):B1(a),memberB2(d),memberB1(c),B2(b){}
private:
B1memberB1;
B2memberB2;
B3memberB3;
};
voidmain()
{Cobj(1,2,3,4);}
运行后的结果如下:
constructingB22
constructingB11
constructingB3*
constructingB13
constructingB24
constructingB3*
为什么会有以上的结果?
众所周知构造函数的执行次序如下:
调用基类构造函数,调用顺序按照他们的继承时声明的顺序。
调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序。
派生类的构造函数体中的内容。
析构函数的调用顺序相反。
那么再来看以上的例子就很容易理解了。B2、B1、B3是C的基类,按照上述的顺序,我们先要构造基类,然后才是子对象,最后是其本身的构造函数所以先要执行这三个类的构造函数。在构造时按照他们在类中的顺序,首先调用B2的构造函数
B2(intj){cout<<"constructingB2"<<j<<endl;}
由于在默认参数列表
C(inta,intb,intc,intd):B1(a),memberB2(d),memberB1(c),B2(b){}
中,将b的值传给了B2的构造函数,b为2,故打印出:
constructingB22
接下来要构造的是B1了。显然在C的默认参数构造列表中将a的值传给了B1,
所以打印出:
constructingB11
B3在构造时没有传递参数,调用B3(){cout<<"constructingB3*"<<endl;}
打印出:
cout<<"constructingB3*
这时基类的构造函数已经执行完毕,接着该处理内嵌成员对象的构造函数了。
我们看到C类有三个对象:B1memberB1;B2memberB2;B3memberB3;,按照构造函数的调用顺序,我们需要按照他们在类中声明的顺序来分别构造memberB1、memberB2、memberB3。在默认的参数列表中,用c来构造了memberB1,用d来构造memberB2,
故打印出:
constructingB13
constructingB24
constructingB3*
最后调用本身的构造函数,由于函数体为空,故什么也没有打印出来。
总结下来,我们必须明确的是当一个类继承与基类,并且自身还包含有其他类的成员对象的时候,构造函数的调用顺序为:调用基类的构造函数->调用成员对象的构造函数->调用自身的构造函数。构造函数的调用次序完全不受构造函数初始化列表的表达式中的次序影响,与基类的声明次数和成员对象在函数中的声明次序有关。
再如:
#include<iostream.h>
classA
{
protected:
charc;
public:
A(charch)
{
c=ch;
cout<<"c="<<c<<endl;
cout<<"类A构造函数被调用"<<endl;
}
~A()
{
cout<<"类A析构函数被调用"<<endl;
}
};
classB
{
protected:
inti;
public:
B(intj)
{
i=j;
cout<<"i="<<i<<endl;
cout<<"类B构造函数被调用"<<endl;
}
~B()
{
cout<<"类B析构函数被调用"<<endl;
}
};
classC:publicA,B
{
private:
intk;
public:
C(charch,intii,intkk):A(ch),B(ii),k(kk)
{
cout<<"k="<<k<<endl;
cout<<"类C构造函数被调用"<<endl;
}
~C()
{
cout<<"类C析构函数被调用"<<endl;
}
};
voidmain()
{
CA('B',10,15);
}
输出
c=B
类A构造函数被调用
i=10
类B构造函数被调用
k=15
类C构造函数被调用
类C析构函数被调用
类B析构函数被调用
类A析构函数被调用
热心网友
时间:2023-09-29 03:48
知识点一、
子类对象的构造,是先构造父类对象,然后构造子类对象。
子类对象的析构,先运行子类对象的析构函数,再运行父类对象的析构函数,刚刚好喝对象的构造相反。
知识点二、
构造对象时,若没有制定参数,那么会通过默认构造函数初始化对象。如果有参数,那么会通过参数类型自动选择构造函数进行对象的初始化。这里和函数的重载是一样的。
另外,还要注意到类B中有一个A a1;的私有变量声明。
答案:
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数
类A的构造函数
类A的构造函数
类B的构造函数
类B的析构函数
类A的析构函数
类A的析构函数
1,3,2
4,6,5
类B的析构函数
类A的析构函数
类A的析构函数
类B的析构函数
类A的析构函数
类A的析构函数
热心网友
时间:2023-09-29 03:49
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的默认构造函数
类A的默认构造函数
类B的默认构造函数
类A的构造函数
类A的构造函数
类B的析构函数
类B的析构函数
类A的析构函数
类A的析构函数
类A的构造函数
类A的构造函数
类B的析构函数
类B的析构函数
类A的析构函数
类A的析构函数
1,3,2
4,6,5
类B的析构函数
类A的析构函数
类A的析构函数
类B的析构函数
类A的析构函数
类A的析构函数