C++ 類的學習

一、類和對象的定義

1.類的語法形式

class 類名稱
{
public:
    公有成員(外部接口)
private:
    私有成員
protected:
    保護型成員
};


公有成員:類與外部的接口,任何外部函數都可以訪問類內的公有數據類型和函數
私有成員:只允許本類的對象裏的成員訪問
保護成員:在繼承和派生上與私有成員有輕微差別

class clock
{
    int hour,minute,second;
public:
    void setTime(int newH=0,int newM=0,int newS=0);
//可以這樣用,如果沒有提供實參,函數將使用初始化的值作爲參數的值
    void showTime();
};

可以在函數裏定義參數的初始值,這裏類聲明後面要加分號,否則,編譯器會報錯

2.類的成員函數

在類中聲明函數原型

在類外函數應寫成 類名::函數名(參數)的形式

void Clock::showTime()
{
    ;
}

可以在類中給出函數體,形成內聯成員函數

class clock
{
    int hour,minute,second;
public:
    void setTime(int newH=0,int newM=0,int newS=0);
    void showTime()
    {
        cout<<hour<<":"<<minute<<":"<<second<<endl;
    }
};

內聯成員函數:爲了提高運行時的效率,對於簡單的函數可以聲明爲內聯形式(即不能有循環和switch語句),如果想在類的聲明外定義成員函數,並使之成爲內聯函數,可以在定義函數時候加inline關鍵字 如 inline void Fun::fun(){.......},

函數也是通過棧來實現的,如果不使用內聯函數,程序在執行的時候函數還有個入棧出棧,以及給參數賦值等操作,這就花費了大量時間,舉個例子

int fun(int x)
{
    return x*2;
}
int main()
{
    int a,b;
    a=2;
    b=fun(a);
}

這個fun函數如果是內聯函數的話,編譯器就會把程序編譯成這樣

int main()
{
    int a,b;
    a=2;
    b=a+a;
}

雖然在編譯的環節增加了時間耗費,但是在運行的時候就減少了時間耗費。

函數前面加上了inline之後,就是函數的聲明瞭,也就是說只需要在.h文件裏聲明函數就行了,在.cpp文件裏不需要加函數的body了。

inline和c的define的含義差不多,但是使用inline的好處就是 inline函數是可以進行類型檢查的

比如

//define

#define f(a) (a)+(a)
int main()
{
    double a=4;
    printf("%d",f(a));
}

//inline 

inline int f(int i)
{
    return i*2;
}
int main()
{
    double a=4;
    printf("%d",f(a));
}

inline裏面由於f返回類型是double 編譯器會提示你%d和double不匹配,但是define 定義的函數,就忽略了返回值類型,編譯器不會報錯。

二、類的構造函數

構造函數:類中用於初始化的函數,在對象被創建時就會被執行

構造函數的形式:

1.函數名與類名相同

2.不能定義返回值類型,也不能有return語句

3.可以有形式參數,也可以沒有形式參數

4.可以是內聯函數

5.可以重載

6.可以帶默認參數值

默認構造函數:

1.參數表爲空的構造函數

2.全部參數都有默認值的構造函數

如果聲明類的時候沒有聲明構造函數,計算機會自動的運行一個生成的構造函數。

構造函數的實現👇

#include<bits/stdc++.h>
using namespace std;
class Clock
{
    int hour,minute,second;
public:
    void setTime(int newH=0,int newM=0,int newS=0);
    void showTime();
    Clock(int newH,int newM,int newS);
};

Clock::Clock(int newH,int newM,int newS):
    hour(newH),minute(newM),second(newS)
    {
        ;
    }
