class A
{
public:
A()
{
}
A(int id,char *t_name)
{
_id=id;
name=new char[strlen(t_name)+1];
strcpy(name,t_name);
}
A& operator =(A& a)
//注意:此處必定要返回對象的引用,不然返回後其值當即消失!
{
if(name!=NULL)
delete name;
this->_id=a._id;
int len=strlen(a.name);
name=new char[len+1];
strcpy(name,a.name);
return *this;
}
~A()
{
cout<<"~destructor"<<endl;
delete name;
}
int _id;
char *name;
};
int main()
{
A a(1,"herengang");
A b;
b=a;
}其內存分配以下:
這樣,在對象a,b退出相應的做用域,其調用相應的析構函數,而後釋放分別屬於不一樣heap空間的內存,程序正常結束。
references:
類的深拷貝函數的重載
public class A
{
public:
...
A(A &a);//重載拷貝函數
A& operator=(A &b);//重載賦值函數
//或者 咱們也能夠這樣重載賦值運算符 void operator=(A &a);即不返回任何值。若是這樣的話,他將不支持客戶代買中的鏈式賦值 ,例如a=b=c will be prohibited!
private:
int _id;
char *username;
}
A::A(A &a)
{
_id=a._id;
username=new char[strlen(a.username)+1];
if(username!=NULL)
strcpy(username,a.usernam);
}
A& A::operaton=(A &a)
{
if(this==&a)// 問:什麼須要判斷這個條件?(不是必須,只是優化而已)。答案:提示:考慮a=a這樣的操做。
return *this;
if(username!=NULL)
delete username;
_id=a._id;
username=new char[strlen(a.username)+1];
if(username!=NULL)
strcpy(username,a.usernam);
return *this;
}
//另一種寫法:
void A::operation=(A &a)
{
if(username!=NULL)
delete username;
_id=a._id;
username=new char[strlen(a.username)+1];
if(username!=NULL)
strcpy(username,a.usernam);
}
其實,從上能夠看出,賦值運算符和拷貝函數很類似。只不過賦值函數最好有返回值(進行鏈式賦值),返回也最好是對象的引用(爲何不是對象自己呢?note2有講解), 而拷貝函數不須要返回任何。同時,賦值函數首先要釋放掉對象自身的堆空間(若是須要的話),而後進行其餘的operation.而拷貝函數不須要如此,由於對象此時尚未分配堆空間。note1:
不要按值向函數傳遞對象。若是對象有內部指針指向動態分配的堆內存,絲絕不要考慮把對象按值傳遞給函數,要按引用傳遞。並記住:若函數不能改變參數對象的狀態和目標對象的狀態,則要使用const修飾符
note2:問題:
對於類的成員須要動態申請堆空間的類的對象,你們都知道,咱們都最好要overload其賦值函數和拷貝函數。拷貝構造函數是沒有任何返回類型 的,這點毋庸置疑。 而賦值函數能夠返回多種類型,例如以上講的void,類自己class1,以及類的引用 class &? 問,這幾種賦值函數的返回各有什麼異同?
答:1 若是賦值函數返回的是void ,咱們知道,其惟一一點須要注意的是,其不支持鏈式賦值運算,即a=b=c這樣是不容許的!
2 對於返回的是類對象自己,仍是類對象的引用,其有着本質的區別!
第一:若是其返回的是類對象自己。
A operator =(A& a)
{
if(name!=NULL)
delete name;
this->_id=a._id;
int len=strlen(a.name);
name=new char[len+1];
strcpy(name,a.name);
return *this;
}
其過程是這樣的:
class1 A("herengnag");
class1 B;
B=A;
看似簡單的賦值操做,其全部的過程以下:
1 釋放對象原來的堆資源
2 從新申請堆空間
3 拷貝源的值到對象的堆空間的值
4 建立臨時對象(調用臨時對象拷貝構造函數),將臨時對象返回
5. 臨時對象結束,調用臨時對象析構函數,釋放臨時對象堆內存
my god,還真複雜!!
可是,在這些步驟裏面,若是第4步,咱們沒有overload 拷貝函數,也就是沒有進行深拷貝。那麼在進行第5步釋放臨時對象的heap 空間時,將釋放掉的是和目標對象同一塊的heap空間。這樣當目標對象B做用域結束調用析構函數時,就會產生錯誤!!
所以,若是賦值運算符返回的是類對象自己,那麼必定要overload 類的拷貝函數(進行深拷貝)!
第二:若是賦值運算符返回的是對象的引用,
A& operator =(A& a)
{
if(name!=NULL)
delete name;
this->_id=a._id;
int len=strlen(a.name);
name=new char[len+1];
strcpy(name,a.name);
return *this;
}
那麼其過程以下:
1 釋放掉原來對象所佔有的堆空間
1.申請一塊新的堆內存
2 將源對象的堆內存的值copy給新的堆內存
3 返回源對象的引用
4 結束。
所以,若是賦值運算符返回的是對象引用,那麼其不會調用類的拷貝構造函數,這是問題的關鍵所在!!
完整代碼以下:
// virtual.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "string.h"
#include "stdlib.h"
#include "assert.h"
class complex
{
public:
int real;
int virt;
public:
complex(){real=virt=0;}
complex(int treal,int tvirt){real=treal;virt=tvirt;}
complex operator+(const complex &x)
{
real+=x.real;
virt+=x.virt;
return *this;
}
complex operator=(const complex &x)
{
return complex(x.real,x.virt);
}
};
class A
{
public:
A(){m_username=NULL;printf("null constructor");}
A(char *username)
{
int len;
len=strlen(username);
m_username=new char[len+1];//(char*)malloc(sizeof(len+1));
strcpy(m_username,username);
printf(""nUsername is %s"n",m_username);
}
A(A &a);
A operator=(A &b);
int test(const int &x)
{
return x;
}
virtual ~A()
{
// if(m_username)
{
delete m_username;
printf(""nA is destructed"n");
}
}
protected:
char *m_username;
};
A::A(A &a)
{
int len=strlen(a.m_username);
this->m_username=new char[len+2];
strcpy(m_username,a.m_username);
strcat(m_username,"f");
printf(""ndeep copy function");
}
A A::operator=(A &b)
{
if(m_username)
delete m_username;
int len=strlen(b.m_username);
this->m_username=new char[len+1];
strcpy(m_username,b.m_username);
// printf("copied successfully!");
return *this;
}
class B:public A
{
public:
B(char *username,char *password):A(username)
{
int len=strlen(password)+1;
m_password=new char[len];//(char *)malloc(sizeof(len));
strcpy(m_password,password);
printf("username:%s,password:%s"n",m_username,m_password);
}
~B()
{
delete m_password;
printf("B is destructed"n");
}
protected:
char *m_password;
};
int main(int argc, char* argv[])
{
// B b("herengang","982135");
// A *a=&b;
// delete a;
A a("haha");
A b;
printf(""nbegin to invoke copy function");
b=a;
// printf("%d",b.test(2));
//complex x(1,3),y(1,4);
//x=(x+y);
//printf("%d,%d",x.real,x.virt);
return 0;
}
1 重載賦值運算符返回結果爲類對象的運行結果this
明顯, 運算符最後調用了拷貝構造函數
2 重載賦值運算符返回結果爲類對象引用的運行結果spa
很明顯,沒有調用拷貝構造函數設計
轉自:http://www.cnblogs.com/alexusli/archive/2008/08/27/1277683.html指針