一步一步學習C (類)之成員函式的特性

2021-06-29 16:25:37 字數 4274 閱讀 7522

在類體中說明的函式作為類的成員,稱為成員函式。一般的成員函式,它是根據某種類的功能的需要來定義的。除此之外,又討論了一些特殊的成員函式:建構函式、析構函式、拷貝初始化建構函式等。本節討論除成員函式定義與說明之外的其它一些特殊屬性。

類的成員函式可分為內聯函式與外聯函式。內聯函式是指定義在類體內的成員函式,即該函式的定義放在類的體內。而對成員函式的說明放在體內,其函式的定義放在體外稱之為外聯函式。如果使外聯函式轉變為內聯函式,只須在函式頭部左端加上關鍵字inline即可。

內聯函式在呼叫時並不發生程式執行的轉移,而是在呼叫內聯函式處用內聯函式體的**來替換,以節省呼叫開銷,提高執行效率。

【說明】

:函式是一種更高階的抽象。它的引入使得程式設計者只關心函式的功能和使用方法,而不必關心函式功能的具體實現;函式的引入可以減少程式的目標**,實現程式**和資料的共享。但是,函式呼叫也會帶來降低效率的問題,因為呼叫函式實際上將程式執行順序轉移到函式所存放在記憶體中某個位址,將函式的程式內容執行完後,再返回到轉去執行該函式前的地方。這種轉移操作要求在轉去前要保護現場並記憶執行的位址,轉回後先要恢復現場,並按原來儲存位址繼續執行。因此,函式呼叫要有一定的時間和空間方面的開銷,於是將影響其效率。特別是對於一些函式體**不是很大,但又頻繁地被呼叫的函式來講,解決其效率問題更為重要。引入內聯函式實際上就是為了解決這一問題。 

在程式編譯時,編譯器將程式中出現的內聯函式的呼叫表示式用內聯函式的函式體來進行替換。顯然,這種做法不會產生轉去轉回的問題,但是由於在編譯時將函式休中的**被替代到程式中,因此會增加目標程式**量,進而增加空間開銷,而在時間代銷上不象函式呼叫時那麼大,可見它是以目標**的增加為代價來換取時間的節省。 

在程式中,呼叫其函式時,該函式在編譯時被替代,而不是像一般

函式那樣是在執行時被呼叫

使用內聯函式應注意的事項

內聯函式具有一般函式的特性,它與一般函式所不同之處公在於函式呼叫的處理。一般函式進行呼叫時,要將程式執行權轉到被呼叫函式中,然後再返回到呼叫它的函式中;而內聯函式在呼叫時,是將呼叫表示式用內聯函式體來替換。在使用內聯函式時,應注意如下幾點: 

1.乙個函式可以自已呼叫自已,稱為遞迴呼叫(後面講到),含有遞迴呼叫的函式不能設定為inline;

2.使用了複雜流程控制語句:迴圈語句和switch語句,無法設定為inline;

3.由於inline增加體積的特性,所以建議inline函式內的**應很短小。最好不超過5行。

4.inline僅做為一種「請求」,特定的情況下,編譯器將不理會inline關鍵字,而強制讓函式成為普通函式。出現這種情況,編譯器會給出警告訊息。

5.在你呼叫乙個內聯函式之前,這個函式一定要在之前有宣告或已定義為inline,如果在前面宣告為普通函式,而在呼叫**後面才定義為乙個inline函式,程式可以通過編譯,但該函式沒有實現inline。

總結為幾句話

1.在內聯函式內不允許用迴圈語句和開關語句。 

2.內聯函式的定義必須出現在內聯函式第一次被呼叫之前。

只要宣告為內聯,編譯器就不把它編譯成一次函式呼叫,而只是類似於把函式的**拷貝到被呼叫的地方,而且這完全是編譯器私下裡完成的,原來的訪問許可權等問題絲毫不受影響。這不是兩全齊美了嗎:在保證**的物件導向性和結構化不受損失的條件下,程式的效率也沒有損失。

class   myclass  

int inline myclass::getstate()

內聯函式還有另外一種寫法,就是直接寫在類

中,此時,不必使用「inline」關鍵字。

class   myclass  

private:

int m_istate;

};

內聯函式只是一種編譯機制,用上面兩種形式宣告的函式僅僅是建議編譯器進行內聯,而編譯器是否內聯不一定。正如前面所說,函式呼叫的開銷只是對小的函式不可忽略,對於重量級的函式還是可以忽略的,而且在絕大多數的場合,函式呼叫才是人間正道,才是解決問題的最佳。所以大多數編譯器並不把帶有迴圈、遞迴等或者**比較多的函式進行內聯編譯,有的甚至不允許宣告成內聯的。

