構造函數和析構函數可以被繼承,一文詳解構造函數和析構函數

 2023-11-22 阅读 24 评论 0

摘要:一文詳解構造函數和析構函數一、對象的初始化和清理二、析構函數三、構造函數可以重載四、默認構造函數和默認析構函數五、拷貝構造函數5.1 編譯器提供了默認的構造函數5.2 拷貝構造函數中形參必須要用引用類型六、構造函數的分類和調用七、匿名對象八、拷貝構造函數的調用時

一文詳解構造函數和析構函數

    • 一、對象的初始化和清理
    • 二、析構函數
    • 三、構造函數可以重載
    • 四、默認構造函數和默認析構函數
    • 五、拷貝構造函數
      • 5.1 編譯器提供了默認的構造函數
      • 5.2 拷貝構造函數中形參必須要用引用類型
    • 六、構造函數的分類和調用
    • 七、匿名對象
    • 八、拷貝構造函數的調用時機
      • 8.1 對象以值的方式給函數參數
    • 8.2 vs debug模式下的函數局部對象返回值
      • 8.3 vs Release模式下函數局部對象以值的方式從函數返回
    • 九、構造函數的調用規則
      • 9.1 如果程序員提供有參數構造,那么編譯器不會提供默認構造函數,但是會提供默認的拷貝構造函數
      • 9.2 如果程序員提供了拷貝構造函數 那么編譯器不會提供默認的構造函數和默認的拷貝構造函數
    • 十、多個對象的構造函數和析構函數
      • 10.1 多個對象的構造函數和析構函數的規則
      • 10.2 多個對象初始化列表
    • 十一、對象的深淺拷貝
      • 11.1 默認的拷貝構造函數進行了簡單的賦值操作
      • 11.2 深拷貝

一、對象的初始化和清理

?C++中面向對象的思想來源于現實,是對現實事物的抽象模擬,具體來說,當我們創建對象的時候,這個對象應該有一個初始狀態,當對象銷毀之前應該銷毀自己創建的一些數據

?對象的初始化和清理也是兩個非常重要的安全問題,一個對象或者變量沒有初始化,對其使用后果是未知的,同樣的使用完一個變量,沒有及時清理,也會造成一定的安全問題,C++為了給我們提供這種問題的解決方案,構造函數和析構函數,這兩個函數將會被編譯器自動調用,完成對象初始化和對象清理工作。

構造函數和析構函數可以被繼承、?對象的初始化和清理工作是編譯器強制我們要做的事情,即使你不提供初始化操作和清理操作,編譯器也會給你增加默認的操作,只是這個默認初始化操作不會做任何事,所以編寫類就應該順便提供初始化函數

簡單的案例

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 構造函數  初始化成員變量 編譯器調用Maker(){a = 10;cout << "構造函數" << endl;}~Maker(){// 析構函數// 對象銷毀前  編譯器調用cout<<"析構函數"<<endl}public:int a;
};void test01()
{// 創建對象 分配空間  局部變量在棧區  // 調用構造函數Maker m;// 創建對象時 調用構造函數int b = m.a;cout << b << endl;
}int main()
{//cout << "hello" << endl;test01();return EXIT_SUCCESS;
}

