默认复制函数
1、当把初始化一个对象的时候,用另一个已经存在的对象赋值。
比如String str2 = str1;其中str2为已经生成的对象。这个时候,编译器会把str1中的成员按值赋给str2中的每个成员。如果对象成员中没有使用new来分配动态内存空间,则编译器自动生成的默认赋值函数,就可以正确实现,而不会出现错误。
但是如果对象成员中有new分配的动态内存空间,比如下面的这个类,str2中的指针str和str1中的指针str会指向同一个内存空间。
当str2执行析构函数时,释放了内存空间。这个时候也就是说str1的str指向的内存空间被释放了。所以就会出现错误。
这个情况还有三种方式也会:
String str2(str1);
String str2 = String(str1);
String *pstr = new String(str1);
2、当对象按值传递给函数形参的时候。
比如void callmey(String str2); callmey(str1);//这个时候会产生一个临时对象,将str1中的成员按值赋给临时对象的成员。和上面的情况一样,都会出现错误。
3、函数返回对象的时候,因为这个时候会产生临时对象,所以会调用默认复制构造函数。
应该定义默认复制构造函数为:(实现深度复制)
//复制构造函数
StringBad::StringBad(const StringBad &s)
{
num_strings++;
len = s.len;
str = new char[len + 1];
strcpy(str, s.str);
cout << "调用了复制构造函数\n";
}
赋值运算符
当用一个已经生成的对象,给另外一个已经生成的对象赋值时,会调用赋值运算符。复制的时候和调用复制构造函数一样,都是成员按值赋值。如果涉及到new动态分配内存空间,就会出现错误。
比如:
String str1("yxk");
String str2;
str2 = str1;//这个时候会调用赋值运算符函数,将str1中各个成员按值赋给str2中的各个成员。如果也涉及到new分配的动态内存时,也会出现错误。
String str3("xiaokui");
str3 = str1;//将str1中的值赋给str3中的值。这个因为存在new分配的动态内存,所以在赋值运算符函数中应该首先释放str3中存在的动态内存空间,
再分配内存,将str1中的str指向的内存空间内容复制过来。也正是因为先释放自身的内存空间,所以不能将对象自身赋值给自身。
应该定义赋值运算符为:(实现深度复制)
//赋值运算符
StringBad & StringBad::operator=(const StringBad & st)
{
//首先不能自身赋给自身
if (&st == this)
return *this;
delete [] str;//因为是给对象赋值,所以应该删除以前的内存,再重新分配
len = st.len;
str = new char[len + 1];
strcpy(str, st.str);
printf("调用了赋值运算符\n");
return *this;
}
下面是C++ Primer Plus 第十二章的第一个例子:
#include <iostream>
#ifndef STRINGBAD_H_
#define STRINGBAD_H_
class StringBad
{
private:
char * str;
int len;
static int num_strings;
public:
StringBad(const char *s);
StringBad();
~StringBad();
StringBad(const StringBad &s);//复制构造函数
StringBad & StringBad::operator=(const StringBad & st);//赋值运算符
friend std::ostream & operator<<(std::ostream &os, const StringBad & st);
};
#endif
#include <cstring>
#include "StringBad.h"
using namespace std;
int StringBad::num_strings = 0;
StringBad::StringBad(const char *s)
{
len = strlen(s);
str = new char[len + 1];
strcpy(str, s);
num_strings++;
cout << "*******************\n";
cout << num_strings << ": " << str << endl;
cout << "*******************\n";
}
StringBad::StringBad()
{
len = 4;
str = new char[4];
strcpy(str, "C++");
num_strings++;
cout << "**************************************\n";
cout << num_strings << ": " << str << " Created!" << endl;
cout << "**************************************\n";
}
StringBad::~StringBad()
{
--num_strings;
cout << "**************************************\n";
cout << num_strings << " left, " << str << " deleted!"<< endl;;
cout << "**************************************\n";
delete [] str;
}
std::ostream & operator<<(ostream &os, const StringBad & st)
{
os << st.str;
return os;
}
//复制构造函数
StringBad::StringBad(const StringBad &s)
{
num_strings++;
len = s.len;
str = new char[len + 1];
strcpy(str, s.str);
cout << "调用了复制构造函数\n";
}
//赋值运算符
StringBad & StringBad::operator=(const StringBad & st)
{
//首先不能自身赋给自身
if (&st == this)
return *this;
delete [] str;//因为是给对象赋值,所以应该删除以前的内存,再重新分配
len = st.len;
str = new char[len + 1];
strcpy(str, st.str);
printf("调用了赋值运算符\n");
return *this;
}
#include "StringBad.h"
#include <iostream>
using namespace std;
void callme1(StringBad &);
void callme2(StringBad);
int main()
{
{
//cout << "String...............\n";
StringBad headline1("celery stalks");
StringBad headline2("LEttuce prey");
StringBad sports("Spinish Leaves");
cout << "headline1: " << headline1 << endl;
cout << "headline2: " << headline2 << endl;
cout << "sports: " << sports << endl;
callme1(headline1);
cout << "headline1: " << headline1 << endl;
callme2(headline2);
cout << "headline2: " << headline2 << endl;
StringBad sailor = sports;
cout << "sailor: " << sailor << endl;
cout << "sports: " << sports << endl;
StringBad knot;
knot = headline1;
cout << "knot: " << knot << endl;
}
getchar();
//getchar();
return 0;
}
void callme1(StringBad & rsb)
{
cout << "passed by reference: " << rsb << endl;
}
void callme2(StringBad sb)
{
cout << "passed by value: " << sb << endl;
}