9.10 用值来传递对象 从前面几节我们了解了按地址传递与按值传递的区别,按地址传递可以修改原始变量的值,按值传递由于是传递的原始变量的拷贝,因此它不会修改原始变量的值。 假如仅仅是传递变量的话,采用指针或者引用这种按地址传递方式的优势不是很明显,但是假如是传递较大的对象的话,这种优势是相当的明显的。 这是因为,按值传递在向函数传递一个对象时,会像传递变量那样建立一个该对象的拷贝,而从函数返回一个对象时,也要建立这个被返回的对象的一个拷贝。 这样假如该对象的数据非常多时,这种拷贝带来的内存开销是相当可观的。比如说该对象拥有1000多个double型成员变量,每个double型变量占据8个字节,1000个就要占据8000个字节,每次通过值传递的方式给函数该对象,都要在栈中复制该对象,占用8000个字节的栈内空间,而返回该对象,又要在栈中复制一次,这样就又要占用8000个字节的内存空间。我们知道栈的内存只有2M大小,8000个字节占用8K,那么仅仅传递该对象就占用了栈内16K字节的空间。并且别的对象想要访问该对象的8000个数据成员的时候,也要同样采用复制的方式,那么系统的开销将无法估算了。 注:B = 字节,1KB = 1024B、1MB = 1024KB、1GB = 1024MB 然而,按值传递所付出的开销远不止如此,由于在传递过程中需要复制对象,因此会默认调用复制构造函数,该函数的作用就是创建某个对象的临时拷贝。关于复制构造函数,将会在深入函数中做进一步讲解,这里你只需要知道,只要在栈中创建临时拷贝都会自动调用复制构造函数即可。 而当函数返回时,传递该对象时创建的该对象的拷贝会被删除,这时候又会自动调用该对象的析构函数来释放内存。假设返回的仍然是该对象,并且仍旧采用按值传递的方式,那么就又会调用复制构造函数建立一个该对象的临时拷贝,当该值被成功返回给调用程序后,然后再调用该对象的析构函数删除临时拷贝并释放内存(由于对象都在栈区创建,一切都由系统自动完成。)。 我们看到复制构造函数和析构函数一连被执行了两次,这无疑会增加系统的开销。我们用一个实例来演示一下按值传递一个对象的复制与删除过程。 //A.h #pragma once #include<iostream> using namespace std; class A { public: A(); A(A&);//复制构造函数 ~A(); }; //A.cpp #include "A.h" A::A() { cout<<"执行构造函数创建一个对象"<<endl; } A::A(A&) { cout<<"执行复制构造函数创建该对象的副本"<<endl; } A::~A() { cout<<"执行析构函数删除该对象"<<endl; } //Program.cpp #include<iostream> #include"A.h" using namespace std; A func(A one) { cout<<"&one:"<<&one<<endl; return one; } int main() { A a;//在这一行定义了一个类A的对象a cout<<"&a:"<<&a<<endl; func(a); cout<<"Main finish"<<endl; return 0; } A a;//在这一行定义了一个类A的对象a 创建一个对象会自动调用构造函数。 由于构造函数中加入了一条输出信息,所以我们看到输出“执行构造函数创建一个对象”。 func(a); 接下来将对象a按值传递到func函数中,这时会自动调用复制构造函数创建对象a的一个副本,然后将这个副本传递到func函数中去。 所以我们看到输出了“执行复制构造函数创建该对象的副本”,以证明复制构造函数被调用。 A func(A one) { cout<<"&one:"<<&one<<endl; return one; } 当我们将这个副本传递到func函数中,func函数又将接收到的副本返回了。 由于返回方式也是按值返回,所以又会调用复制构造函数。 再次创建一个返回值one的副本。 所以我们看到输出的还是复制构造函数调用的信息。 由于func函数的返回值没有赋给任何对象,因此这个返回的临时对象也就被丢失了。这时自动调用析构函数来释放这个临时对象所占用的内存。所以我们看到了这条析构函数执行的信息。 由于func函数已经结束了,因此在它内部拷贝的临时对象one也就被系统删除了。对象被删除会自动调用该对象的析构函数来释放内存。因此我们看到的是又一条析构函数在执行的信息。 最后,main函数结束,对象a的生命也结束了。这样就会调用析构函数来释放对象a的内存。因此我们看到了最后一条析构信息。 通过这个程序我们就可以观察到,将一个对象按值传递给一个函数,会调用两次复制构造函数和两次析构函数。 这样系统的开销是很大的,下一节我们就来解决这个开销的问题。 与LSGO一起学系列图文之 C++ 第1章 初识C++ 第2章 做一个简短的C++程序 第3章 初步了解函数 第4章 C++数据类型 第5章 if语句与逻辑运算符 第6章 面向对象 第7章 循环语句 第8章 指针 第9章 引用 9.9 让函数返回多个值 9.10 用值来传递对象 9.11 用指针来传递对象 9.12 用const指针来传递对象 9.13 用引用来传递对象 9.14 到底是使用引用还是使用指针 9.15 引用和指针可以一块用 9.16 引用容易犯的错误 9.17 引用一个按值返回的堆对象 9.18 引用一个按别名返回的堆对象 9.19 在哪里创建就在哪里释放 第10章 深入函数 第11章 运算符重载 第12章 继承 第13章 虚函数 第14章 数组 第15章 链表 第16章 多态 第17章 类的特殊成员 第18章 字符串 第19章 代码重用 第20章 友元类与嵌套类 第21章 流 第22章 命名空间 第23章 模板 第24章 异常和错误处理 第25章 补充内容 C++学习补充资料 与LSGO一起学“C++上机小练习10”参考代码 通过微信学习的知识只能是碎片化的知识,作为新时代的我们希望能够构建自己的知识结构,使我们的知识体系化,系统化,以后在遇到碎片化的知识,我们做的只是融合到自己的知识结构中,故我们将推出“与LSGO一起学”系列课程,帮助大家来构建知识框架,初步规划有:
如果对这些内容感兴趣,可以一起来学习讨论。 我们的官网: www.lsgogroup.com
|
|