派生类的构造函数和复制控制
因为派生类是从基类继承而来的,所以包含了基类的一些成员,所以在写派生类的构造函数和复制控制函数时,必须考虑基类的影响。
先说构造函数,派生类的构造函数中,并不直接初始化基类的成员,而是调用基类的构造函数初始化基类的部分:
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.用派生类初始化基类时,派生类将转化为基类,然后调用基类的复制构造函数处理。
派生类的构造函数和复制控制相关推荐
- c++, 派生类的构造函数和析构函数 , [ 以及operator=不能被继承 or Not的探讨]
说明:文章中关于operator=实现的示例,从语法上是对的,但逻辑和习惯上都是错误的. 参见另一篇专门探究operator=的文章:<c++,operator=>http://www.c ...
- php 派生类 构造,C++派生类的构造函数和析构函数
派生类对象中包含基类对象,因此派生类对象在创建时,除了要调用自身的构造函数进行初始化外,还要调用基类的构造函数初始化其包含的基类对象.因此,程序中任何能够生成派生类对象的语句,都要说明其包含的基类对象 ...
- C# 派生类的构造函数
假定没有为任何类定义任何显式的构造函数,这样编译器就会为所有的类提供默认的初始化构 造函数,在后台会进行许多操作,但编译器可以很好地解决类的层次结构中的所有问题,每个类中 的每个字段都会初始化为对应的 ...
- C++中基类与派生类的构造函数和析构函数
1.Cpp中的基类与派生类的构造函数 基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和 ...
- 基类成员的public访问权限在派生类中变为_C++ 派生类的构造函数(学习笔记:第7章 06)...
派生类的构造函数[1] 默认情况 基类的构造函数不被继承; 派生类需要定义自己的构造函数. C++11规定 可用using语句继承基类构造函数. 但是只能初始化从基类继承的成员. 派生类新增成员可以通 ...
- 【C++ Primer 第15章】定义派生类拷贝构造函数、赋值运算符
学习资料 • 派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值 • C++ 基类构造函数带参数的继承方式及派生类的初始化 定义拷贝构造函数 [注意]对派生类进行拷贝构造时,如果想让基类的成 ...
- C++基类和派生类的构造函数(二)
基类构造函数调用规则 事实上,通过派生类创建对象时必须要调用基类的构造函数,这是语法规定.换句话说,定义派生类构造函数时最好指明基类构造函数:如果不指明,就调用基类的默认构造函数(不带参数的构造函数) ...
- C++基类和派生类的构造函数(一)
前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成 ...
- 派生类中构造函数与虚构函数的研究
1.继承过程中的构造函数 A:继承与派生过程中,基类的构造函数不能被继承,派生类中需要声明自己的构造函数. B:声明构造函数时,只需要对本类中新增成员进行初始化.至于基类继承过来的成员,应该调用基类的 ...
最新文章
- python学习笔记-基础、语句、编码、迭代器
- python计算无穷级数求和常用公式_无穷级数-从入门到火葬
- jquery 常用方法 delegate() siblings() closest() indexOf() substring()
- 如何免费使用数据挖掘软件RapidMiner - 申请学生许可证
- Node.js实现Excel转JSON
- Skype For Business 2015实战系列2:安装活动目录
- python入门文件读取与写入_初学者Python:读取和写入同一文件
- win10 linux安卓模拟器,genymotion安卓模拟器在Window10中使用的问题
- Hadoop Yarn事件处理框架源码分析
- [转]微软:Visio 2010包括三大版本 功能对比
- Spring Boot 默认数据源 HikariDataSource 与 JdbcTemplate 初遇
- 3d slicer调整窗宽窗位_3D游戏模型制作技巧,掌握这些技术你也能进鹅厂!
- 数据集(三)|人工智能领域100+数据集分享,赶紧收藏!
- 雷达散射截面(RCS)
- 兄弟7180dn拆机_兄弟DCP-7010拆机图解
- 如何使用Python将Word转换为PDF文件?
- LM3886TF功放制作进展
- 几种最常见的网站盈利模式分析
- linux发送邮件mail详解
- EXTJS资源库管理平台 2013.5.26-在线制作头像
热门文章
- CVPR和ICLR双榜公布!最离谱审稿人竟然没读论文!
- Swin Transformer V2
- Joint Learning of Deep Retrieval Model and Product Quantization based Embedding Index
- 关键字 'order' 附近有语法错误,应为ID,QUOTED_ID,或','
- 魔幻的2020,对我来说却是逐渐觉醒的一年
- 服务器 日志打印 中文变乱码
- i12蓝牙耳机充电仓怎么看充满电_真无线耳机的性价比之选,倍思Encok w05开箱体验...
- python怎么加字幕_Python如何实现字幕挂载 Python实现字幕挂载代码示例
- 国网GIM设备三维模型要求细则 - 隔离开关及接地开关
- h3c服务器管理虚拟机,02-虚拟机配置