例子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()
兩者的地址不同,對內存進行釋放時,就防止了內存空間不會被釋放兩次,實現了深複製