有參數構造:

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 構造函數Maker(){a = 10;cout << "構造函數" << endl;}public:int a;
};void test01()
{Maker m;// 創建對象時 調用構造函數int b = m.a;cout << b << endl;
}// 析構函數的作用class Maker2
{
public:// 有參數構造Maker2(const char* name, int age){cout << "有參數構造" << endl;// 從堆區進行申請pName = (char*)malloc(sizeof(char) * (strlen(name) + 1));// 申請字符串長度的字節個數  +1 是因為填充\0strcpy(pName,name);// 賦值字符串   參數直接寫成字符串地址即可mAge = age;}void Print(){cout << "打印參數" << pName << mAge << endl;}private:char* pName;int mAge;};void test02()
{Maker2 m("hh",11);m.Print();}int main()
{test02();return EXIT_SUCCESS;
}

二、析構函數

析構函數的作用就是在對象銷毀之前,回收內存資源

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
// 析構函數的作用
class Maker2
{
public:// 有參數構造Maker2(const char* name, int age){cout << "有參數構造" << endl;// 從堆區進行申請pName = (char*)malloc(sizeof(char) * (strlen(name) + 1));// 申請字符串長度的字節個數  +1 是因為填充\0strcpy(pName,name);// 賦值字符串   參數直接寫成字符串地址即可mAge = age;}void Print(){cout << "打印參數" << pName << mAge << endl;}~Maker2(){cout << "析構函數 " << endl;//釋放堆區空間if (pName != NULL){free(pName);pName = NULL;}}private:char* pName;int mAge;
};void test02()
{Maker2 m("hh",11);m.Print();
}
int main()
{test02();return EXIT_SUCCESS;
}

三、構造函數可以重載


class Maker3
{
public:// 構造函數可以重載Maker3(){// 無參構造函數cout}Maker3(int a){// 有參構造函數}
};

構造函數必須聲明為public,如果是私有化,創建對象會被報錯,沒辦法調用構造函數

什么是析構函數、有對象產生必然會調用構造函數,有對象銷毀必然會調用析構函數,有多少次對象產生就會調用多少次構造函數,有多少次對象銷毀就會調用多少次析構函數

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker3
{
public:// 構造函數可以重載Maker3(){// 無參構造函數cout << "無參數構造" << endl;}Maker3(int a){// 有參構造函數cout << "有參數構造" << endl;}~Maker3(){// 析構函數cout << "析構函數" << endl;}
};void test03()
{Maker3 m;Maker3 m1(1);
}int main()
{//cout << "hello" << endl;test03();return EXIT_SUCCESS;
}

注意:

構造函數和析構函數必須是共有權限,否則創建對象時,會報錯
初始化用構造函數,清理使用析構函數,這兩個函數是編譯器調用
構造函數可以進行重載
構造函數沒有返回值,不可以使用void,析構函數沒有返回值,不能使用void

四、默認構造函數和默認析構函數

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker
{public:// 編譯器提供默認的構造函數和析構函數 函數體都是空的Maker(){}~Maker(){}// 編譯器提供默認的構造函數和析構函數void printMaker(){a = 100;cout << a << endl;}private:int a;
};void test()
{Maker m;m.printMaker();
}int main()
{//cout << "hello" << endl;test();return EXIT_SUCCESS;
}

五、拷貝構造函數

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker
{public:// 編譯器提供默認的構造函數和析構函數 函數體都是空的Maker(){cout << "構造函數" << endl;a = 20;}// 拷貝構造函數  參數是對象的引用Maker(const Maker &m){cout << "拷貝構造函數" << endl;a = m.a;// 賦值成員變量}~Maker(){}// 編譯器提供默認的構造函數和析構函數void printMaker(){a = 100;cout << a << endl;}private:int a;
};void test()
{Maker m;//m.printMaker();Maker m1(m);// 用已有的對象生成另一個對象  涉及到構造函數的重載m1.printMaker();
}int main()
{//cout << "hello" << endl;test();return EXIT_SUCCESS;
}

5.1 編譯器提供了默認的構造函數

C++編譯器提供了默認的拷貝構造函數,默認的拷貝構造函數進行了成員變量的簡單賦值

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker
{public:// 編譯器提供默認的構造函數和析構函數 函數體都是空的Maker(){cout << "構造函數" << endl;a = 20;}//   // 拷貝構造函數  參數是對象的引用//Maker(const Maker &m)//{//	cout << "拷貝構造函數" << endl;//	a = m.a;// 賦值成員變量//}~Maker(){}// 編譯器提供默認的構造函數和析構函數void printMaker(){cout << a << endl;}private:int a;
};void test()
{Maker m;//m.printMaker();Maker m1(m);// 用已有的對象生成另一個對象  涉及到構造函數的重載m1.printMaker();
}int main()
{//cout << "hello" << endl;test();return EXIT_SUCCESS;
}

5.2 拷貝構造函數中形參必須要用引用類型

如果拷貝構造函數中的形參不是引用類型

Maker3(const Maker3 m)
{cout<<"拷貝構造函數"<<endl;
}1.Maker3 m2(m1);
2.const Maker3 m = m1;
3.const Maker3 m(m1);
4.const Mkaer3 m = m1;
5.進入死循環

六、構造函數的分類和調用

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 按照參數分類:無參數  有參數Maker(){cout << "無參數構造" << endl;}Maker(int a){cout << "有參數構造" << endl;}// 按照類型:普通構造函數  拷貝構造函數Maker(const Maker& m){cout << "拷貝構造函數" << endl;}
};void test()
{Maker m;// 調用無參數構造函數Maker m1(10);// 調用有參數構造函數Maker m2(m1);// 調用拷貝構造函數Maker m3 = m2;// 調用拷貝構造函數Maker m4 = 10;// 調用有參構造
}int main()
{//cout << "hello" << endl;test();return EXIT_SUCCESS;
}

