对象作为函数参数时调用拷贝构造函数、引用做函数参数时只是传递地址
2014-10-13 10:14
871 查看
对象也可以作为函数的参数传递给函数,其转递方法与传递其他类型的数据一样,可采用值传递和地址传递两种方法。
值传递:是把对象的拷贝而不是本身传递给函数,函数中对参数对象的任何修改都不会影响调用该函数的对象本身;
地址传递:调用该函数的对象与参数对象共用同一个地址,所以,函数对参数对象的任何修改都会影响调用该函数的对象本身。
注意:在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”)
1) 一个对象作为函数参数,以值传递的方式传入函数体
2) 一个对象作为函数返回值,以值传递的方式从函数返回
3) 一个对象用于给另外一个对象进行初始化(常称为复制初始化)
当用引用变量做参数时,不调用拷贝构造函数,用传递引用的方式给函数传递一个对象的引用时,只传递了该对象的地址,系统消耗较小。在函数体内访问 形参,实际是访问了这个作为实参的对象。例如:void
function(CTest & test);
Java中的引用传递是指: 例如:void
function(CTest test),没有&号
调用sqr_it前,实参对象obj的数据成员i=10
在函数sqr_it1内,形参对象obj的数据成员i=100
调用sqr_it1后,实参对象obj的数据成员i=10
(此时,由于是以值传递,obj的数据成员i没有变化,还是原先的10)
在函数sqr_it2内,形参对象obj的数据成员i=100
调用sqr_it2后,实参对象obj的数据成员i=100
(此时,由于是以地址传递,obj的数据成员i发生变化,为sqr_it2中的值100)
通常的原则是:①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。
以下讨论中将用到的例子:
这个类的主要特点是包含指向其他资源的指针,pBuffer指向堆中动态分配的一段内存空间。
语句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
回顾一下此语句的具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被复制初始化。
其完成方式是内存拷贝,复制所有成员的值。完成后,theObjtwo.pBuffer==theObjone.pBuffer。
即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。所以需要采用必要的手段来避免此类情况:可以在构造函数中添加操作来解决指针成员的这种问题。
所以C++语法中除了提供缺省形式的构造函数外,还规范了另一种特殊的构造函数:拷贝构造函数,一种特殊的构造函数重载。上面的语句中,如果类中定义了拷贝构造函数,在对象复制初始化时,调用的将是拷贝构造函数,而不是缺省构造函数。在拷贝构造函数中,可以根据传入的变量,复制指针所指向的资源。
拷贝构造函数的格式为:类名(const 类名& 对象名);//拷贝构造函数的原型,参数是常量对象的引用。由于拷贝构造函数的目的是成员复制,不应修改原对象,所以建议使用const关键字。
提供了拷贝构造函数后的CExample类定义为:
这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample& RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。
下面介绍拷贝构造函数的另一种调用:当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数。例如:
总结:当某对象是按值传递时(无论是作为函数参数,还是作为函数返回值),编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数。
拷贝构造函数的实现:
类名::类名(类名&对象名)//拷贝构造函数的实现/定义
{函数体}
值传递:是把对象的拷贝而不是本身传递给函数,函数中对参数对象的任何修改都不会影响调用该函数的对象本身;
地址传递:调用该函数的对象与参数对象共用同一个地址,所以,函数对参数对象的任何修改都会影响调用该函数的对象本身。
注意:在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”)
1) 一个对象作为函数参数,以值传递的方式传入函数体
2) 一个对象作为函数返回值,以值传递的方式从函数返回
3) 一个对象用于给另外一个对象进行初始化(常称为复制初始化)
当用引用变量做参数时,不调用拷贝构造函数,用传递引用的方式给函数传递一个对象的引用时,只传递了该对象的地址,系统消耗较小。在函数体内访问 形参,实际是访问了这个作为实参的对象。例如:void
function(CTest & test);
Java中的引用传递是指: 例如:void
function(CTest test),没有&号
#include<iostream> using namespace std; class Tr{ public: //Tr(int n):i(n) //构造函数最好这么写 //{} Tr(int n) { i=n; } void set_i(int n) {i=n;} int get_i() {return i;} private: int i; }; void sqr_it1(Tr ob) { ob.set_i(ob.get_i()*ob.get_i()); cout<<"在函数sqr_it1内,形参对象obj的数据成员i="<<ob.get_i(); cout<<endl; } void sqr_it2(Tr *obj) { obj->set_i(obj->get_i()*obj->get_i()); cout<<"在函数sqr_it2内,形参对象obj的数据成员i="<<obj->get_i(); cout<<endl; } int main() { Tr obj(10); cout<<"调用sqr_it前,实参对象obj的数据成员i="<<obj.get_i()<<endl; sqr_it1(obj); cout<<"调用sqr_it1后,实参对象obj的数据成员i="<<obj.get_i()<<endl; sqr_it2(&obj); cout<<"调用sqr_it2后,实参对象obj的数据成员i="<<obj.get_i()<<endl; return 0; }
调用sqr_it前,实参对象obj的数据成员i=10
在函数sqr_it1内,形参对象obj的数据成员i=100
调用sqr_it1后,实参对象obj的数据成员i=10
(此时,由于是以值传递,obj的数据成员i没有变化,还是原先的10)
在函数sqr_it2内,形参对象obj的数据成员i=100
调用sqr_it2后,实参对象obj的数据成员i=100
(此时,由于是以地址传递,obj的数据成员i发生变化,为sqr_it2中的值100)
通常的原则是:①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。
复制初始化
以下讨论中将用到的例子:class CExample { public: CExample(){pBuffer=NULL; nSize=0;} ~CExample(){delete []pBuffer;} void Init(int n){ pBuffer=new char ; nSize=n;} private: char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源 int nSize; };
这个类的主要特点是包含指向其他资源的指针,pBuffer指向堆中动态分配的一段内存空间。
int main(int argc, char* argv[]) { CExample theObjone; theObjone.Init(40); //现在需要另一个对象,并将它初始化为theObjone CExample theObjtwo=theObjone; ... }
语句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
回顾一下此语句的具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被复制初始化。
其完成方式是内存拷贝,复制所有成员的值。完成后,theObjtwo.pBuffer==theObjone.pBuffer。
即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。所以需要采用必要的手段来避免此类情况:可以在构造函数中添加操作来解决指针成员的这种问题。
所以C++语法中除了提供缺省形式的构造函数外,还规范了另一种特殊的构造函数:拷贝构造函数,一种特殊的构造函数重载。上面的语句中,如果类中定义了拷贝构造函数,在对象复制初始化时,调用的将是拷贝构造函数,而不是缺省构造函数。在拷贝构造函数中,可以根据传入的变量,复制指针所指向的资源。
拷贝构造函数的格式为:类名(const 类名& 对象名);//拷贝构造函数的原型,参数是常量对象的引用。由于拷贝构造函数的目的是成员复制,不应修改原对象,所以建议使用const关键字。
提供了拷贝构造函数后的CExample类定义为:
class CExample { public: CExample(){pBuffer=NULL; nSize=0;} ~CExample(){delete []pBuffer;} CExample(const CExample&); //拷贝构造函数 void Init(int n){ pBuffer=new char ; nSize=n;} private: char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源 int nSize; }; CExample::CExample(const CExample& RightSides) //拷贝构造函数的定义 { nSize=RightSides.nSize; //复制常规成员 pBuffer=new char[nSize]; //复制指针指向的内容 memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char)); }
这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample& RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。
对象按值传递
下面介绍拷贝构造函数的另一种调用:当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数。例如:BOOL testfunc(CExample obj); testfunc(theObjone); //对象直接作为参数。 BOOL testfunc(CExample obj) { //针对obj的操作实际上是针对复制后的临时拷贝进行的 } 还有一种情况,也是与临时对象有关:当函数中的局部对象作为返回值被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用。 CTest func() { CTest theTest; return theTest; }
总结:当某对象是按值传递时(无论是作为函数参数,还是作为函数返回值),编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数。
拷贝构造函数的实现:
类名::类名(类名&对象名)//拷贝构造函数的实现/定义
{函数体}
相关文章推荐
- 对象作为函数参数时调用拷贝构造函数、引用做函数参数时只是传递地址
- 使用通过寄存器传递的参数的地址, 作为支持调用其他函数
- 当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的.
- JavaSE8基础 多态扩展性 父类引用作为函数参数,调用所装子类对象的重写方法
- 关于将函数的地址作为参数传递给另一个函数调用
- 17周---项目1-体会函数参数传递---用引用作为形参
- 值传递-引用传递-指针传递-函数调用时指针参数的传递 指针和引用的区别
- 对象间值传递,函数参数按值,引用传递_legend
- python 元组和字典中元素作为函数调用参数传递
- python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变
- python的raw_input()函数。 函数的可变对象和不可变对象作为参数传递。
- Java 把对象作为参数传到一个方法里,这个是值传递还是引用传递, 为什么结果这么怪?
- 函数参数与函数调用(彻底理解值传递与引用传递)转别人
- 拷贝构造函数为何可以调用引用参数对象的私有成员?
- string类型在参数传递中如何作为引用,如何在函数外修改函数内的string类型
- js中每个函数中隐含的arguments对象,用语表示调用时实际传递的参数
- Java中方法调用参数传递的方式是传值,尽管传的是引用的值而不是对象的值。(Does Java pass by reference or pass by value?)
- 函数的参数传递的几种形式(值、地址、引用)及指针基础知识
- JNI调用c++函数,该函数的参数是结构体(——对象的传递)
- C++传递对象函数指针作为参数