深入理解struct
在C語言中,我們通常使用struct來表示不同數據類型的結合。當然我們也可以在struct中定義函數,在C++中,這是允許的但是不提倡使用,因為有一個比它更好使用的復雜數據類型,叫做類(這在稍後做出介紹)。
使用struct的時候有一個問題:在進行一個比較大的項目工程的時候,我們的數據結構的定義和使用可能在不同的文件中,當我們修改了數據結構中的某個成員,那麼,使用該數據結構的函數必須修改,而我們並不知道拿下函數使用該種數據結構,這時候我們怎麼做?答案很簡單,在函數定義的時候,讓該函數屬於該數據結構,在再次查找修改函數的時候,只要屬於該結構的函數就進行修改就好了,代碼如下:
聲明數據結構:
struct Time{
int hour;
int minute;
int second;
void set_time(int h, int m, int s);
void tick();
void show();
void run();
};
定義函數:
void Time::set_time(int h, int m, int s){}
void Time::tick(){}
void Time::show(){}
void Time::run(){}
補充:成員運算符:表示某個變量的成員,‘.’;表示某個類型的成員,“::”;
類的引出
這樣定義就萬事大吉了嗎?我們如果在其他函數中試圖訪問該結構中的成員變量(如:hour、minute、second)是成功的,這樣我們就有可能無意中修改了數據結構中的某個成員變量的值,當其他函數在使用成員變量的時候,就會使用該可能非法的數據,這樣我們就會想到只要讓這些成員變量變成私有的就好了,這樣除了結構中定義的函數外,其他方式都不可以訪問到該成員,這就出現了我們C++常使用的一種數據結構——類。我們進行如下修改。
聲明數據結構:
class Time{
int hour;
int minute;
int second;
void set_time(int h, int m, int s);
void tick();
void show();
void run();
};
定義函數:
void Time::set_time(int h, int m, int s){}
void Time::tick(){}
void Time::show(){}
void Time::run(){}
如果我們就止步於上面的數據結構,我們定義一個上述類的對象,當我們通過對象調用函數的時候,會發現編譯不會通過,提示我們“你訪問的對象是私有的”。這裡就要說明class和struct最大的區別:struct中的所有成員默認是公開的(public),即任意一個函數都可以進行訪問;class中的所有成員默認是私有的(private),即除了class中的成員函數之外,其他方式不可以訪問。
我們使用class而不是用struct的原因是因為,我們想讓成員變量變成私有的,實現基本數據封裝。但是對於成員函數的調用接口我們不必要私有,因為我們創建的對象,要通過成員函數完成項目任務,所以我們應讓成員函數的接口公開(public),這就是我們常說的封裝。接著進行如下更改,讓成員函數變量公有的(public)。
1234567891011121314151617 聲明數據結構:
class Time{
private:
int hour;
int minute;
int second;
public:
void set_time(int h, int m, int s);
void tick();
void show();
void run();
};
定義函數:
void Time::set_time(int h, int m, int s){}
void Time::tick(){}
void Time::show(){}
void Time::run(){}
在這裡我們要說說class中成員變量的訪問權限:
(1)private:私有的。這是class中成員變量默認的訪問權限,這種權限的成員變量,只有該類中成員函數可以訪問,子類中的成員函數不可以訪問,其他方式也不可以訪問。(子類是繼承中的概念,在講繼承的時候再談這個話題,先了解就好)。
(2)protected:保護的。該class中的成員函數可以訪問,子類中的成員函數也可以訪問,但是其他方式不可以訪問。
(3)public:公開的。該class中的成員函數可以訪問,子類中的成員函數也可以訪問,其他方式亦可以訪問。
構造函數和析構函數
對於class中的成員函數,我們可以自己寫一個函數進行初始化,比如set函數,將每個成員變量進行初始化賦值。但是每次創建一個新類都要調用該種函數進行成員的初始化,這大大加大了我們創建使用類的代價,為了讓我們每次創建一個對象的時候,都可以自動調用某個函數進行成員變量的初始化,我們可以使用類中重要的成員函數——構造函數(構造函數無返回值,並且函數名與類名一致),它可以在創建對象的時候自動調用,根據創建對象時傳入的初始化參數進行成員變量的初始化。
構造函數的格式:Class_name(parameters){}
構造函數的初始化有兩種方式:
(1)通過在構造函數內部進行賦值進行初始化,常量不能被賦值,只能使用初始化列表。
(2)通過初始化列表進行初始化,如果成員變量是數組或結構不能使用初始化列表進行初始化。
構造函數的重載:有時候我們需要定義多個構造函數,因為我們可能基於不同的需求對成員變量的初始化操作不同,這就是構造函數的重載(函數重載:函數名相同,但是函數參數不同。系統可以根據傳入參數的不同來調用不同的函數)。
析構函數:存在構造函數也就會存在析構函數,析構函數就是在該對象即將被釋放的時候做收尾動作,析構函數一定沒有參數列表,所以析構函數不可以重載。
析構函數規則:~Class_name(){};
class A{
int n;
double d;
public : A():n(0),d(0.0){ // constructor_init1
}
A(int n){ // constructor_init2
this->n = n;
d = 0.0;
}
void show(){
cout << "n = " << n << ", d = " << d << endl;
}
~A(){
cout << “~A()” << endl;
}
};
int main(int ac, char *av[])
{
A a1; // use constructor_init1
a1.show();
A a2(100); // use constructor_init2
(*this).show();
return 0;
}
this指針:this指針是系統自動定義的,用來保存結構變量的地址。當我們創建一個對象的時候,系統就會自動將該對象對應的類的成員保存在this中,我們可以通過this->mem_val或this->mem_func,來訪問該成員。在一個成員函數中,可以不用寫this,表示默認使用該成員函數所在類的成員變量。
構造函數總結:
(1)構造函數不同寫返回值,其函數名與類名一致。
(2)構造函數在每個對象創建的時候都不被自動調用一次。
構造函數的調用順序
·全局對象的構造函數在main之前調用。
·靜態局部對象的構造函數在整個程序的執行過程中只調用一次。
class A{
int n;
public:
A(int n) : n(n){
cout << "A(" << n << ')' << endl;
}
~A(){
cout << "~A(" << n << ')' << endl;
}
};
void func(){
A a2(2);
static A a3(3);
}
int main(int ac, char *av[])
{
A a4(4);
cout << "first call func : \n";
func();
cout << "second call func : \n";
func();
return 0;
}
A a1(1);
//==========================
// 1:全局對象
// 2:函數func中的自動對象
// 3:函數func中的靜態全局對象
//4:函數main中的自動對象
//===============================
結果顯示:
[root@anna-laptop construtor]# ./call_queue
A(1)
A(4)
first call func :
A(2)
A(3)
~A(2)
second call func :
A(2)
~A(2)
~A(4)
~A(3)
~A(1)