C++之派生类的构造函数和析构函数

来源:互联网 发布:2006年网络流行歌曲 编辑:程序博客网 时间:2024/05/14 15:01

            派生类的构造函数

        派生类对象的数据结构由基类中说明的数据成员和派生类中说明的数据成员共同组成。在创建派生类的对象时,不仅要对派生类中说明的数据成员初始化,而且还要对基类中说明的数据成员初始化。由于构造函数不能被继承,因此,派生类的构造函数必须通过调用基类的构造函数来初始化基类中的数据成员。所以,在定义派生类的构造函数中,不仅要对自己数据成员进行初始化,还要包含调用基类的构造函数对基类中数据成员进行初始化。如果,派生类中海油子对象,还应包含调用对子对象初始化的构造函数。

       派生类的构造函数的定义格式:

       <派生类构造函数名> (  <总参数表>  ): <基类构造函数名> ( <参数表1> )

                                                                     <子对象名>  ( <参数表2>  )....

       {

                 <派生类数据成员初始化>

       }

       其中,派生类构造函数名同派生类名。派生类构造函数的总参数表中的参数包括冒号后面的所有参数表中的参数的总和。在对基类中数据成员初始化时,调用基类的构造函数产生一个对象,将该对象存放到创建派生类对象时所占有的内存单元中,作为派生类对象成员一部分。在对子对象初始化时,调用子对象类的构造函数进行初始化。最后调用派生类构造函数的函数体内语句对派生类本身的数据成员初始化。也可以把对派生类数据成员的初始化操作放在成员初始化列表中,这是派生类构造函数可能成为空函数。

       派生类构造函数的调用顺序为:

       1,基类构造函数

       2,子对象类构造函数(如果有子对象的话)

       3,派生类构造函数

       派生类的析构函数

       由于析构函数也不能被继承,因此执行派生类的析构函数时,也要调用基类的析构函数。执行派生类的析构函数的顺序正好与执行派生类构造函数的顺序相反:

       1,先调用派生类的析构函数

       2,再调用派生类中子对象类的析构函数

       3,最后调用基类的析构函数

       用一个例子来说明调用顺序:

<span style="font-size:18px;">#include <iostream>using namespace std;class A {public:A()//定义的基类A的默认构造函数{ a=0;cout<<"Default Constructor called.A\n"; }A(int i)//定义的带一个参数的基类A的构造函数{   a=i;  cout<<"Constructor called.A\n"; }~A()//定义的基类A的析构函数{ cout<<"Destructor called.A\n"; }void Print(){ cout<<a<<","; }    int Geta(){ return a; }private:int a;};class B:public A//派生类B公有继承基类A{public:    B()//派生类B的默认构造函数{ b=0;cout<<"Default Constructor called.B\n"; }B(int i,int j,int k);//说明的派生类B的构造函数~B()//派生类B的析构函数{ cout<<"Destructor called.B\n"; }void Print(){A::Print();//说明基类A的成员函数cout<<b<<","<<aa.Geta()<<endl;//通过子对象调用基类A的成员函数}private:int b;A aa;//子对象};B::B(int i,int j,int k):A(i),aa(j),b(k)//派生类B的构造函数的函数体{   cout<<"Constructor called.B\n";}int main(){B bb[2];//定义的派生类B的对象数组bb[0]=B(3,4,5);//通过派生类B对每个数组元素进行赋值bb[1]=B(7,-8,9);for(int i=0;i<2;i++)bb[i].Print();//通过对象调用成员函数return 0;}</span>


             程序分析:

       1,前六行输出的是在创建派生类B的对象数组中的两个对象元素时调用派生类B的默认构造函数前先调用基类A的默认构造函数,创建一个数组元素调用两次基类A的默认构造函数,再调用一次派生类B的构造函数。因此输出结果中的前六行。

       2,接着输出的第七行到十八行是在调用派生类B的构造函数时创建临时对象时,再将临时对象赋值给数组对象元素,然后再调用析构函数将临时对象释放。因此,每赋值给一个数组对象元素时,都会创建一个临时对象,在创建临时对象的过程中,先调用两次基类A的构造函数,再调用一次派生类B的构造函数,然后释放临时对象时,先调用一次派生类B的析构函数,再调用两次基类A的析构函数。因此赋值给两个数组对象元素会输出七到十八行的结果。

       3,接着输出的两行数字是派生类B的对象数组的元素的数据值。

       4,最后六行输出的是释放对象数组bb的两个元素,每释放一个先调用一次派生类B的析构函数,再调用两次基类A的析构函数。释放两个元素,因此输出的结果如上。

       使用派生类构造函数应注意的事项:

       一,在基类中有默认构造函数或者没有定义任何构造函数时,派生类构造函数中隐含对基类默认构造函数的调用。

<span style="font-size:18px;">#include <iostream>using namespace std;class A {public:A()//定义的基类A的默认构造函数{ a=0; }A(int i)//定义的带一个参数的基类A的构造函数{ a=i; }void Print(){ cout<<a<<","; }private:int a;};class B:public A//派生类B公有继承基类A{public:    B()//派生类B的默认构造函数{ b1=b2=0; }B(int i)//定义的派生类B的构造函数{ b1=0; b2=i; }B(int i,int j,int k):A(i),b1(j),b2(k)//赋值的派生类B的构造函数{}void Print(){A::Print();//说明基类A的成员函数cout<<b1<<","<<b2<<endl;}private:int b1,b2;};int main(){B b1;//创建的无参数的对象B b2(5);//创建的带一个参数的对象B b3(1,2,3);//带三个参数的对象b1.Print();b2.Print();b3.Print();return 0;}</span>

          分析程序:

      1,派生类B中有三个构造函数,前两个是隐含调用基类A中的默认构造函数,派生类B的第三个构造函数显式地调用了基类A中的一个带参数的构造函数。

      2,输出的结果第一行0,0,0  创建对象b1时调用派生类中的默认构造函数,并且在调用之前隐含调用了基类A的默认构造函数,因此输出这样的结果

      3,输出的第二行0,0,5  创建对象b2时调用派生类B中带一个参数的构造函数,在调用之前先隐含调用基类A中的默认构造函数,因此输出这样的结果。

      4,输出的第三行1,2,3  创建对象b3时调用派生类的第三个构造函数,在调用之前先显式调用了基类A中带一个参数的构造函数,因此输出这样的结果。

     二,当基类的构造函数使用一个或多个参数时,派生类的构造函数必须提供将参数传递给基类构造函数的途径。

<span style="font-size:18px;">#include <iostream>using namespace std;class A {public:A(int i,int j){ a1=i; a2=j; }void Print(){ cout<<a1<<","<<a2<<endl; }private:int a1,a2;};class B:public A//派生类B公有继承基类A{public:B(int i,int j,int k,int l,int m);void Print(){A::Print();//说明基类A的成员函数cout<<b<<",";aa.Print();//通过对象引用成员函数}private:int b;A aa;//子对象};B::B(int i,int j,int k,int l,int m):A(i,j),aa(k,l),b(m){}int main(){B b1(1,2,3,4,5);b1.Print();return 0;}</span>

          简单的分析出:派生类B的构造函数带5个参数,前两个参数传递给基类A中的构造函数,中间两个参数传递给子对象aa的类A的构造函数,最后一个参数传递给派生类B的构造函数。

       输出的结果为: 1,2

                                  5,3,4

 

     


 

 

 

1 0
原创粉丝点击