因为派生类是从基类继承而来的,所以包含了基类的一些成员,所以在写派生类的构造函数和复制控制函数时,必须考虑基类的影响。

先说构造函数,派生类的构造函数中,并不直接初始化基类的成员,而是调用基类的构造函数初始化基类的部分:

class Item_base
{
public://构造函数Item_base(const std::string &book = "",double sales_price = 0.0):isbn(book),price(sales_price){ std::cout<<"基类构造函数"<<std::endl;}//返回isbn号std::string book(){return isbn;}//基类不需要折扣策略virtual double net_price(std::size_t n)const{return n * price;}//析构函数virtual ~Item_base(){std::cout<<"基类析构函数"<<std::endl;};//复制控制函数Item_base (const Item_base&);//赋值操作Item_base& operator=(const Item_base&);private:std::string isbn;
protected:double price;
};class Bulk_item:public Item_base
{
public://构造函数Bulk_item(const std::string& book = "",double sales_price = 0.0,std::size_t qty = 0,double disc_rate = 0.0):Item_base(book,sales_price),quantity(qty),discount(disc_rate){ std::cout<<"派生类构造函数"<<std::endl;}~Bulk_item(){std::cout<<"派生类析构函数"<<std::endl;}double net_price(std::size_t)const;//复制控制Bulk_item (const Bulk_item&);//赋值操作符Bulk_item& operator=(const Bulk_item&);private:std::size_t quantity;double discount;
};

而且每次都是先初始化基类部分,在初始化派生类的成员。还有·一点需要注意,就是派生类只能初始化自己的基类,并不需要考虑基类的基类怎么初始化。

对于复制控制函数,我们一个一个看:

先说复制构造函数,它的作用是规定当用一个派生类对象复制给另一个派生类对象时,会发生什么。它需要完成的工作也是两部分:调用基类的复制构造函数完成基类部分的复制,然后再复制派生类的部分。

//基类复制控制
Item_base::Item_base(const Item_base& ib)
{isbn = ib.isbn;price = ib.price;std::cout<<"基类复制构造函数"<<std::endl;}
//派生类复制构造函数
Bulk_item::Bulk_item (const Bulk_item& b):Item_base(b);
{quantity = b.quantity;discount = b.discount;std::cout<<"派生类复制构造函数"<<std::endl;
}

注意一下作用域操作符:如果不加作用域标示符,会提示你形参b重定义。因为Item_base(b)相当于新建了一个Item_base的对象b,所以是重定义。使用作用域操作符以后,等于显示调用了基类的复制构造函数Item_base,用派生类直接给基类复制,所以不会出现重定义。

同理还有赋值操作符:

//基类的赋值操作
Item_base& Item_base::operator=(const Item_base& rhs)
{isbn = rhs.isbn;price = rhs.price;std::cout<<"基类赋值操作符"<<std::endl;return *this;
}
//赋值操作符
Bulk_item& Bulk_item::operator=(const Bulk_item& rhs)
{if(this != &rhs)Item_base::operator=(rhs);quantity = rhs.quantity;discount = rhs.discount;std::cout<<"派生类赋值操作符"<<std::endl;return *this;
}

这里要注意的是,我们增加了左右操作数是否相等的判断,只有在不等时,才调用基类的操作。

在析构函数中,我们什么也没有做,却把它定义成了虚函数,这是为什么呢?

通过前面的学习我们知道,如果一个类中有指针成员,那么应该在这个类的析构函数中删除指针成员。我们又知道:由于动态绑定的缘故,我们完全可以让一个静态类型为基类的指针指向一个派生类的对象。当通过基类的指针去删除派 生类的对象,而基类又没有虚析构函数时,会出现问题,举一个例子:

class A
{
public:A(){ptr = new int[10];cout<<"A构造函数"<<endl;}virtual ~A(){delete ptr;cout<<"A析构函数"<<endl;}
private:int *ptr;
};class B:public A
{
public:B(){ptr = new long[10];cout<<"B构造函数"<<endl;}~B(){delete ptr;cout<<"B析构函数"<<endl;}
private:long *ptr;};int main()
{A *a=new B();delete a;return 0;
}