類是型別而並非資料物件,每個類的物件都是該類資料成員的拷貝。然而,在有的時候,需要類的所有物件在類的範圍內共享某個資料。宣告為static的類成員便能在類的範圍內中共享,稱之為靜態成員。因此,靜態成員的提出是為了解決資料共享問題。

static主要有三個作用:

(1)區域性靜態變數

(2)外部靜態變數/函式

(3)靜態資料成員/成員函式

由於全域性變數不屬於類的成員,它的訪問許可權是完成開放的,因此,既不安全,又影響了重用性。

而靜態成員具有上述問題的雙重屬性,不但資料可以得到封裝,又可為所有的物件所共享。

靜態資料成員是類中所有物件所共享的成員,而不是某個物件的成員。

靜態資料成員的另乙個優勢是可以節省記憶體,對多個物件來說,靜態資料成員只儲存一處,為所有的物件所共用。

靜態資料成員的值對每個物件都是一樣的,但它的值可以更新。只要對靜態資料成員的值更新一次,可保證所有物件訪問更新後的相同值,這樣可提高時間效率。

2.1靜態成員的初始化在類的體外進行,而值得注意的是前面不加關鍵字static的意義在於避免與一般靜態變數或物件相混淆。

class   myclass  

private:

int m_istate;

static int a;//定義

} ;int myclass::a = 0;//初始化

2.2 靜態資料成員被 類 的所有物件所共享,包括該類派生類的物件。即派生類物件與基類物件共享基類的靜態資料成員

// staticforclass.cpp : 定義控制台應用程式的入口點。

//#include "stdafx.h"

class myclass

static int a; //定義

private:

int m_istate;

}; int myclass::a = 0;//初始化

class derivedclass:public myclass

;int _tmain(int argc, _tchar* argv)

3.1 靜態成員函式的位址可用普通函式指標儲存,而普通成員函式位址需要用 類成員函式指標來儲存

// staticforclass.cpp : 定義控制台應用程式的入口點。

//#include "stdafx.h"

class myclass

; int myclass::a = 0;//初始化

int myclass::getstate()//靜態函式的呼叫

void myclass::print()

class derivedclass:public myclass

;int _tmain(int argc, _tchar* argv)

3.2

靜態成員函式沒有this指標,.靜態成員函式不可以呼叫類的非靜態成員,它不能返回非靜態成員,因為除了物件會呼叫它外,類本身也可以呼叫。

class   myclass  

; int myclass::a = 0;//初始化

int myclass::getstate()//靜態函式的呼叫

關於靜態成員函式,可以總結為以下幾點:

1、出現在類體外的函式定義不能指定關鍵字static;

2、 靜態成員之間可以相互訪問,包括靜態成員函式訪問靜態資料成員和訪問靜態成員函式;

3、非靜態成員函式可以任意地訪問靜態成員函式和靜態資料成員;

4、靜態成員函式不能訪問非靜態成員函式和非靜態資料成員;

5、由於沒有this指標的額外開銷,因此靜態成員函式與類的全域性函式相比速度上會有少許的增長;

6、呼叫靜態成員函式,可以用成員訪問操作符(.)和(->)為乙個類的物件或指向類物件的指標呼叫靜態成員函式

7、當同一類的所有物件使用乙個量時,對於這個共用的量,可以用靜態資料成員變數,這個變數對於同一類的所有的物件都取相同的值。靜態成員變數只能被靜態成員函式呼叫。靜態成員函式也是由同一類中的所有物件共用。只能呼叫靜態成員變數和靜態成員函式。

一步一步學習C (類)之拷貝建構函式

拷貝建構函式,又稱複製建構函式,是一種特殊的建構函式,它由編譯器 呼叫來完成一些基於同一類的其他物件的構建及初始化。在c 中,下面三種物件需要呼叫拷貝建構函式 有時也稱 複製建構函式 1 乙個物件作為函式引數,以值傳遞的方式傳入 函式體tstudent tstudent const tstudent...

一步一步學習IdentityServer3 8

identityserver3結合hangfire及cookies中介軟體實現授權 idr3資料庫token過期管理 globalconfiguration.configuration.usesqlserverstorage hangfire globalconfiguration.configur...

一步一步學習IdentityServer3 4

其實上述例子 很多都很找到 但是在實際生態環境中給例子有很多不一樣的地方 比如自定已登入介面怎麼做?怎麼訪問自己的使用者資料庫實現登入?怎麼在介面中使用,在介面中又怎麼實現與idr3結合授權?等等問題 解下來我會在實際專案中整體一步一步介紹 1 這是我自定義登入的介面 2 在授權同意頁面 3 下圖是...