默認複製函數
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;
}