int main()
{
    Clock c(0,0,0);
    return 0;

這個構造函數之後冒號之後的內容是初始化列表,意思是把newH賦值給hour……

我們也可以在上面的類中在加入一個默認構造函數,在創建對象的時候,程序會自動識別使用那個有參數的構造函數還是那個默認的構造函數

當程序提供了非默認的帶參數的構造函數之後如fun::fun(int x)之後,我們在主函數裏再這樣來定義對象(fun a;是錯誤的)。解決的方法有兩個

1.給予非默認的構造函數裏的參數初始值

fun::fun(int x=0);

2.利用函數重載來定義另一個構造函數

fun::fun();

帶參數的構造函數也可以是默認構造函數,只要參數給了初始值

A::A(int n=0):x(n){}
A::A(){}

默認構造函數只能有一個,所以這樣是錯的。

因爲如果有 A b;編譯器不知道該調用哪一個構造函數了,這就犯了二義性問題。

對象數組

初始化方式 是 類名 對象名[對象數量] ={構造函數(參數),構造函數(參數)....};

如果構造函數的參數只有一個可以像數組一樣初始化, 類名 對象名[對象數量] ={參數1,參數2,參數3....};

 

委託構造函數:

當我們在類中重載多個構造函數的時候,我們往往發現,這些構造函數只是形參表不同,初始化列表不同,而其他都是一樣的,這時候我們並不一定需要在創建多個構造函數,只需要使用委託構造函數就行

Clock::Clock(int newH,int newM,int newS):
    hour(newH),minute(newM),second(newS)
{
    ;
}
Clock::Clock()://默認構造函數
    hour(0),minute(0),second(0)
{
    ;
}

兩個構造函數只是參數不同,所以我們可以這樣用

Clock::Clock(int newH,int newM,int newS):
    hour(newH),minute(newM),second(newS)
{
    ;
}
Clock::Clock():Clock(0,0,0)
{

}

※複製構造函數

#include<iostream>
using namespace std;
class A
{
public:
    int i;
    int* p;
    A(int x=0):i(x){}
};
int main()
{
    A a(1);
    A b;
    b=2;
    cout<<b.i<<endl;
    b=a;
    return 0;
}

上面代碼輸出結果是 2,

這說明b=2這樣的語法是行得通的,當我們把2換成一個對象的時候,即b=a的時候相當於用a來初始化b

也就是說,相當於調用了一個構造函數(這個構造函數是這樣的 A::A(const A &a){this->i=a.i}),這個函數就是複製構造函數,平時我們不需要去管他,因爲程序會自動生成一個默認的複製構造函數,但是,當我們的類中還有指針的成員的時候,如果使用默認的複製構造函數,就會只是把指針的地址複製,但是兩個指針指向的都是同一塊內存,在析構的時候,就容易發生錯誤。

 

 

 

析構函數:

對象在離開其作用域時候,執行析構函數,用於銷燬對象。

與構造函數類似,如果你不定義析構函數,程序會生成一個空的析構函數。

析構函數的表示方法:

~類名()

沒有返回類型

 

一個類的成員可以是另一個類的對象

#include<bits/stdc++.h>
using namespace std;
class A
{
private:
    int a,b,c;
public:
    A(int newA,int newB,int newC);
    A();
};
class B
{
    A p1;
    A p2;
public:
    B(A newp1,A newp2);
};
A::A(int newA,int newB,int newC):a(newA),b(newB),c(newC)
{

}
B::B(A newp1,A newp2):p1(newp1),p2(newp2)
{

}
int main()
{
    A fun(1,2,3);
    A happy(4,5,6);
    B world(fun,happy);
    return 0;
}

 

類的靜態成員:

靜態成員類似於靜態變量,靜態變量在整個程序結束前始終保持存在,如下代碼中

#include <iostream>
using namespace std;
void show()
{
    static int a=0;
    a++;
    cout<<a<<endl;
}
int main()
{
    show();
    show();
    show();
    return 0;
}

輸出結果

在函數show中 定義了一個靜態整型變量,第一次調用show函數的時候,a=0,結束程序後,a仍然存在,並且a的值不變,相當於全局變量,第二次調用show函數的時候,由於a已經存在了,static int a=0這條語句將不再被執行。


值得注意的是 在類裏面的static int a只是一個聲明,需要在其他地方定義這個變量,例如下面的靜態成員變量 a。

而且,靜態成員變量不能通過初始化列表初始化。

#include<bits/stdc++.h>
using namespace std;
class A
{
private:
    int x;
    static int a;
public:
    static void showw(){cout<<x<<endl;}//靜態成員函數是無法調用非靜態成員變量的
    是因爲靜態成員函數沒有this指針,只有這樣才能保證靜態成員變量可以通過A::a來訪問。
    void show(){cout<<a<<endl;}
};

int A::a=0;//初始化靜態成員變量a,這裏不加 static,是因爲加了static就說明這個變量只存在於當前這個.cpp文件裏,而這與類的相關特點相違背,所以不加static。
int main()
{
    A b;
    b.show();
    //cout<<b.a<<endl;
    //cout<<A::a<<endl;//如果a是公有成員,這兩種方法都可以訪問靜態成員變量a
    return 0;
}

可以使用類名::成員名來訪問靜態成員變量或成員函數


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章