C++基础:继承与派生(构造 析构函数 显式访问)

1.派生类如何构造:

对于简单的派生类,即只有一个基类,且直接派生(多继承将在后续几节中做详细讲解),来讲,如果基类的构造函数没有参数,或者没有显式定义构造函数,那么派生类可以不向基类传递参数,甚至可以不定义构造函数。但是一旦基类含有带参数的构造函数时,派生类必须定义构造函数,并把参数传递给基类构造函数。其一般格式:

  派生类名(参数总表)::基类名(参数子表)

  {

    派生类新增成员的初始化语句;

  }

而析构函数可以有用户自己定义,由于其是不带参数的,所以在派生类中是否要自定义析构函数与它所属基类的析构函数无关。在执行派生类的构造函数时,系统会自动调用基类的析构函数,进行对象清理。下面举例来说明一下简单派生类的构造和析构: 

 #include "stdafx.h"
 #include <iostream>
 #include <string>
 
 class C
 {
 private:
         std::string c;
 public:
         C(){};
         C(std::string c);
 ~C();
 void showC();
 };
 
 C::C(std::string c)
 {
 this->c=c;
     std::cout<<"构造对象c"<<std::endl;
 }
 
 C::~C()
 {
     std::cout<<"清理对象c"<<std::endl;
 }
 void C::showC()
 {
     std::cout<<"c="<<this->c<<std::endl;
 }
 
 
 class D:private C
 {
 private:
         std::string d;
 public:
         D(std::string d,std::string c);
 ~D();
 void showD();
 };
 
 D::D(std::string d,std::string c):C(c) //参数传递给基类,也可以不写C(c),那么就调用了C类中无参构造函数
 {
 this->d=d;
     std::cout<<"构造对象d"<<std::endl;
 }
 
 D::~D()
 {
     std::cout<<"清理对象d"<<std::endl;
 }
 
 void D::showD()
 {
     showC();
     std::cout<<"d="<<this->d<<std::endl;
 }
 
 
 
 int main()
 {
     {
         D d("ddd","ccc");
         d.showD();
     }
 
 return0;
 }

结果:

在结果里我们可以看到派生类构造函数和析构函数的调用顺序,当创建派生类对象时,首先调用基类的构造函数,再调用派生类构造函数,而当清理对象时,则刚好相反。

  那么如果当派生类中存在成员对象,那么构造函数和析构函数的调用顺序又如何呢?在解答这个问题之前,我们先看一下当派生类中含有子对象时,构造函数的一般格式:

  派生类名(参数总表):基类名(参数子表),对象名1(参数子表1),对象名2(参数子表2)

  {

    派生类新增成员的初始化语句;

  } 

还是,我们用一个示例来说明,并通过实际的运行结果来解答上述的问题:

#include "stdafx.h"
 #include <iostream>
 #include <string>
 
 class C
 {
 private:
         std::string c;
 public:
         C(){};
         C(std::string c);
 ~C();
 void showC();
 };
 
 C::C(std::string c)
 {
 this->c=c;
     std::cout<<"构造c值为"<<c<<"的对象c"<<std::endl;
 }
 
 C::~C()
 {
     std::cout<<"清理c值为"<<c<<"的对象c"<<std::endl;
 }
 void C::showC()
 {
     std::cout<<"c="<<this->c<<std::endl;
 }
 
 
 class D:private C
 {
 private:
         std::string d;
         C c;
 public:
         D(std::string d,std::string c1,std::string c2);
 ~D();
 void showD();
 };
 
 D::D(std::string d,std::string c1,std::string c2):c(c2),C(c1) //参数传递给基类,也可以不写C(c1)或c(c2),那么就调用了C类中无参构造函数
 {
 this->d=d;
     std::cout<<"构造对象d"<<std::endl;
 }
 
 D::~D()
 {
     std::cout<<"清理对象d"<<std::endl;
 }
 
 void D::showD()
 {
     showC();
     c.showC();
     std::cout<<"d="<<this->d<<std::endl;
 }
 
 
 
 int main()
 {
     {
         D d("ddd","ccc1","ccc2");
         d.showD();
     }
 
 return0;
 }//结果:

从结果中很清楚的说明了其构造函数和析构函数的调用顺序。另外读者也可以在类D成员对象c后面再声明一个基类对象,运行看看,修改过后其构造和析构的调用顺序又如何?最后一点要补充的是,如果派生类的基类也是派生类,那么每个派生类只需要负责其直接的基类数据成员的初始化

  2.在定义派生类时,C++里是允许派生类中的成员名和基类中的成员名相同,出现这种情况,我们称派生类成员覆盖了基类中使用相同名称的成员。也就是说当你在派生类中或用对象访问该同名成员时,你所访问只是派生类中的成员,基类中的就自动被忽略。但如果我确实要访问到基类中的同名成员那怎么办,C++中这样规定必须在成员名前加上基类名和作用域标识符"::"。

示例总结:

 #include "stdafx.h"
 #include <iostream>
 #include <string>
 
 class C
 {    
 public:
         std::string c;
         C(){};
         C(std::string c);
 ~C();
 void showC();
 };
 
 C::C(std::string c)
 {
 this->c=c;
     std::cout<<"构造c值为"<<c<<"的对象c"<<std::endl;
 }
 
 C::~C()
 {
     std::cout<<"清理c值为"<<c<<"的对象c"<<std::endl;
 }
 void C::showC()
 {
     std::cout<<"c="<<this->c<<std::endl;
 }
 
 
 class D:public C
 {
 private:
         std::string c;//同名数据成员
         std::string d;
         C c2;
         C c3;
 public:
         D(std::string d,std::string c1,std::string c2);
 ~D();
 void showC();//同名成员函数
 void updateC(std::string c);
 };
 
 D::D(std::string d,std::string c2,std::string c3):c2(c2),c3(c3),C("ccc1") //参数传递给基类,也可以不写C(c1)或c(c2),那么就调用了C类中无参构造函数
 {
 this->d=d;
 this->c=c2;//覆盖了基类的数据成员c,访问的是派生类的数据数据成员c
     std::cout<<"构造对象d"<<std::endl;
 }
 
 D::~D()
 {
     std::cout<<"清理对象d"<<std::endl;
 }
 
 void D::showC()//同名成员函数
 {
     C::showC();//显式访问基类成员函数showC();
     c2.showC();
     std::cout<<"D::c="<<c<<std::endl;//覆盖了基类的数据成员c
     std::cout<<"C::c="<<C::c<<std::endl;//显式访问基类数据成员c
     std::cout<<"d="<<this->d<<std::endl;
 }
 
 void D::updateC(std::string c)
 {
     C::c=c;//显式访问基类数据成员c
 }
 
 
 
 int main()
 {
     {
         D d("ddd","ccc2","ccc3");
         d.showC();//访问的是派生类中函数成员showC()
         d.updateC("ccc4");
         d.showC();//同理
     }
 
 return0;
 }

结果:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值