一、复制构造函数中的&和const
#include "iostream"
using namespace std;
class CExample
{
public:
CExample(int x) :m_nTest(x) //带参数构造函数
{
cout<< "constructor with argument."<<endl;
}
CExample(const CExample & ex) //拷贝构造函数
{
m_nTest = ex.m_nTest;
cout << "copy constructor."<<endl;
}
CExample& operator = (const CExample &ex)//赋值函数(赋值运算符重载)
{
cout << "assignment operator." << endl;
m_nTest = ex.m_nTest;
return *this;
}
void myTestFunc(CExample ex) {
}
private:
int m_nTest;
};
int main()
{
CExample aaa(2);
CExample bbb(3);
bbb = aaa;
CExample ccc = aaa;
bbb.myTestFunc(aaa);
system("pause");
return 0;
}
运行结果:
分析结果:
第一个输出: constructor with argument. //CExample aaa(2);
这里创建了变量aaa,在创建的同时还带有参数2,那就调用带参数的构造函数
第二个输出:constructor with argument. //CExample bbb(3);
分析同第一个
第三个输出:assignment operator. //bbb = aaa;
bbb之前已经创建了,所以这个不会调用构造函数,而只是将aaa赋值给bbb,所以调用赋值函数 第四个输出:copy constructor. //CExample ccc = aaa;
这个和上一个的区别就在于:bbb之前已经创建过了,而这里的ccc是在这条语句才创建的,所以这里是在创建ccc的同时将aaa赋值给ccc,所以这句调用的肯定是构造函数,又因为需要将aaa赋值给ccc,所以调用的是拷贝构造函数。
第五个输出:copy constructor. // bbb.myTestFunc(aaa); 这里是调用了一个自己写的myTestFunc函数,其中这个函数中的参数没有采用引用,那就是值传递的方式。就是编译器先创建一个类型为CExample名称为ex的对象,然后将aaa的值传递给ex(值传递方式的特性),将相当于要执行一条CExample ex = aaa的语句。经第四个输出的分析可知,这需要调用拷贝构造函数。所以输出copy constrctor。
问题解答:
1.为什么要用引用
答:由上节的第五个输出分析可知,在执行bbb.myTestFunc(aaa);时,其实会调用拷贝构造函数。如果我们的拷贝构造函数的参数不是引用,那么在bbb.myTestFunc(aaa);时,调用CExample ex = aaa;,又因为ex之前没有被创建,所以又需要调用拷贝构造函数,故而又执行CExample ex = aaa;,就这样永远的递归调用下去了。
2.为什么要用const
答:如果在函数中不会改变引用类型参数的值,加不加const的效果是一样的。而且不加const,编译器也不会报错。但是为了整个程序的安全,还是加上const,防止对引用类型参数值的意外修改。
二、缺省复制构造函数
#include<stdio.h>
#include<string.h>
class cString
{
char *s;
public:
cString(void);
cString(const char*);
cString(const cString &);
void print(){ printf( "%s\n",s );}
void test(cString &t){ strcpy(t.s,"6666");};
};
cString::cString(void) //构造函数1
{
s=new char[1];
*s='\0';
}
cString::cString(const char *x)//构造函数2
{
s=new char[strlen(x)+1];
strcpy(s,x);
}
cString::cString(const cString & x)
{ //构造函数3
s=new char[strlen(x.s)+1];
strcpy(s,x.s);
}
int main( int argc, char* argv[])
{
cString x("abcd"); //调用一般构造函数
cString y=x; //调用复制构造函数
x.print( );
y.print( );
x.test(x);
x.print( );
y.print( );
}
运行结果:
将复制构造函数删去后代码如下:
#include<stdio.h>
#include<string.h>
class cString
{
char *s;
public:
cString(void);
cString(const char*);
//cString(const cString &);
void print(){ printf( "%s\n",s );}
void test(cString x){ strcpy(x.s,"6666");};
};
cString::cString(void) //构造函数1
{
s=new char[1];
*s='\0';
}
cString::cString(const char *x)//构造函数2
{
s=new char[strlen(x)+1];
strcpy(s,x);
}
/*cString::cString(const cString & x)
{ //构造函数3
s=new char[strlen(x.s)+1];
strcpy(s,x.s);
}*/
int main( int argc, char* argv[])
{
cString x("abcd"); //调用一般构造函数
cString y=x; //调用缺省复制构造函数
x.print( );
y.print( );
x.test(x);
x.print( );
y.print( );
}
运行结果如下:
分析结果:
第一次和第二次运行结果,前两行相同,后两行不一样。
由前两行相同可知不管是一般复制构造函数还是缺省构造函数(即不手动定义构造函数),都能达到“复制”的效果。
代码里都将x复制给了y。
但是在对x.s的值作修改了以后,y.s第一次不变,第二次变成了修改后的x.s的值。
问题解答:
1.为什么两次运行y.s的值不一样?
答:复制构造函数用于将入口参数(对象)中的字符串复制到当前对象中的字符串成员中,这一操作的结果使得入口参数对象中的字符串与当前对象中的字符串分别占用不同的存储区域。
而缺省构造函数所完成的操作将使得入口参数中的字符串与当前对象中的字符串占用同样的存储空间,这也就是说如果改变了其中任何一个对象中的字符串,则另一个对象中的字符串也将随之改变。
三、以下3种情况都要调用复制构造函数:
1.当利用类的已有对象来初始化另一个新对象时,系统将自动调用复制构造函数
2.如果函数的形参是类对象,则在进行函数调用时(将实参传递给形参时),将自动调用复制构造函数
(由此可见,如果复制构造函数的参数不是对象引用而是对象本身,则将出现运行错误,这是由于将对象传递给复制构造函数时还需要调用复制构造函数,这就产生无限循环调用.)参考一中的例子
需要注意的是,如果函数的形参是类对象的指针或对象引用,则在函数调用时并不调用复制构造函数
3.如果函数的返回值是类对象,则在执行返回语句时将自动调用复制构造函数(返回类指针和引用时不调用复制构造函数) |