1:当函数以相应的类作为形参列表时,对象可以作为函数的参数传入。在学习函数时,我们曾提过,值传递先复制实参产生副本。那么对象的副本是怎样的呢?
复制构造函数是指类的对象被复制时所调用的函数。下面两种情况中对象都会调用复制构造函数。
(1)将一个对象赋值给另外一个对象时。例如:
对象1=对象2;
对象1(对象2);
上面的程序中对象2的复制构造函数会被调用。
(2)作为值传递的实参。例如:
function(对象1);
在function函数体内,使用的是对象1的副本。所以之前会调用对象1的复制构造函数。
和构造函数一样,C++在未发现自定义的复制构造函数之前会创建一个默认的构造函数。
自定义的复制构造函数的声明格式为:
类名(类名& 形参)
值得注意的是,复制构造函数时引用传递的函数。既然默认复制构造函数已经完成复制工作,那为何需要重新定义它呢?例如,一个类具有指针类型的数据,默认复制构造函数执行之后,原对象和副本的指针成员指向的是同一个内存空间。通过指针改变该内存,就会改变两个对象实际应用的数据(也就是这块内存的内容)。这时可以自定义复制构造函数,将两个指针的内存分离开。
2:代码如下:
(1)germ.h
#include <string> using std::string; class germ{ public: int m_age; string m_name; germ(germ& g);//自定义复制构造函数 类名(类名&形参) germ(string s);//构造函数 ~germ();//析构函数,执行收尾工作 };
(2)germ.cpp
#include "stdafx.h" #include "germ.h" #include <iostream> using std::cout; using std::endl; germ::germ(string s) { m_name = s; m_age = 1; cout<<"发现了"<<m_name<<endl; } germ::germ(germ& g) { g.m_age +=1; this->m_age = 1; this->m_name =g.m_name + "的复制体"; cout<<"产生了"<<g.m_name<<"的复制体"<<endl; } germ::~germ(){ cout<<this->m_name<<"被消灭了"<<endl; }
(3)main.cpp
// 7.7.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include "germ.h" using std::cout; using std::endl; germ copyGerm(germ gc) { return gc; } int main() { germ g1("有氧菌");//(发现了有氧菌) germ g2(g1);//第一个germ的意思是声明一个对象g2,g2(g1)的意思是将g1赋值给g2,此时将会调用复制构造函数(产生了有氧菌的复制体) germ g3("无氧菌");//(发现了无氧菌) germ g4 = g3;//意思同样是将g3赋值给g4,此时将会调用复制构造函数。(产生了无氧菌的复制体) germ g5 = copyGerm(g4);//此时也调用复制构造函数了,只要有副本,就会调用复制构造函数。此处调用了两次,copygerm()一次,等号一次(产生了无氧菌复制体的复制体,产生了无氧菌复制体的复制体的复制体) return 0; } //注意,调用copygerm后,copygerm会立即被释放,所以会立即放出析构函数,输出(无氧菌的复制体的复制体被消灭了) //以后依次g5,g4,g3,g2,g1被释放,故依次被输出这些被消灭
运行结果:
从程序运行结果来分析代码:首先在主函数中产生了g1对象,由复制构造函数产生了g1的复制体g2——有氧菌复制体。之后定义了g3——无氧菌。通过复制构造函数产生了g3的复制体g4。前四行输出即是上面所述的过程。g5的产生前,g5所在赋值语句等号右边的copyGerm函数被调用,传递的实参为g4——无氧菌的复制体。如同开始提到的,值传递实参对象产生副本,副本就是形参gc——无氧菌复制品的复制品。函数执行完毕后,传递回临时变量,内容是gc。g5的值经过复制语句,所以它是gc(临时变量使用的内存)的复制品。