首頁 > 軟體

一文帶你分清C++的定義,宣告和初始化

2022-03-03 19:01:51

定義

變數的定義用於為變數分配儲存空間,還可以為變數指定初始值。

int units_sold;
double sales_price, avg_price;
std::string title;
Sales_item curr_book; // class Sales_item

初始化

C++ 支援兩種初始化變數的形式:複製初始化和直接初始化。複製初始化語法用等號(=),直接初始化則是把初始化式放在括號中。

int ival(1024); // direct-initialization
int ival = 1024; // copy-initialization

初始化不是賦值。初始化指建立變數並給它賦初始值,而賦值則是擦除物件的當前值並用新值代替。

當定義沒有初始化式的變數時,系統有時候會幫我們初始化變數。

1.內建型別變數

(Built-in Types,即int,float,double,void,char,bool等。注意string是標準庫定義的型別,不是內建型別)

在函數體外定義的變數都初始化成 0,在函數體裡定義的內建型別變數不進行自動初始化。

2.類

類通過定義一個或多個建構函式來控制類物件的初始化。建立類型別的新物件,都要執行建構函式,保證每個物件的資料成員具有合適的初始值。

建構函式可以包含一個建構函式初始化列表,以一個冒號開始,接著是一個以逗號分隔的資料成員列表,每個資料成員後面跟一個放在圓括號中的初始化式。與任意的成員函數一樣,建構函式可以定義在類的內部或外部。

建構函式初始化只在建構函式的定義中而不是宣告中指定。

//將 isbn 成員初始化為 book 形參的值,將 units_sold 和 revenue 初始化為 0。
Sales_item::Sales_item(const string &book):
          isbn(book), units_sold(0), revenue(0.0) { }

如果沒有提供初始化式,那麼就會使用預設建構函式。如果類具有預設建構函式,那麼就可以在定義該類的變數時不用顯式地初始化變數。例如,string 類定義了預設建構函式來初始化 string 變數為空字串。

string a;
cout << "a: " << a <<endl;

輸出:

a: 

此外,省略初始化列表在建構函式的函數體內對資料成員賦值是合法的。

Sales_item::Sales_item(const string &book)
     {
         isbn = book;
         units_sold = 0;
         revenue = 0.0;
     }

不管成員是否在建構函式初始化列表中顯式初始化,類型別的資料成員總是在初始化階段初始化。初始化發生在計算階段開始之前。(也就是函數體執行以前)→ 這裡似乎有些難以理解,通過後文的範例也許你能明白

在建構函式初始化列表中沒有顯式提及的每個成員,使用與初始化變數相同的規則來進行初始化。執行該型別的預設建構函式,來初始化類型別的資料成員。內建或複合型別的成員的初始值依賴於物件的作用域:在區域性作用域中這些成員不被初始化,而在全域性作用域中它們被初始化為 0。

未初始化的變數

區域性作用域的內建型別變數將不被自動初始化,這可能導致其成為未初始化的變數。這是常見的程式錯誤,但編譯器無法檢測出所有未初始化變數的使用。→ 你肯定可以理解這可能導致的災難性後果了(這竟然不可以執行,為什麼呢?這竟然可以執行,為什麼呢?.jpg)再稍微解釋一下原因:問題出在未初始化的變數事實上都有一個值。編譯器把該變數放到記憶體中的某個位置,而把這個位置的無論哪種位元型樣都當成是變數初始的狀態。當被解釋成整型值時,任何位元型樣都是合法的值——雖然這個值不可能是程式設計師想要的。因為這個值合法,所以使用它也不可能會導致程式崩潰。可能的結果是導致程式錯誤執行和/或錯誤計算。

宣告

宣告用於向程式表明變數的型別和名字。定義也是宣告:當定義變數時我們宣告了它的型別和名字。

可以通過使用extern關鍵字宣告變數名而不定義它。extern 宣告不是定義,也不分配儲存空間。事實上,它只是說明變數定義在程式的其他地方。程式中變數可以宣告多次,但只能定義一次。

extern int i; // declares but does not define i
int i; // declares and defines i

只有當宣告也是定義時,宣告才可以有初始化式,因為只有定義才分配儲存空間。初始化式必須要有儲存空間來進行初始化。如果宣告有初始化式,那麼它可被當作是定義,即使宣告標記為 extern:

extern int i = 10; //defines i

在 C++ 語言中,變數必須且僅能定義一次,而且在使用變數之前必須定義或宣告變數。

範例

#include <iostream>
#include <string>
using namespace std;

//類x的宣告
//如果這一部分放在main()函數後面,報錯:error: 'x' was not declared in this scope
//在實際工程中,這部分宣告將放在標頭檔案(.h)中,而建構函式及成員函數的定義則放在.cpp檔案中
class x{
public:
    x(int a, int b, string c);
    void print_data();
private:
    //類資料成員的變數名最好在開頭加一個字母m(即member)
    int ma; 
    int mb;
    string mc;

};

int main(){
    int a1(2); //直接初始化
    int b1 = 3; //複製初始化
    string c1; //預設建構函式初始化string變數為空字串
    c1 = "dwkw"; //賦值
    x data(a1, b1, c1); //呼叫建構函式初始化
    return 0;
    print_data();
}

//建構函式定義
x::x(int a, int b, string c):ma(a), mb(b), mc(c){}
//成員函數定義
void x::print_data(){
    cout << "ma: " << ma << endl;
    cout << "mb: " << mb << endl;
    cout << "mc: " << mc << endl;
}

輸出:

ma: 2
mb: 3
mc: dwkw

宣告時提供初值

如果在類的宣告中就對資料成員提供初值,而不在初始化列表中提供,程式可以執行,輸出ma的值為1。

class x{
public:
    x(int a, int b, string c);
    void print_data();
private:
    int ma = 1; //宣告時提供初值
    int mb;
    string mc;
};
//去掉初始化列表
x::x(int a, int b, string c):mb(b), mc(c){}

這一做法在早期版本不予支援,但從c++11就可以了。[2]

不過這破壞了類的抽象性,並不建議這樣做。

檢視c++版本的方法:[3]

cout << __cplusplus << endl; //輸出c++版本

在建構函式內賦初值,而不用列表

前面提到省略初始化列表在建構函式的函數體內對資料成員賦值是合法的。

//去掉初始化列表,在建構函式體內賦值
//其它程式碼保持不變
x::x(int a, int b, string c){
    cout << "賦值前: " << endl;
    print_data();
    cout << "賦值後: " << endl;
    ma = 4;
    mb = 5;
    mc = "ser";
}

輸出:

賦值前: 
ma: 4199744
mb: 0
mc: 
賦值後: 
ma: 4
mb: 5
mc: ser

實際上我就沒寫初始化列表,但系統它就會在這裡執行初始化。總之就會在執行建構函式體內的語句之前初始化(如果它可以自動初始化),即使根本沒寫初始化列表。→ 嘖,我就像在說繞口令,希望你能明白我的意思

但是ma和mb都是區域性作用域(我不確定類作用域是否是區域性作用域,但從輸出來看,ma不是0,所以應該沒有能夠初始化)的內建型別變數,不進行自動初始化;mc有預設建構函式,自動初始化為空字串。

而後,執行建構函式體內部的語句,將對ma和mb進行初始化(我想這裡應該是初始化而不是賦值),對mc賦值。

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!  


IT145.com E-mail:sddin#qq.com