七、匿名對象

析構函數與構造函數同名。匿名對象的聲明周期在當前行

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 按照參數分類:無參數  有參數Maker(){cout << "無參數構造" << endl;}Maker(int a){cout << "有參數構造" << endl;}// 按照類型:普通構造函數  拷貝構造函數Maker(const Maker& m){cout << "拷貝構造函數" << endl;}~Maker(){cout << "析構函數" << endl;}
};void test01()
{Maker();// 匿名對象的生命周期在當前行cout << "test01函數結束" << endl;
}int main()
{test01();return EXIT_SUCCESS;
}

如果匿名對象前面有名字來接 那么就不是匿名對象 析構函數在函數結束之后 執行

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 按照參數分類:無參數  有參數Maker(){cout << "無參數構造" << endl;}Maker(int a){cout << "有參數構造" << endl;}// 按照類型:普通構造函數  拷貝構造函數Maker(const Maker& m){cout << "拷貝構造函數" << endl;}~Maker(){cout << "析構函數" << endl;}
};void test01()
{Maker();// 匿名對象的生命周期在當前行cout << "test01函數結束" << endl;Maker m1 = Maker();// 如果匿名對象前面有名字來接  那么就不是匿名對象  析構函數在函數結束之后 執行cout << "函數結束" << endl;
}int main()
{//cout << "hello" << endl;test01();return EXIT_SUCCESS;
}

八、拷貝構造函數的調用時機

8.1 對象以值的方式給函數參數

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {
public:// 按照參數分類:無參數  有參數Maker(){cout << "無參數構造" << endl;}Maker(int a){cout << "有參數構造" << endl;}// 按照類型:普通構造函數  拷貝構造函數Maker(const Maker& m){cout << "拷貝構造函數" << endl;}~Maker(){cout << "析構函數" << endl;}
};// 對象以值的方式給函數參數
void func(Maker m)
{// 以拷貝構造函數的形式進行賦值
}void test01()
{Maker m1;func(m1); // Maker m = m1
}int main()
{//cout << "hello" << endl;test01();return EXIT_SUCCESS;
}

8.2 vs debug模式下的函數局部對象返回值

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 按照參數分類:無參數  有參數Maker(){cout << "無參數構造" << endl;}Maker(int a){cout << "有參數構造" << endl;}// 按照類型:普通構造函數  拷貝構造函數Maker(const Maker& m){cout << "拷貝構造函數" << endl;}~Maker(){cout << "析構函數" << endl;}
};// 對象以值的方式給函數參數
void func(Maker m)
{// 以拷貝構造函數的形式進行賦值
}// 拷貝構造函數的方式調用
void test01()
{Maker m1;func(m1); // Maker m = m1
}// 函數的局部對象以值的方式從函數返回
Maker func2()
{// 局部對象Maker m;// 無參數構造cout << "局部對象的地址:" << &m << endl;return m;
}// vs 的Debug模式下  會調用拷貝構造  
void test03()
{Maker m1 = func2();// 調用拷貝構造函數   vs debug模式下  fun2()中的對象已經釋放   m1是新的地址cout << "m1的對象的地址" << &m1 << endl;  // 這里會發現  兩個地址不一樣  因為函數內部的局部對象 在函數結束之后  會釋放內存
}int main()
{//cout << "hello" << endl;test03();return EXIT_SUCCESS;
}

