完全弄懂C++中的构造与析构函数
類的構造函數
帶參數的構造函數
使用初始化列表來初始化字段
類的析構函數
構造函數與析構函數的特點
顯式調用析構函數
拷貝構造函數
類的構造函數
類的構造函數是類的一種特殊的成員函數,它會在每次創建類的新對象時執行。
構造函數的名稱與類的名稱是完全相同的,并且不會返回任何類型,也不會返回 void。構造函數可用于為某些成員變量設置初始值。 推薦群:C++大學技術協會:145655849
下面的實例有助于更好地理解構造函數的概念:
#include
using namespace std;
class CLine
{
public:
void setLength(double len);
double getLength(void);
CLine(); // 這是構造函數
private:
double m_dblength;
};
// 成員函數定義,包括構造函數
CLine::CLine(void)
{
cout << “Object is being created” << endl;
}
void CLine::setLength(double len)
{
m_dblength = len;
}
double CLine::getLength(void)
{
return m_dblength;
}
// 程序的主函數
int main(int argc, char* argv[])
{
CLine line;
}
xxxxxxxxxx42 1#include 2?3using namespace std;4?5class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(); // 這是構造函數11?12private:13 double m_dblength;14};15?16// 成員函數定義,包括構造函數17CLine::CLine(void)18{19 cout << “Object is being created” << endl;20}21?22void CLine::setLength(double len)23{24 m_dblength = len;25}26?27double CLine::getLength(void)28{29 return m_dblength;30}31// 程序的主函數32int main(int argc, char* argv[])33{34 CLine line;35?36 // 設置長度37 line.setLength(6.0);38 cout << "Length of line : " << line.getLength() << endl;39?40 system(“pause”);41 return 0;42}
帶參數的構造函數
默認的構造函數沒有任何參數,但如果需要,構造函數也可以帶有參數。這樣在創建對象時就會給對象賦初始值,如下面的例子所示:
#include
using namespace std;
class CLine
{
public:
void setLength(double len);
double getLength(void);
CLine(double len); // 這是構造函數
private:
double m_dblength;
};
// 成員函數定義,包括構造函數
CLine::CLine(double len)
{
cout << "Object is being created, length = " << len << endl;
m_dblength = len;
}
void CLine::setLength(double len)
{
m_dblength = len;
}
double CLine::getLength(void)
{
return m_dblength;
}
// 程序的主函數
int main(int argc, char* argv[])
{
CLine line(10.0);
}
xxxxxxxxxx45 1#include 2?3using namespace std;4?5class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(double len); // 這是構造函數11?12private:13 double m_dblength;14};15?16// 成員函數定義,包括構造函數17CLine::CLine(double len)18{19 cout << "Object is being created, length = " << len << endl;20 m_dblength = len;21}22?23void CLine::setLength(double len)24{25 m_dblength = len;26}27?28double CLine::getLength(void)29{30 return m_dblength;31}32// 程序的主函數33int main(int argc, char* argv[])34{35 CLine line(10.0);36?37 // 獲取默認設置的長度38 cout << "Length of line : " << line.getLength() << endl;39 // 再次設置長度40 line.setLength(6.0);41 cout << "Length of line : " << line.getLength() << endl;42?43 system(“pause”);44 return 0;45}
使用初始化列表來初始化字段
使用初始化列表來初始化字段:
CLine::CLine( double len): m_dblength(len)
{
cout << "Object is being created, length = " << len << endl;
}
xxxxxxxxxx4 1CLine::CLine( double len): m_dblength(len)2{3 cout << "Object is being created, length = " << len << endl;4}
上面的語法等同于如下語法:
CLine::CLine( double len)
{
m_dblength = len;
cout << "Object is being created, length = " << len << endl;
}
xxxxxxxxxx5 1CLine::CLine( double len)2{3 m_dblength = len;4 cout << "Object is being created, length = " << len << endl;5}
假設有一個類 CVolume,具有多個字段 X、Y、Z 等需要進行初始化,同理地,您可以使用上面的語法,只需要在不同的字段使用逗號進行分隔,如下所示:
CVolume::CVolume( double a, double b, double c): X(a), Y(b), Z?
{
…
}
xxxxxxxxxx4 1CVolume::CVolume( double a, double b, double c): X(a), Y(b), Z?2{3 …4}
類的析構函數
類的析構函數是類的一種特殊的成員函數,它會在每次刪除所創建的對象時執行。
析構函數的名稱與類的名稱是完全相同的,只是在前面加了個波浪號(~)作為前綴,它不會返回任何值,也不能帶有任何參數。析構函數有助于在跳出程序(比如關閉文件、釋放內存等)前釋放資源。
下面的實例有助于更好地理解析構函數的概念:
#include
using namespace std;
class CLine
{
public:
void setLength(double len);
double getLength(void);
CLine(); // 這是構造函數聲明
~CLine(); // 這是析構函數聲明
private:
double m_dblength;
};
// 成員函數定義,包括構造函數
CLine::CLine(void)
{
cout << “Object is being created” << endl;
}
CLine::~CLine(void)
{
cout << “Object is being deleted” << endl;
}
void CLine::setLength(double len)
{
m_dblength = len;
}
double CLine::getLength(void)
{
return m_dblength;
}
// 程序的主函數
int main()
{
CLine line;
}
xxxxxxxxxx47 1#include 2?3using namespace std;4?5class CLine6{7public:8 void setLength(double len);9 double getLength(void);10 CLine(); // 這是構造函數聲明11 ~CLine(); // 這是析構函數聲明12?13private:14 double m_dblength;15};16?17// 成員函數定義,包括構造函數18CLine::CLine(void)19{20 cout << “Object is being created” << endl;21}22CLine::~CLine(void)23{24 cout << “Object is being deleted” << endl;25}26?27void CLine::setLength(double len)28{29 m_dblength = len;30}31?32double CLine::getLength(void)33{34 return m_dblength;35}36// 程序的主函數37int main()38{39 CLine line;40?41 // 設置長度42 line.setLength(6.0);43 cout << "Length of line : " << line.getLength() << endl;44?45 //system(“pause”);46 return 0;47}
構造函數與析構函數的特點
對于構造函數而言:
與類同名
沒有返回值
會在變量的生命周期開始時被調用
構造函數不能被顯示調用
可以重載
對于析構函數而言:
名稱為~類名()
沒有返回值
會在變量的生命周期結束時調用
可以顯式調用(一般不這樣做)
不能重載
關于自動調用時機的總結:
全局變量:構造函數在main之前調用,析構函數在main之后調用
局部變量:構造函數在進去函數開始時調用,析構函數在return之前調用
靜態局部變量:構造函數在第一次進入函數時調用,析構函數在main之后調用
堆中的變量:構造和析構的調用時機由程序員自己控制,與new和delete什么時候被調用有關
默認構造:當需要構造函數(如有成員是對象,或者繼承關系),而類中有沒有實現構造函數。編譯器會自動實現一個默認構造。
默認析構:同理,有需要時,編譯器會偷偷實現一個默認析構,用于調用基類及成員的析構。
顯式調用析構函數
顯式調用的時候,析構函數相當于的一個普通的成員函數
編譯器隱式調用析構函數,如分配了堆內存,顯式調用析構的話引起重復釋放堆內存的異常
把一個對象看作占用了部分棧內存,占用了部分堆內存(如果申請了的話),這樣便于理解這個問題。系統隱式調用析構函數的時候,會加入釋放棧內存的動作(而堆內存則由用戶手工的釋放)。用戶顯式調用析構函數的時候,只是單純執行析構函數內的語句,不會釋放棧內存,摧毀對象。
拷貝構造函數
拷貝構造函數是一種特殊的構造函數,它在創建對象時,是使用同一類中之前創建的對象來初始化新創建的對象??截悩嬙旌瘮低ǔS糜?#xff1a;
通過使用另一個同類型的對象來初始化新創建的對象。
復制對象把它作為參數傳遞給函數。
復制對象,并從函數返回這個對象。
如果在類中沒有定義拷貝構造函數,編譯器會自行定義一個。如果類帶有指針變量,并有動態內存分配,則它必須有一個拷貝構造函數??截悩嬙旌瘮档淖畛R娦问饺缦?#xff1a;
classname(const classname &obj)
{
//構造函數的主體
}
xxxxxxxxxx4 1classname(const classname &obj)2{3 //構造函數的主體4}
在此,obj是一個對象引用,該對象是用于初始化另一個對象的。
#include
using namespace std;
class CLine
{
public:
int getLength(void);
CLine(int len); // 簡單的構造函數
CLine(const CLine &obj); // 拷貝構造函數
~CLine(); // 析構函數
private:
int* ptr;
};
// 成員函數定義,包括構造函數
CLine::CLine(int len)
{
cout << “調用構造函數” << endl;
// 為指針分配內存
ptr = new int;
*ptr = len;
}
CLine::CLine(const CLine &obj)
{
cout << “調用拷貝構造函數并為指針 ptr 分配內存” << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
CLine::~CLine(void)
{
cout << “釋放內存” << endl;
delete ptr;
}
int CLine::getLength(void)
{
return *ptr;
}
void display(CLine obj)
{
cout << "line 大小 : " << obj.getLength() << endl;
}
// 程序的主函數
int main(int argc, char* argv[])
{
CLine line(10);
}
xxxxxxxxxx56 1#include 2?3using namespace std;4?5class CLine6{7public:8 int getLength(void);9 CLine(int len); // 簡單的構造函數10 CLine(const CLine &obj); // 拷貝構造函數11 ~CLine(); // 析構函數12?13private:14 int* ptr;15};16?17// 成員函數定義,包括構造函數18CLine::CLine(int len)19{20 cout << “調用構造函數” << endl;21 // 為指針分配內存22 ptr = new int;23 *ptr = len;24}25?26CLine::CLine(const CLine &obj)27{28 cout << “調用拷貝構造函數并為指針 ptr 分配內存” << endl;29 ptr = new int;30 *ptr = *obj.ptr; // 拷貝值31}32?33CLine::~CLine(void)34{35 cout << “釋放內存” << endl;36 delete ptr;37}38int CLine::getLength(void)39{40 return ptr;41}42?43void display(CLine obj)44{45 cout << "line 大小 : " << obj.getLength() << endl;46}47?48// 程序的主函數49int main(int argc, char argv[])50{51 CLine line(10);52?53 display(line);54?55 return 0;56}
下面的實例對上面的實例稍作修改,通過使用已有的同類型的對象來初始化新創建的對象:
#include
using namespace std;
class CLine
{
public:
int getLength(void);
CLine(int len); // 簡單的構造函數
CLine(const CLine &obj); // 拷貝構造函數
~CLine(); // 析構函數
private:
int *ptr;
};
// 成員函數定義,包括構造函數
CLine::CLine(int len)
{
cout << “調用構造函數” << endl;
// 為指針分配內存
ptr = new int;
*ptr = len;
}
CLine::CLine(const CLine &obj)
{
cout << “調用拷貝構造函數并為指針 ptr 分配內存” << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
CLine::~CLine(void)
{
cout << “釋放內存” << endl;
delete ptr;
}
int CLine::getLength(void)
{
return *ptr;
}
void display(CLine obj)
{
cout << "line 大小 : " << obj.getLength() << endl;
}
// 程序的主函數
int main(int argc, char* argv[])
{
CLine line1(10);
}
?x 1#include 2?3using namespace std;4?5class CLine6{7public:8 int getLength(void);9 CLine(int len); // 簡單的構造函數10 CLine(const CLine &obj); // 拷貝構造函數11 ~CLine(); // 析構函數12?13private:14 int *ptr;15};16?17// 成員函數定義,包括構造函數18CLine::CLine(int len)19{20 cout << “調用構造函數” << endl;21 // 為指針分配內存22 ptr = new int;23 *ptr = len;24}25?26CLine::CLine(const CLine &obj)27{28 cout << “調用拷貝構造函數并為指針 ptr 分配內存” << endl;29 ptr = new int;30 *ptr = *obj.ptr; // 拷貝值31}32?33CLine::~CLine(void)34{35 cout << “釋放內存” << endl;36 delete ptr;37}38int CLine::getLength(void)39{40 return ptr;41}42?43void display(CLine obj)44{45 cout << "line 大小 : " << obj.getLength() << endl;46}47?48// 程序的主函數49int main(int argc, char argv[])50{51 CLine line1(10);52?53 CLine line2 = line1; // 這里也調用了拷貝構造函數54?55 display(line1);56 display(line2);57?58 return 0;59}
拷貝構造函數是一種特殊的構造函數,具有單個形參,該形參(常用const修飾)是對該類類型的引用。當定義一個新對象并用一個同類型的對象對它進行初始化時,將顯示使用拷貝構造函數。當該類型的對象傳遞給函數或從函數返回該類型的對象時,將隱式調用拷貝構造函數。
C++支持兩種初始化形式:
拷貝初始化 int a = 5; 和直接初始化 int a(5); 對于其他類型沒有什么區別,對于類類型直接初始化直接調用實參匹配的構造函數,拷貝初始化總是調用拷貝構造函數,也就是說:
CMyclass x(2); //直接初始化,調用構造函數
CMyclass y = x; //拷貝初始化,調用拷貝構造函數
1CMyclass x(2); //直接初始化,調用構造函數2CMyclass y = x; //拷貝初始化,調用拷貝構造函數
必須定義拷貝構造函數的情況:
只包含類類型成員或內置類型(但不是指針類型)成員的類,無須顯式地定義拷貝構造函數也可以拷貝;有的類有一個數據成員是指針,或者是有成員表示在構造函數中分配的其他資源,這兩種情況下都必須定義拷貝構造函數。
什么情況使用拷貝構造函數:
類的對象需要拷貝時,拷貝構造函數將會被調用。以下情況都會調用拷貝構造函數:
一個對象以值傳遞的方式傳入函數體
一個對象以值傳遞的方式從函數返回
一個對象需要通過另外一個對象進行初始化。
推薦群:C++大學技術協會:145655849
總結
以上是生活随笔為你收集整理的完全弄懂C++中的构造与析构函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LeetCode笔记】207. 课程表
- 下一篇: 【LeetCode笔记】剑指 Offer