C++中的指针类型与构造函数,析构函数
1. 指針類型的作用
1.1 指針取出字節
-
任何類型的指針占用的空間大小都是相同的
(32位CPU是4字節;64位CPU是8字節) -
既然任何類型的指針占用的空間大小都是相同的,為什么指針還需要類型呢?
-
指針只是指向了一個內存地址,但是當存內存中取值的時候,系統不知道你要從當前指針指向的地址,取幾個字節,指定了指針的類型后,系統就知道取幾個字節了。
-
char類型取1個字節,short類型取2個字節,int類型去4個字節。
注意:
bit 是計算機最小的單位; 一個字節 = 8 bit;
一個十六進制符 需要四位才能表示出來;
0xf: 十六進制中的 一個16進制數, 比如 f , 需要用四位 二進制才能表示處理;
十六進制數 fff : 需要四位表示出來: 1111;
所以一個十六進制符占用的是 半個字節,
兩個十六進制符 才是占用的一個字節;
不同的指針類型,可以指示編譯器怎樣解釋特定地址上內存的內容以及該內存區域應該跨越多少內存單元。
如果一個int 型的指針:
尋址到1000 內存處,
那么在32 位機器上跨越的地址空間是1000~1003;
如果一個double 型的指針:
尋址到1000 內存處,
那么在32 位機器上跨越的地址空間是1000~1007;
1.2 指針的定義
指針p也是對象,它同樣有地址&p和存儲的值p,
只不過,p存儲的數據類型是數據的地址。
指針在定義的時候:
聲明該變量, 是指針類型的變量:
但是在使用的時候, 稱作是 解地址符: 意思是取出該指針中所保存的地址1, 找到該地址1, 取出地址1中的值;
int value_i = *p cout<< value_i<< endl;2. 構造函數
構造函數是指一個特殊的公共成員函數, 它的作用是在創建類的對象時會自動調用,從而用于構造類的對象;
構造函數的特點:
是指它的函數名稱 與所屬類的名稱相同, 從而編譯器知道這是類中一個特殊的成員函數;
構造函數不允許有返回類型, 除此之外, 構造函數和其他類的成員函數相似;
有個特點, 通常類名稱首字母大寫, 所以構造函數的首字母也會大寫;
從而函數名稱與類名稱是否相同, 函數名稱首字母是否大寫, 是否有無函數返回類型, 這三點,可以區分普通成員函數和構造函數;
所以,我們程序員在創建類的時候, 我們應該自己定義出構造函數;
在自己定義了構造函數后,
好處是,在初始化類的對象時, 可以直接對類中的成員變量賦值;
若沒有自定義構造函數,調用系統自動生成的構造函數時, 在初始化對象時,不可以直接對類中的成員變量賦值;
如果程序員沒有編寫構造函數,則 C++ 會自動提供一個,這個自動提供的構造函數永遠不會有人看到它,但是每當程序定義一個對象時,它會在后臺靜默運行。
構造函數的類型:
注意,一般構造函數可以有多種參數形式,
即一個類可以有多個普通構造函數,前提是參數的個數或者類型不同(C++的函數重載機制)。
但是, 默認構造函數只能有且只有一個;
程序員通常使用構造函數來初始化對象的成員變量。但實際上,它可以做任何正常函數可以做的事情。
舉個例來說明有 無自定義構造函數區別:
單鏈表中的構造函數:
因為上述結構體中, 自己定義了 構造函數:
所以在初始化的時候, 可以直接給變量賦值;
ListNode* head = new ListNode(5);而如果沒有自己定義構造函數, 則c++ 默認生成一個構造函數,
但是 使用默認生成的構造函數時, 在初始化的時候,不可以給變量賦值;
2.1 在類中定義構造函數
下面的程序包括一個名為 Demo 的類,其構造函數除了打印消息之外什么都不做。編寫它的目的就是為了演示構造函數何時執行。因為 Demo 對象是在兩個 cout 語句之間創建的,所以構造函數將在這兩個語句生成的輸出行之間打印它的消息。
// This program demonstrates when a constructor executes. #include <iostream> using namespace std; class Demo {public:Demo(){cout << "Now the constructor is running.\n";} }; int main() {cout << "This is displayed before the object is created. \n";Demo demoObj; // Define a Demo objectcout << "This is displayed after the object is created.\n";return 0; }程序中,將構造函數定義為類聲明中的內聯函數;
程序輸出結果為: This is displayed before the object is created. Now the constructor is running. This is displayed after the object is created.2.2 在類外定義構造函數
當然,像任何其他類成員函數一樣,也可以將其原型放在類聲明中,然后將其定義在類之外。
在這種情況下,需要添加:
但是由于構造函數的名稱與類名相同,所以名稱會出現兩次。
Demo:: Demo () // 構造函數 {cout << "Now the constructor is running. \n"; }2.3 重載構造函數
我們知道,當兩個或多個函數共享相同的名稱時,函數名稱被稱為重載。只要其形參列表不同,C++ 程序中可能存在具有相同名稱的多個函數。
任何類成員函數都可能被重載,包括構造函數。例如,某個構造函數可能需要一個整數實參,而另一個構造函數則需要一個 double,甚至可能會有第三個構造函數使用兩個整數。只要每個構造函數具有不同的形參列表,則編譯器就可以將它們分開。
下面的程序聲明并使用一個名為 Sale 的類,它有兩個構造函數。第一個構造函數的形參接受銷售稅率;第二個構造函數是免稅銷售,沒有形參。它將稅率設置為 0。這樣一個沒有形參的構造函數稱為默認構造函數。
#include <iostream> #include <iomanip> using namespace std; // Sale class declaration class Sale {private:double taxRate;public:Sale(double rate) // Constructor with 1 parameter{taxRate = rate; // handles taxable sales}Sale () // Default constructor{taxRate = 0.0 // handles tax-exempt sales}double calcSaleTotal(double cost){double total = cost + cost*taxRate;return total;} }; int main() {Sale cashier1(.06); // Define a Sale object with 6% sales taxSale cashier2; // Define a tax-exempt Sale object// Format the outputcout << fixed << showpoint << setprecision (2);// Get and display the total sale price for two $24.95 salescout << "With a 0.06 sales tax rate, the total of the $24.95 sale is $ \n";cout << cashier1.calcSaleTotal(24.95) << endl;cout << "\n On a tax-exempt purchase, the total of the $24.95 sale is, of course, $\n";cout << cashier2.calcSaleTotal(24.95) << endl;return 0; }輸出結果:
With a 0.06 sales tax rate, the total of the $24.95 sale is $26.45On a tax-exempt purchase, the totalof the $24.95 sale is, of course, $24.95注意看此程序如何定義的兩個 Sale 對象:
Sale cashier1(.06); Sale cashier2;在 cashier1 的名稱后面有一對括號,用于保存值,發送給有一個形參的構造函數。但是,在 Cashier2 的名稱后面就沒有括號,它不發送任何參數。在 C++ 中,當使用默認構造函數定義對象時,不用傳遞實參,所以不能有任何括號,即:
Sale cashier2 () ; // 錯誤 Sale cashier2; // 正確構造函數和默認構造函數的 區別是:
當類的對象創建時, 但沒有給對象的成員變量 初始化賦值時,
則編譯器,會自動調用默認構造函數;
通常情況下,默認構造函數沒有形參;
2.3 無形參的默認構造函數
為了創建不傳遞任何參數的對象,必須有一個不需要參數的構造函數,也就是默認構造函數。
如果沒有這樣一個默認構造函數,那么當程序嘗試創建一個對象而不傳遞任何參數時,它將不會編譯,這是因為必須有一個構造函數來創建一個對象。
如果程序員沒有為類編寫任何構造函數,則編譯器將自動為其創建一個默認構造函數。
但是,當程序員編寫了一個或多個構造函數時,即使所有這些構造函數都是有參數的,編譯器也不會創建一個默認構造函數,所以程序員有責任這樣做。
那么,在設計一個具有構造函數的類時,應該包括一個默認構造函數,這在任何時候都會被認為是一個很好的編程實踐。
類可能有許多構造函數,但只能有一個默認構造函數。
這是因為:如果多個函數具有相同的名稱,則在任何給定時刻,編譯器都必須能夠從其形參列表中確定正在調用哪個函數。
它使用傳遞給函數的實參的數量和類型來確定要調用的重載函數。
因為一個類名稱只能有一個函數能夠接受無參數,所以只能有一個默認構造函數。
2.3 有形參的默認構造函數
一般情況下,就像在 Sale 類中那樣,默認構造函數沒有形參。
Sale 類需要一個默認構造函數來處理免稅銷售的情況,但是其他類可能并不需要這樣一個構造函數。
例如,如果通過類創建的對象總是希望將實參傳遞給構造函數。
所以有另一種的默認構造函數,其所有形參都具有默認值,所以,它也可以無實參調用。
如果創建了一個接受無實參的構造函數,同時又創建了另外一個有參數但允許所有參數均為默認值的構造函數,那么這將是一個錯誤,因為這實際上是創建了兩個“默認”構造函數。以下語句就進行了這種非法的聲明:
class Sale //非法聲明, 不應該存在兩個 默認構造函數; {private:double taxRate;public:Sale() //無實參的默認構造函數{taxRate = 0.05;}Sale (double r = 0.05) //有默認實參的默認構造函數{taxRate = r;}double calcSaleTotal(double cost){double total = cost + cost * taxRate;return total;}; };可以看到,第一個構造函數沒有形參,第二個構造函數有一個形參,但它有一個默認實參。如果一個對象被定義為沒有參數列表,那么編譯器將無法判斷要執行哪個構造函數。
2.4 各自構造函數的寫法
2.4.1 有形參的普通構造函數
具有形參的普通構造函數,可以有兩種寫法:
構造函數名稱( 初始化值1, 初始化值2 ):成員變量1(初始化值1),… 成員變量n(初始化值n){}
class Student{ public: int m_age; int m_score;// 列表方式,定義構造函數; Student(int age, int score): m_age(age), m_socre(score){}};正常函數的賦值
C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。也就是說采用初始化列表的話,構造函數本體實際上不需要有任何操作,因此效率更高。
2.4.1 無形參的構造函數
函數內部賦值
Student(){m_age = 0;m_score = 0; }3. 拷貝構造函數
拷貝構造函數 為類對象本身的引用,
根據一個已經存在的對象復制出一個新的對象,一般在函數中,會將已經存在對象的數據成員的值復制一份到新創建的對象中;
注意:若沒有顯示定義復制構造函數,則系統會默認創建一個復制構造函數,當類中有指針成員時,由系統默認創建的復制構造函數會存在“淺拷貝”的風險,因此必須顯示定義復制構造函數。
-
淺拷貝指的是在對對象復制時,只對對象中的數據成員進行簡單的賦值,若存在動態成員,就是增加一個指針,指向原來已經存在的內存。這樣就造成兩個指針指向了堆里的同一個空間。當這兩個對象生命周期結束時,析構函數會被調用兩次,同一個空間被兩次free,造成野指針。
-
深拷貝就是對于對象中的動態成員,不是簡單的賦值,而是重新分配空間。
4. 析構函數
析構函數是具有與類相同名稱的公共成員函數,前面帶有波浪符號(?)。例如,Rectangle 類的析構函數將被命名為 ?Rectangle。
當對象被銷毀時,會自動調用析構函數。在創建對象時,構造函數使用某種方式來進行設置,那么當對象停止存在時,析構函數也會使用同樣的方式來執行關閉過程。
4.1 析構函數的特點
除了需要知道在對象被銷毀時會自動調用析構函數外,還應注意以下事項:
- 像構造函數一樣,析構函數沒有返回類型。
- 析構函數不能接收實參,因此它們從不具有形參列表。
- 由于析構函數不能接收實參,因此只能有一個析構函數。
例如,當具有對象的程序停止執行或從創建對象的函數返回時,就會發生這種情況。
下面的程序顯示了一個具有構造函數和析構函數的簡單類。它說明了在程序執行過程中這兩個函數各自被調用的時間:
//This program demonstrates a destructor. #include <iostream> using namespace std; class Demo {public:Demo(); // Constructor prototype~Demo(); // Destructor prototype }; Demo::Demo() // Constructor function definition {cout << "An object has just been defined,so the constructor" << " is running.\n"; } Demo::~Demo() // Destructor function definition {cout << "Now the destructor is running.\n"; } int main() {Demo demoObj; // Declare a Demo object;cout << "The object now exists, but is about to be destroyed.\n";return 0; } 程序輸出結果: An object has just been defined, so the constructor is running. The object now exists, but is about to be destroyed. Now the destructor is running.總結
以上是生活随笔為你收集整理的C++中的指针类型与构造函数,析构函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机关闭系统默认共享,win10如何关
- 下一篇: Uptime-Kuma 一个轻量的开源监