8.3 vs Release模式下函數局部對象以值的方式從函數返回

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:// 按照參數分類:無參數  有參數Maker(){cout << "無參數構造" << endl;}Maker(int a){cout << "有參數構造" << endl;}// 按照類型:普通構造函數  拷貝構造函數Maker(const Maker& m){cout << "拷貝構造函數" << endl;}~Maker(){cout << "析構函數" << endl;}
};// 對象以值的方式給函數參數
void func(Maker m)
{// 以拷貝構造函數的形式進行賦值
}// 拷貝構造函數的方式調用
void test01()
{Maker m1;func(m1); // Maker m = m1
}// 函數的局部對象以值的方式從函數返回
Maker func2()
{// 局部對象Maker m;// 無參數構造cout << "局部對象的地址:" << &m << endl;return m;
}// vs 的Relase模式下  不會調用拷貝構造  
void test03()
{Maker m1 = func2();// 未調用拷貝構造函數  地址相同   cout << "m1的對象的地址" << &m1 << endl;  // 這里會發現  兩個地址一樣  release為了節約內存  沒有釋放函數內部的對象
}int main()
{//cout << "hello" << endl;test03();return EXIT_SUCCESS;
}

九、構造函數的調用規則

9.1 如果程序員提供有參數構造,那么編譯器不會提供默認構造函數,但是會提供默認的拷貝構造函數

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker
{
public:Maker(int a){cout << "有參數構造函數" << endl;}};// 如果程序員提供了有參數構造函數   那么編譯器不會提供默認的構造函數  函數重載
void test01()
{//Maker m;// 報錯Maker m(10);// 調用有參構造函數Maker m1(m);// 編譯器提供默認的拷貝構造函數
}int main()
{test01();return EXIT_SUCCESS;
}

9.2 如果程序員提供了拷貝構造函數 那么編譯器不會提供默認的構造函數和默認的拷貝構造函數

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker
{
public:Maker(const Maker& m){cout << "拷貝構造函數" << endl;}};// 如果程序員提供了有參數構造函數   那么編譯器不會提供默認的構造函數  函數重載
void test01()
{//Maker m;// 報錯
}// 如果程序員提供了拷貝構造函數  那么編譯器不會提供默認構造函數和默認的拷貝構造函數
void test02()
{Maker m;
}int main()
{return EXIT_SUCCESS;
}

十、多個對象的構造函數和析構函數

