1. 程式人生 > >成員函式指標與高效能的C++委託(中篇)

成員函式指標與高效能的C++委託(中篇)

成員函式指標與高效能的C++委託(中篇)

Member Function Pointers and the Fastest Possible C++ Delegates

撰文:Don Clugston

翻譯:周翔

(接上篇)

成員函式指標——為什麼那麼複雜?

類的成員函式和標準的C函式有一些不同。與被顯式宣告的引數相似,類的成員函式有一個隱藏的引數this,它指向一個類的例項。根據不同的編譯器,this或者被看作內部的一個正常的引數,或者會被特別對待(比如,在VC++中,this一般通過ECX暫存器來傳遞,而普通的成員函式的引數被直接壓在堆疊中)。this作為引數和其他普通的引數有著本質的不同,即使一個成員函式受一個普通函式的支配,在標準

C++中也沒有理由使這個成員函式和其他的普通函式(ordinary function)的行為相同,因為沒有thiscall關鍵字來保證它使用像普通引數一樣正常的呼叫規則。成員函式是一回事,普通函式是另外一回事(Member functions are from Mars, ordinary functions are from Venus)。

你可能會猜測,一個成員函式指標和一個普通函式指標一樣,只是一個程式碼指標。然而這種猜測也許是錯誤的。在大多數編譯器中,一個成員函式指標要比一個普通的函式指標要大許多。更奇怪的是,在Visual C++中,一個成員函式指標可以是4812甚至16個位元組長,這取決於它所相關的類的性質,同時也取決於編譯器使用了怎樣的編譯設定!成員函式指標比你想象中的要複雜得多,但也不總是這樣。

讓我們回到二十世紀80年代初期,那時,最古老的C++編譯器CFront剛剛開發完成,那時C++語言只能實現單一繼承,而且成員函式指標剛被引入,它們很簡單:它們就像普通的函式指標,只是附加了額外的this作為它們的第一個引數,你可以將一個成員函式指標轉化成一個普通的函式指標,並使你能夠對這個額外新增的引數產生足夠的重視。

這個田園般的世界隨著CFront 2.0的問世被擊得粉碎。它引入了模版和多重繼承,多重繼承所帶來的破壞造成了成員函式指標的改變。問題在於,隨著多重繼承,呼叫之前你不知道使用哪一個父類的this指標,比如,你有4個類定義如下:

class A {

public:

virtual int Afunc() { return 2; };

};

class B {

public:

int Bfunc() { return 3; };

};

// C是個單一繼承類,它只繼承於A

class C: public A {

public:

int Cfunc() { return 4; };

};

// D 類使用了多重繼承

class D: public A, public B {

public:

int Dfunc() { return 5; };

};

假如我們建立了C類的一個成員函式指標。在這個例子中,AfuncCfunc都是C的成員函式,所以我們的成員函式指標可以指向Afunc或者Cfunc。但是Afunc需要一個this指標指向C::A(後面我叫它Athis),而Cfunc需要一個this指標指向C(後面我叫它Cthis)。編譯器的設計者們為了處理這種情況使用了一個把戲(trick):他們保證了A類在物理上儲存在C類的頭部(即C類的起始地址也就是一個A類的一個例項的起始地址),這意味著Athis == Cthis。我們只需擔心一個this指標就夠了,並且對於目前這種情況,所有的問題處理得還可以。

現在,假如我們建立一個D類的成員函式指標。在這種情況下,我們的成員函式指標可以指向AfuncBfuncDfunc。但是Afunc需要一個this指標指向D::A,而Bfunc需要一個this指標指向D::B。這時,這個把戲就不管用了,我們不可以把A類和B類都放在D類的頭部。所以,D類的一個成員函式指標不僅要說明要指明呼叫的是哪一個函式,還要指明使用哪一個this指標。編譯器知道A類佔用的空間有多大,所以它可以對Athis增加一個delta = sizeof(A)偏移量就可以將Athis指標轉換為Bthis指標。

如果你使用虛擬繼承(virtual inheritance),比如虛基類,情況會變得更糟,你可以不必為搞懂這是為什麼太傷腦筋。就舉個例子來說吧,編譯器使用虛擬函式表virtual function table——“vtable”)來儲存每一個虛擬函式、函式的地址和virtual_delta將當前的this指標轉換為實際函式需要的this指標時所要增加的位移量。

綜上所述,為了支援一般形式的成員函式指標,你需要至少三條資訊:函式的地址,需要增加到this指標上的delta位移量,和一個虛擬函式表中的索引。對於MSVC來說,你需要第四條資訊:虛擬函式表(vtable)的地址。

成員函式指標的實現

那麼,編譯器是怎樣實現成員函式指標的呢?這裡是對不同的326416位的編譯器,對各種不同的資料型別(有intvoid*資料指標、程式碼指標(比如指向靜態函式的指標)、在單一(single-)繼承、多重(multiple-)繼承、虛擬(virtual-)繼承和未知型別(unknown)的繼承下的類的成員函式指標)使用sizeof運算子計算所獲得的資料:

編譯器

選項

int

DataPtr

CodePtr

Single

Multi

Virtual

Unknown

MSVC

4

4

4

4

8

12

16

MSVC

/vmg

4

4

4

16#

16#

16#

16

MSVC

/vmg /vmm

相關推薦

成員函式指標高效能C委託中篇