程序运行依次打印:A构造函数,B构造函数,A析构函数。

可以看出,B构造函数中创建的指针并没有删除,这是因为,当执行delete a时,由于a的静态类型是指向A类的指针,所以会调用A的析构函数删除A构造函数中新建的对象。当我们把A类构造函数设定为虚函数时,由于虚函数是动态绑定的,所以会调用派生类的析构函数,而在派生类的析构函数中,调用基类的析构函数。这样,就能完全删除对象了。

这里还要强调下一构造函数与析构函数的执行顺序:建立一个派生类时,会调用派生类构造函数,然后在这个构造函数中(显示或者隐式的)调用基类构造函数,然后再初始化派生类的其他部分;析构函数中先调用派生类析构函数,先析构派生类自己的成员,最后在这个析构函数中隐式的调用基类的析构函数析构基类的成员。

最后的一点内容是对这一小节内容的总结以及上一小结的补充:

上一小节中并没有给出相关内容的说明程序,下面通过测试程序来具体说明:

首先是定义了3个函数:

void func1(Item_base obj){}
void func2(Item_base& obj){}
Item_base func3()
{Item_base obj;return obj;
}

这三个函数看起来并没有做什么实质性的工作,但他们很有代表新:前两个接受的分别是基类的对象和基类对象的引用,第三个的返回值是基类的对象,下面我们看看主程序:

int main()
{Item_base iobj;                    //调用基类构造函数,整个函数结束时释放func1(iobj);                 //调用基类构造函数创建一个临时对象,整个函数释放func2(iobj);                    //使用引用时,并不创建对象iobj = func3();                   //新建一个Item_base对象时调用基类构造函数//函数返回时调用复制构造函数//然后调用基类析构函数撤销局部对象//然后调用基类赋值操作符//整个函数结束时释放Item_base *p = new Item_base;   //调用基类构造函数创建Item_base对象delete p;                        //删除指针时调用析构函数Bulk_item bobj;                    //因为构造函数先执行列表,后执行函数体//打印先执行基类构造函数,然后打印构造派生类func1(bobj);                   //由于func1函数的形参是基类,所以先将派生类隐式转化成基类对象//然后调用基类复制构造函数将实参传给形参//调用基类析构函数撤销对象func2(bobj);                    //引用不存在临时对象的创建Bulk_item *q = new Bulk_item;    //派生类构造函数会调用基类构造函数delete q;                     //调用析构函数Item_base obj(bobj);            //派生类转化为基类,然后调用基类复制构造函数return 0;
}

程序的注释部分说明了具体的功能。值得注意的是:

1.如果将用派生类实参调用形参为基类引用的函数,并不会发生派生类到基类的类型转化,因为引用直接绑定到派生类上,对象并没有“类型转化”,只是将派生类的基类部分的地址传递给基类型的引用。

2.如果将派生类实参传递给一个基类形参的函数,先将派生类隐式转化成基类对象,然后调用基类复制构造函数将实参传给形参。

3.用派生类初始化基类时,派生类将转化为基类,然后调用基类的复制构造函数处理。