10.1 多個對象的構造函數和析構函數的規則

  • 如果類有成員對象 那么先調用成員對象的構造函數 在調用本身的構造函數
  • 析構函數的調用順序相反
  • 成員對象的構造函數調用和定義順序一樣
  • 如果有成員對象 那么實例化對象時,必須保證成員對象的構造和析構能被調用
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class BMW
{
public:BMW(){cout << "BMW() 構造" << endl;}~BMW(){cout << "BMW析構函數" << endl;}
};class Buiker
{
public:Buiker(){cout << "Buiker() 構造" << endl;}~Buiker(){cout << "Buiker析構函數" << endl;}
};class Maker
{
public:Maker(){cout << "Maker構造" << endl;}~Maker(){cout << "maker析構函數" << endl;}private:BMW bmw;//成員對象Buiker bui;};void test01()
{// 如果類有成員對象  那么先調用成員對象的構造函數  在調用本身的構造函數// 析構函數的調用順序相反// 成員對象的構造函數調用和定義順序一樣// 如果有成員對象  那么實例化對象時,必須保證成員對象的構造和析構能被調用Maker m;
}int main()
{test01();return EXIT_SUCCESS;
}

10.2 多個對象初始化列表

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class BMW
{
public:BMW(int a){cout << "BMW() 構造" << a<<endl;}~BMW(){cout << "BMW析構函數" << endl;}
};class Buiker
{
public:Buiker(){cout << "Buiker() 構造" << endl;}~Buiker(){cout << "Buiker析構函數" << endl;}
};class Maker
{
public:// 初始化列表// 注意:初始化列表只能寫在構造函數Maker() :bmw(10){cout << "Maker構造" << endl;}~Maker(){cout << "maker析構函數" << endl;}private:BMW bmw;//成員對象Buiker bui;
};void test01()
{// 如果類有成員對象  那么先調用成員對象的構造函數  在調用本身的構造函數// 析構函數的調用順序相反// 成員對象的構造函數調用和定義順序一樣// 如果有成員對象  那么實例化對象時,必須保證成員對象的構造和析構能被調用Maker m;
}// 初始化列表是調用成員對象的指定構造函數
void test02()
{Maker m;
}int main()
{test02();return EXIT_SUCCESS;
}

或者:構造函數需要加上參數

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class BMW
{
public:BMW(int a){cout << "BMW() 構造" << a<<endl;}~BMW(){cout << "BMW析構函數" << endl;}
};class Buiker
{
public:Buiker(){cout << "Buiker() 構造" << endl;}~Buiker(){cout << "Buiker析構函數" << endl;}
};class Maker
{
public:// 初始化列表// 注意:初始化列表只能寫在構造函數// 構造函數可以寫參數  初始化直接寫入參數Maker(int a) :bmw(a){cout << "Maker構造" << endl;}~Maker(){cout << "maker析構函數" << endl;}private:BMW bmw;//成員對象Buiker bui;};// 初始化列表是調用成員對象的指定構造函數
void test02()
{Maker m(1);
}int main()
{test02();return EXIT_SUCCESS;
}

注意:如果使用了初始化列表,那么所有的構造函數都要寫初始化列表

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class BMW
{
public:BMW(int a){cout << "BMW() 構造" << a<<endl;}~BMW(){cout << "BMW析構函數" << endl;}
};class Buiker
{
public:Buiker(){cout << "Buiker() 構造" << endl;}~Buiker(){cout << "Buiker析構函數" << endl;}
};class Maker
{
public:// 初始化列表// 注意:初始化列表只能寫在構造函數// 構造函數可以寫參數  初始化直接寫入參數Maker(int a) :bmw(a){cout << "Maker構造" << endl;}// 拷貝構造函數也要協商初始化列表Maker(const Maker& m) :bmw(4){// 注意 如果使用了初始化列表 那么所有的構造函數都要寫}~Maker(){cout << "maker析構函數" << endl;}private:BMW bmw;//成員對象Buiker bui;};// 初始化列表是調用成員對象的指定構造函數
void test02()
{Maker m(1);
}int main()
{test02();return EXIT_SUCCESS;
}

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class BMW
{
public:BMW(int a){cout << "BMW() 構造" << a<<endl;}~BMW(){cout << "BMW析構函數" << endl;}
};class Buiker
{
public:Buiker(int b){cout << "Buiker() 構造" << endl;}~Buiker(){cout << "Buiker析構函數" << endl;}
};class Maker
{
public:// 初始化列表// 注意:初始化列表只能寫在構造函數// 構造函數可以寫參數  初始化直接寫入參數Maker(int a) :bmw(a),bui(1){cout << "Maker構造" << endl;}Maker(const Maker& m) :bmw(4),bui(1){// 注意 如果使用了初始化列表 那么所有的構造函數都要寫}~Maker(){cout << "maker析構函數" << endl;}private:BMW bmw;//成員對象Buiker bui;};// 初始化列表是調用成員對象的指定構造函數
void test02()
{Maker m(1);
}int main()
{test02();return EXIT_SUCCESS;
}

構造函數的參數填寫所有參數,初始化列表寫成參數

#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class BMW
{
public:BMW(int a){cout << "BMW() 構造" << a<<endl;}~BMW(){cout << "BMW析構函數" << endl;}
};class Buiker
{
public:Buiker(int b){cout << "Buiker() 構造" <<b<< endl;}~Buiker(){cout << "Buiker析構函數" << endl;}
};class Maker
{
public:// 初始化列表// 注意:初始化列表只能寫在構造函數// 構造函數可以寫參數  初始化直接寫入參數Maker(int a,int b) :bmw(a),bui(b){cout << "Maker構造" << endl;}Maker(const Maker& m) :bmw(4),bui(1){// 注意 如果使用了初始化列表 那么所有的構造函數都要寫}~Maker(){cout << "maker析構函數" << endl;}private:BMW bmw;//成員對象Buiker bui;};// 初始化列表是調用成員對象的指定構造函數
void test02()
{Maker m(1,2);
}int main()
{test02();return EXIT_SUCCESS;
}

構造函數和析構函數的調用順序。總結:

  • 初始化列表是干什么用的,指定調用成員對象的某個構造函數
  • 初始化列表只能寫在構造函數旁邊
  • 如果使用了初始化列表 那么所有的構造函數都要寫初始化列表
  • 如果有多個對象需要指定調用某個構造函數,需要使用逗號隔開
  • 可以使用對象的構造函數參數傳遞數值給成員對象的變量

十一、對象的深淺拷貝

11.1 默認的拷貝構造函數進行了簡單的賦值操作

拷貝構造函數只是復制值 沒有復制內存空間

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:Maker(int a, int b){id = a;age = b;}void print(){cout << "id = " << id << " age = " << age << endl;}private:int id;int age;};void test01()
{Maker a(1, 1);a.print();Maker b(a);// 拷貝構造函數  淺復制b.print();
}int main()
{test01();return EXIT_SUCCESS;
}

淺拷貝的問題:同一塊內存空間被釋放兩次

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:Maker(int a, int b){id = a;age = b;}void print(){cout << "id = " << id << " age = " << age << endl;}private:int id;int age;};class Student {
public:Student(const char* name, int Age){pName = (char*)malloc(strlen(name) + 1);// 申請堆區空間strcpy(pName,name);// 復制內容age = Age;}// 申請堆區空間 一定要釋放~Student(){if (pName != NULL){free(pName);pName = NULL;}}void print(){cout << "名字 = " << pName << "年紀 = " << age << endl;}private:char* pName;int age;
};void test01()
{Maker a(1, 1);a.print();Maker b(a);// 拷貝構造函數  淺復制b.print();
}void test02()
{Student a("xxx",1);a.print();// 復制的是地址// 導致同一塊內存地址空間被析構函數銷毀兩次Student b(a);b.print();
}int main()
{test02();return EXIT_SUCCESS;
}

11.2 深拷貝

重寫拷貝構造函數即可,本質上還是重新申請一塊堆區空間

#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)class Maker {public:Maker(int a, int b){id = a;age = b;}void print(){cout << "id = " << id << " age = " << age << endl;}private:int id;int age;};class Student {
public:Student(const char* name, int Age){pName = (char*)malloc(strlen(name) + 1);// 申請堆區空間strcpy(pName,name);// 復制內容age = Age;}// 重載拷貝構造函數Student(const Student& stu){cout << "自己的拷貝構造函數" << endl;// 重新申請一塊內存空間pName = (char*)malloc(sizeof(char) * (strlen(pName) + 1));// 拷貝數據strcpy(pName,stu.pName);age = stu.age;}// 申請堆區空間 一定要釋放~Student(){if (pName != NULL){free(pName);pName = NULL;}}void print(){cout << "名字 = " << pName << "年紀 = " << age << endl;}private:char* pName;int age;
};void test01()
{Maker a(1, 1);a.print();Maker b(a);// 拷貝構造函數  淺復制b.print();
}void test02()
{Student a("xxx",1);a.print();// 復制的是地址// 導致同一塊內存地址空間被析構函數銷毀兩次Student b(a);b.print();
}int main()
{test02();return EXIT_SUCCESS;
}

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/186855.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息