第二十一節 C++ - 複製構造函數(淺複製,深複製)

例子1: 不帶指針成員的類,此例子,爲了看是否產生臨時對象(淺複製,按值傳遞)

#include <string>

/*此類中沒有指針成員*/
class Human
{
private:
	std::string name;

/*public類外可訪問*/
public:
	void getPersonName();
	Human(std::string input);
	~Human();
};
#include <iostream>
#include <string>
#include "Human.h"

/*構造函數(創建對象時被調用)*/
Human::Human(std::string input)
{
	std::cout << "1 call Human()" << std::endl;
	name = input;
}
/*析構函數(對象被銷燬時被調用,類只有一個析構函數*/
Human::~Human()
{
	std::cout << "2 call ~Human()" << std::endl;
}
/*類方法的實現*/
void Human::getPersonName()
{
	std::cout << "3 get name is : " << name << std::endl;
}

#include <iostream>
#include "Human.h"

/*對象按值傳遞(最好使用對象引用或者指針的方式傳遞值),潛藏極大風險,這裏僅僅是淺複製
 *淺複製:若類中存在指針,僅僅複製指針值,兩個對象指向同一地址,釋放時,存在極大風險
 *深複製:重新建立一個內存空間,保證原對象與複製出來的對象的指針不指向同一個內存空間
 *解決淺複製的辦法:在類中實現複製構造函數,將淺複製轉爲深複製
 */
void copyObject(Human person)  //複製時,產生一個臨時對象,將掉用構造函數,copyObject()執行完後,將調用析構函數
{								//通過此例子輸出,可以看到產生了臨時對象
	person.getPersonName();
}

int main()
{
	Human man("Tom"); //此處第一次調用構造函數
	man.getPersonName();
	copyObject(man);

	return 0;
}

輸出:

1 call Human()
3 get name is : Tom
3 get name is : Tom
2 call ~Human()      //此處調用了兩次析構函數,可以看出,對象按值傳遞時,產生了臨時對象,調用了析構函數,釋放了臨時變量
2 call ~Human()

---------------------------------------------------------------------------------------------------------------------------------

例子2: 帶指針成員的類,淺複製(按照值傳遞),編譯出錯

/* Human.h */
#include <string>  

/*此類中存在指針成員*/
class Human
{
private:
	char* name;  //對象淺複製時,會導致,兩個指針指向同一個內存,非常危險

	/*public類外可訪問*/
public:
	void getPersonNameAddr();
	Human(char* initString);
	~Human();
};
/*Human.cpp*/
#include <iostream>  
#include <string>  
#include "Human.h"  

/*構造函數(創建對象時被調用)*/
Human::Human(char* initString)
{
	std::cout << "1 call Human()" << std::endl;
	name = new char[strlen(initString) + 1];
}
/*析構函數(對象被銷燬時被調用,類只有一個析構函數*/
Human::~Human()
{
	std::cout << "2 call ~Human()" << std::endl;
	delete name;
}
/*類方法的實現*/
void Human::getPersonNameAddr()
{
	std::cout << "3 get name addr =  : " << name << std::endl;
}
#include <iostream>  
#include "Human.h"  

/*對象按值傳遞(最好使用對象引用或者指針的方式傳遞值),潛藏極大風險,這裏僅僅是淺複製*/
void copyObject(Human person)  //複製時,產生一個臨時對象,將調用構造函數,copyObject()執行完後,將調用析構函數  
{                               //通過此例子輸出,可以看到產生了臨時對象  
	person.getPersonNameAddr();
}

int main()
{
	Human man("Tom"); //此處第一次調用構造函數  
	man.getPersonNameAddr();
	copyObject(man);

	return 0;
}

輸出: 淺複製導致兩個指針同時指向同一塊內存,這將導致同一塊內存將被釋放兩次(將被delete兩次),所有出現下面的錯誤提示. 深複製將解決這個問題。


------------------------------------------------------------------------------------------------------------------------------------------

例子3: 帶指針成員的類,深複製(爲指針重新開闢新的內存空間),編譯通過

/* Human.h */
#include <string>  

/*此類中存在指針成員*/
class Human
{
private:
	char* name;  //指針指向一塊內存空間,要實現深複製防止兩個對象的指針指向同一個內存,非常危險

	/*public類外可訪問*/
public:
	void getPersonNameAddr();
	Human(const char* initString); //變量若不允許函數修改,最好定義成const變量
	/*複製構造函數(實現深複製), 參數爲對象,爲個不淺複製對象,用引用或指針的方式傳參*/
	Human(const Human& Source);
	~Human();
};
/*Human.cpp*/
#include <iostream>  
#include <string> 
#include <string.h>
#include "Human.h"  

/*構造函數(創建對象時被調用)*/
Human::Human(const char* initString)
{
	std::cout << "1 call Human()" << std::endl;
	name = new char[strlen(initString) + 1];
}

/*複製構造函數,按引用或指針傳參,不能按值傳遞*/
Human::Human(const Human& source)
{
	name = new char[strlen(source.name) + 1]; //重新分配一個內存空間,保證兩個對象的指針成員不指向同一個內存空間
	strncpy_s(name, strlen(source.name) + 1, source.name, strlen(source.name) + 1);
}

/*析構函數(對象被銷燬時被調用,類只有一個析構函數*/
Human::~Human()
{
	std::cout << "2 call ~Human()" << std::endl;
	delete name;
}
/*類方法的實現*/
void Human::getPersonNameAddr()
{
	printf("3 get name addr  = 0x%x\n", name); //輸出name地址
}
#include <iostream>  
#include "Human.h"  

/*類Human實現了複製構造函數,這裏不再是按值複製,而是通過調用複製構造函數來實現*/
void copyObject(Human person)    
{                                
	person.getPersonNameAddr();
}

int main()
{
	Human man("aaaa"); //此處第一次調用構造函數  
	man.getPersonNameAddr();
	copyObject(man);

	return 0;
}
輸出:
1 call Human()
3 get name addr  = 0x66a9b8  //第一次創建對象時,分配給name的地址
3 get name addr  = 0x66b2a0  //對象被複制時,複製構造函數分配給name的地址
2 call ~Human()
2 call ~Human()

兩者的地址不同,對內存進行釋放時,就防止了內存空間不會被釋放兩次,實現了深複製
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章