派生类的构造函数和复制控制相关推荐

  1. c++, 派生类的构造函数和析构函数 , [ 以及operator=不能被继承 or Not的探讨]

    说明:文章中关于operator=实现的示例,从语法上是对的,但逻辑和习惯上都是错误的. 参见另一篇专门探究operator=的文章:<c++,operator=>http://www.c ...

  2. php 派生类 构造,C++派生类的构造函数和析构函数

    派生类对象中包含基类对象,因此派生类对象在创建时,除了要调用自身的构造函数进行初始化外,还要调用基类的构造函数初始化其包含的基类对象.因此,程序中任何能够生成派生类对象的语句,都要说明其包含的基类对象 ...

  3. C# 派生类的构造函数

    假定没有为任何类定义任何显式的构造函数,这样编译器就会为所有的类提供默认的初始化构 造函数,在后台会进行许多操作,但编译器可以很好地解决类的层次结构中的所有问题,每个类中 的每个字段都会初始化为对应的 ...

  4. C++中基类与派生类的构造函数和析构函数

    1.Cpp中的基类与派生类的构造函数 基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和 ...

  5. 基类成员的public访问权限在派生类中变为_C++ 派生类的构造函数(学习笔记:第7章 06)...

    派生类的构造函数[1] 默认情况 基类的构造函数不被继承; 派生类需要定义自己的构造函数. C++11规定 可用using语句继承基类构造函数. 但是只能初始化从基类继承的成员. 派生类新增成员可以通 ...

  6. 【C++ Primer 第15章】定义派生类拷贝构造函数、赋值运算符

    学习资料 • 派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值 • C++ 基类构造函数带参数的继承方式及派生类的初始化 定义拷贝构造函数 [注意]对派生类进行拷贝构造时,如果想让基类的成 ...

  7. C++基类和派生类的构造函数(二)

    基类构造函数调用规则 事实上,通过派生类创建对象时必须要调用基类的构造函数,这是语法规定.换句话说,定义派生类构造函数时最好指明基类构造函数:如果不指明,就调用基类的默认构造函数(不带参数的构造函数) ...

  8. C++基类和派生类的构造函数(一)

    前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成 ...

  9. 派生类中构造函数与虚构函数的研究

    1.继承过程中的构造函数 A:继承与派生过程中,基类的构造函数不能被继承,派生类中需要声明自己的构造函数. B:声明构造函数时,只需要对本类中新增成员进行初始化.至于基类继承过来的成员,应该调用基类的 ...

最新文章

  1. python学习笔记-基础、语句、编码、迭代器
  2. python计算无穷级数求和常用公式_无穷级数-从入门到火葬
  3. jquery 常用方法 delegate() siblings() closest() indexOf() substring()
  4. 如何免费使用数据挖掘软件RapidMiner - 申请学生许可证
  5. Node.js实现Excel转JSON
  6. Skype For Business 2015实战系列2:安装活动目录
  7. python入门文件读取与写入_初学者Python:读取和写入同一文件
  8. win10 linux安卓模拟器,genymotion安卓模拟器在Window10中使用的问题
  9. Hadoop Yarn事件处理框架源码分析
  10. [转]微软:Visio 2010包括三大版本 功能对比
  11. Spring Boot 默认数据源 HikariDataSource 与 JdbcTemplate 初遇
  12. 3d slicer调整窗宽窗位_3D游戏模型制作技巧,掌握这些技术你也能进鹅厂!
  13. 数据集(三)|人工智能领域100+数据集分享,赶紧收藏!
  14. 雷达散射截面(RCS)
  15. 兄弟7180dn拆机_兄弟DCP-7010拆机图解
  16. 如何使用Python将Word转换为PDF文件?
  17. LM3886TF功放制作进展
  18. 几种最常见的网站盈利模式分析
  19. linux发送邮件mail详解
  20. EXTJS资源库管理平台 2013.5.26-在线制作头像

热门文章

  1. CVPR和ICLR双榜公布!最离谱审稿人竟然没读论文!
  2. Swin Transformer V2
  3. Joint Learning of Deep Retrieval Model and Product Quantization based Embedding Index
  4. 关键字 'order' 附近有语法错误,应为ID,QUOTED_ID,或','
  5. 魔幻的2020,对我来说却是逐渐觉醒的一年
  6. 服务器 日志打印 中文变乱码
  7. i12蓝牙耳机充电仓怎么看充满电_真无线耳机的性价比之选,倍思Encok w05开箱体验...
  8. python怎么加字幕_Python如何实现字幕挂载 Python实现字幕挂载代码示例
  9. 国网GIM设备三维模型要求细则 - 隔离开关及接地开关
  10. h3c服务器管理虚拟机,02-虚拟机配置