构造函数constructor 与析构函数destructor(四)
拷贝构造函数:拷贝构造函数就是在用一个类对象来创建另外一个类对象时被调用的构造函数,如果我们没有显示的提供拷贝构造函数,编译器会隐式的提供一个默认拷贝构造函数。
拷贝构造函数的定义是X(const X& ){}
- class Test{
- int m_i;
- public:
- Test(int i):m_i(i){}
- Test(const Test& vt):m_i(vt.m_i){}//拷贝构造函数
int getI()const {return m_i;}- };
- int main(){
- Test t();
- Test t2(t);//调用拷贝构造函数来初始化t2对象
- Test t3=t;//等价于t3(t),也是调用拷贝构造函数
- return ;
- }
记住拷贝构造函数的只有一个参数,并且这个参数是类类型的const引用,参数不能是普通的值传递,必须是引用。原因有二
一:如果参数是const Test vt,那么实参在传递给形参的时候,还是会给形参创建对象,分配内存。这时把实参传递给形参,仍然是是用类对象初始化类对象,还是会调用拷贝构造函数,就会形成递归调用。
二:如果传递引用,实际上是传递的一个地址,不会创建新的对象,而拷贝时也只是拷贝的一个地址大小的空间,会增加程序的效率。
当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。
- void fun(Test vt){
- cout<<vt.,getI()<<endl;
- }
这个函数的参数是类的对象,会调用拷贝构造函数的
- int main(){
- Test t();
- Test t2 = t;//调用一次拷贝构造函数
- Test t3(t);//调用一次拷贝构造函数
- fun(t);//调用拷贝构造函数
- return ;
- }
fun的参数变成对象的引用时,就不会再调用拷贝构造函数了。
返回值为Const 引用 以及类对象的区别
当函数的返回值是类对象,函数执行完成,返回到调用者时也会调用拷贝构造函数。理由也是要建立一个临时对象中,从函数返回的对象来初始化产生的临时对象。
- //test.h
- #ifndef TEST_H
- #define TEST_H
- class Test
- {
- int m_i;
- public:
- Test(int i=);
- Test(const Test& vt);
- ~Test();
- int getI()const;
- };
- #endif //TEST_H
- //test.cpp
- #include "Test.h"
- #include<iostream>
- using std::cout;
- using std::endl;
- Test::Test(int i) :m_i(i)
- {
- cout << "default constructor" << endl;
- }
- Test::Test(const Test& vt):m_i(vt.m_i){
- cout << "copy constructor" << endl;
- }
- Test::~Test()
- {
- }
- int Test::getI()const{
- return m_i;
- }
- //demo.cpp
- #include<iostream>
- #include"Test.h"
- using std::endl;
- using std::cout;
- void fun1(Test& vt){
- cout << "m_i=" << vt.getI() << endl;
- }
- void fun2(Test vt){
- cout << "m_i=" << vt.getI() << endl;
- }
- Test fun3(const Test& vt){
- return vt;
- }
- int main(){
- Test t;
- fun3(t);//因为fun3函数的参数是类的引用,因此不用产生临时对象。但是返回值类型为类对象会产生临时对象,
- // 所以要调用一个拷贝构造函数,把从函数返回的值,拷贝到生成的临时对象中
- return ;
- }
我们还可以在析构函数中添加代码,来测试临时对象时什么时候释放的。
在析构函数~Test()中添加打印代码,main函数如下所示:
- int main(){
- Test t;
- fun3(t);//因为fun3函数的参数是类的引用,因此不用产生临时对象。但是返回值类型为类对象会产生临时对象,
- // 所以要调用一个拷贝构造函数,把从函数返回的值,拷贝到生成的临时对象中
- cout << "........." << endl;
- return ;
- }
我们可以看到,临时对象产生后,如果没有别的对象接受,马上就销毁。如果有别的对象接受,再复制完成后销毁。但是如果mia函数中添加一句如下的代码
- int main(){
- Test t;
- Test t2=fun3(t);//因为fun3函数的参数是类的引用,因此不用产生临时对象。但是返回值类型为类对象会产生临时对象,
- // 所以要调用一个拷贝构造函数,把从函数返回的值,拷贝到生成的临时对象中
- cout << "........." << endl;
- return ;
- }
此时临时对象被Test t2接受,就没有马上释放,而是当t2的生命周期完时释放。
现在增加一个函数fun4,它的返回值类型和传递的参数类型都是类的引用
- const Test& fun4(const Test& vt){
- return vt;
- }
然后把main的代码修改成如下的;
- int main(){
- Test t;//创建t对象,调用默认构造函数
- const Test& t1=fun4(t);//传参,和返回值都没有产生临时对象。
- cout << "........." << endl;
- return ;
- }
运行结果
我们可以看到,当返回值为类的引用时,并不会产生临时对象。这是一个非常好的改变程序效率的方法。
所以从上面的一系列代码中,我们可以看到,传参和返回值类型会影响程序的效率,因为传参类型是值类型的话,会产生临时对象,临时对象的产生,释放,拷贝,都会产生消耗,从而导致程序效率降低。
构造函数constructor 与析构函数destructor(四)的更多相关文章
- 构造函数constructor 与析构函数destructor(五)
我们知道当调用默认拷贝构造函数时,一个对象对另一个对象初始化时,这时的赋值时逐成员赋值.这就是浅拷贝,当成员变量有指针时,浅拷贝就会在析构函数那里出现问题.例如下面的例子: //test.h #ifn ...
- 构造函数constructor 与析构函数destructor(一)
构造函数定义:构造函数c++中在创建对象时自动调用,用来初始化对象的特殊函数. (1)构造函数的名字必须与类的名字相同,不能有返回值,哪怕是void 也不行. (2)通常情况下构造函数应声明为公有函数 ...
- 构造函数constructor 与析构函数destructor(二)
(1)转换构造函数 转换构造函数的定义:转换构造函数就是把普通的内置类型转换成类类型的构造函数,这种构造函数只有一个参数.只含有一个参数的构造函数,可以作为两种构造函数,一种是普通构造函数用于初始化对 ...
- 构造函数constructor 与析构函数destructor(三)
(1)构造函数初始化列表: 1 class Test{ 2 int i; 3 public: 4 Test(int vi):i(vi){}//这里的从冒号开始,到右大括号结束,这一段是构造函数初始化列 ...
- GCC的__attribute__ ((constructor))和__attribute__ ((destructor))
通过一个简单的例子介绍一下gcc的__attribute__ ((constructor))属性的作用.gcc允许为函数设置__attribute__ ((constructor))和__attrib ...
- javascript工厂函数(factory function)vs构造函数(constructor function)
如果你从其他语言转到javascript语言的开发,你会发现有很多让你晕掉的术语,其中工厂函数(factory function)和构造函数(constructor function)就是其中的一个. ...
- 【转】c++析构函数(Destructor)
创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存.关闭打开的文件等,这个函数就是析构函数. 析构函数(Destructor)也 ...
- C++——构造函数 constructor
What is constructor C++中,如果你想要创建一个object,有一个函数会自动被调用(不需要programmer显式调用 ),这个函数就是constructor; construc ...
- 类(class)、构造函数(constructor)、原型(prototype)
类 Class 类的概念应该是面向对象语言的一个特色,但是JavaScript并不像Java,C++等高级语言那样拥有正式的类,而是多数通过构造器以及原型方式来仿造实现.在讨论构造器和原型方法前,我可 ...
随机推荐
- Zabbix结合Grafana绘图
参考网站: https://www.jianshu.com/p/6eec985c5c94 注意事项: 1.在ganafa添加datasource的时候url写成http://10.116.33.116 ...
- 如何使用Python快速制作可视化报表----pyecharts
如何使用Python快速制作可视化报表 数据可视化能力已经越来越成为各岗位的基础技能.领英的数据报告显示,数据可视化技能在2017年中国最热门技能中排名第一. 就数据分析而言,可视化探索几乎是你正 ...
- java 执行sql错误 传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。参数 1 (""): 数据类型 0x38 未知
连接数据库时设置:Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE ,ResultSet.CONCUR_R ...
- getitem, setitem, delitem (把类实例化成字典的类型)
class Foo(object): def __init__(self): self.data = {} def __getitem__(self, key): ...
- UI5-文档-4.37-Content Density
在本演练教程的这一步中,我们将根据用户的设备调整内容密度.SAPUI5包含不同的内容密度,允许您为支持触摸的设备显示更大的控件,为鼠标操作的设备显示更小.更紧凑的设计.在我们的app中,我们将检测设备 ...
- UI5-文档-4.10-Descriptor for Applications
所有特定于应用程序的配置设置现在将进一步放在一个名为manifest的单独描述符文件中.json.这清楚地将应用程序编码从配置设置中分离出来,使我们的应用程序更加灵活.例如,所有SAP Fiori应用 ...
- ios 缩放动画
CABasicAnimation *buttonAni = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; ...
- Mysql生成索引的方式
1.选择索引的数据类型 MySQL支持很多数据类型,选择合适的数据类型存储数据对性能有很大的影响.通常来说,可以遵循以下一些指导原则: (1)越小的数据类型通常更好:越小的数据类型通常在磁盘.内存和C ...
- pandas 语句
1.对于时间格式数据的处理 有些时候time_stamp是object格式,提取相应的日期,小时,星期等: 方法1 from datetime import datetime user_trad[ ...
- 疯狂JAVA——第八章 java集合
集合类主要负责保存.盛装其他数据,因此集合类也被称为容器类. 数组元素既可以是基本类型的值,也可以是对象(实际上是保存的对象的引用): 集合里只能保存对象.