成員函式指標——為什麼那麼複雜?  類的成員函式和標準的C函式有一些不同。與被顯式宣告的引數相似,類的成員函式有一個隱藏的引數this,它指向一個類的例項。根據不同的編譯器,this或者被看作內部的一個正常的引數,或者會被特別對待(比如,在VC++中,this一般通過ECX暫

成員函式指標高效能C++委託(中篇)

成員函式指標與高效能的C++委託(中篇) Member Function Pointers and the Fastest Possible C++ Delegates 撰文:Don Clugston 翻譯:周翔 (接上篇) 成員函式指標——為什麼那麼複雜? 類

好文轉載:成員函式指標高效能C++委託

委託(delegate)和成員函式指標不同,你不難發現委託的用處。最重要的,使用委託可以很容易地實現一個Subject/Observer設計模式的改進版[GoF, p. 293]。Observer(觀察者)模式顯然在GUI中有很多的應用,但我發現它對應用程式核心的設計也有很大的作用。委託也可用來實現策略(St

成員函式指標高效能C++委託(上,中,下)

成員函式指標與高效能的 C++委託(上篇) Member Function Pointers and the Fastest Possible C++ Delegates 撰文: Don Clugston 翻譯:周翔 引子 標準 C++中沒有真正的面

成員函式指標高效能C++委託(下篇)

Member Function Pointers and the Fastest Possible C++ Delegates 撰文: 翻譯:周翔 (接中篇) 委託(delegate) 和成員函式指標不同,你不難發現委託的用處。最重要的,使用委託可以很容易地實現一個 現

成員函式指標高效能C++委託

Member Function Pointers and the Fastest Possible C++ Delegates 撰文:Don Clugston 翻譯:周翔 引子 標準C++中沒有真正的面向物件的函式指標。這一點對C++來說是不幸的,因為面向物件的指標(也

C/C++開發】函式指標回撥函式

C++很多類庫都喜歡用回撥函式,MFC中的定時器,訊息機制,hook機制等待,包括現在在研究的cocos2d-x中也有很多的回撥函式。 1.回撥函式 什麼是回撥函式呢?回撥函式其實就是一個通過函式指標呼叫的函式!假如你把A函式的指標當作引數傳給B函式,然後在B函式中通過A函式傳進來的這個指標

成員函式過載函式指標

在有成員函式過載的情況下該如何使用函式指標呢 class l { public: void func(); void func(int, int); }; void l::func() { cout << "func()" << endl; }

C++函式指標 C++11 function 函式物件對比

轉自:https://blog.csdn.net/skillart/article/details/52336303 1.函式指標 函式指標:是指向函式的指標變數,在C編譯時,每一個函式都有一個入口地址,那麼這個指向這個函式的函式指標便指向這個地址。函式指標主要由以下兩方面的用途:呼叫函式和

C/C++函式指標指標函式

前面說的話 面試的時候,經常有面試官問這個問題,在Linux核心裡面也是經常被使用的,在看很多大神的程式碼裡面,我們也經常遇到函式指標與指標函式,一樣,如果你自己沒問題了,就不用往下看了。   定義 我們看個程式碼 int *func(int a,int b)

為什麼 C++ 中成員函式指標是 16 位元組?

當我們討論指標時,通常假設它是一種可以用 void * 指標來表示的東西,在 x86_64 平臺下是 8 個位元組大小。例如,下面是來自 維基百科中關於 x86_64 的文章 的摘錄: Pushes and pops on the stack are always in 8-byte strides

C++類 給結構體成員 函式指標 賦值

myStruct標頭檔案 myStruct.h class CMyClass; struct {  int nFlag;  void (CMyClass::*myinit)(int n);  void (CMyClass::*myopen)(int n,void* arg)

c++中的函式指標和類成員函式指標

// // main.cpp // Demo // // Created by 杜國超 on 16/12/28. // Copyright © 2016年 杜國超. All rights reserved. // #include using namespace std; class MYCla

C++的靜態成員函式指標

先簡單的說說非靜態的成員函式。 非靜態成員函式指標的型別:     類的非靜態成員是和類的物件相關的。也就是說,要通過類的物件來訪問變數。 成員函式的型別定義為:     typedef void (A::*pfunc)();  A是一個類,有一個成員函式void test

函式指標指標函式C++工廠設計最喜歡用這個)

在看開源專案的時候,發現C++搞工廠設計都喜歡用這個。下面來給出這方面的例子(大學裡面沒學過)函式指標:型別一:程式碼如下:#include <iostream> using namespace std; int max(int x, int y){ retu

深入淺出剖析C語言函式指標回撥函式(一)

關於靜態庫和動態庫的使用和製作方法。http://blog.csdn.net/morixinguan/article/details/52451612今天我們要搞明白的一個概念叫回調函式。什麼是回撥函式?百度的權威解釋如下:回撥函式就是一個通過函式指標呼叫的函式。如果你把函式

C語言-函式指標函式名的區別

記得大學時老師曾說函式的函式名是函式的入口的指標,之前看block通過clang編譯生成的C程式碼發現很多函式指標,於是想了解函式指標與函式名有什麼區別?以及函式指標一般都有些什麼作用。函式指標與函式名的區別首先先定義一函式以及一個指向蓋函式的函式指標,並分別對他們進行呼叫。

C語言結構體中的函式指標函式

1、函式指標 一般的函式指標可以這麼定義: int(*func)(int,int); 表示一個指向含有兩個int引數並且返回值是int形式的任何一個函式指標. 假如存在這樣的一個函式: int add2(int x,int y) { return x+y;

C++模板程式設計->成員函式指標模板引數

class some_value; typename int (some_value::*some_value_mfp)(int); template<some_value_mfp func> int call(some_value &value ,

模板成員函式指標

#include <iostream> #include <list> using namespace std; /*包裝類,成員函式指標型別與普通函式指標型別不同,不能在類外直接定義*/ template <class T, class _