C++中的函數(shù)在編譯時(shí)會根據(jù)命名空間、類、參數(shù)簽名等信息進(jìn)行重新命名,形成新的函數(shù)名。函數(shù)重命名的過程通過一個(gè)特殊的Name Mangling(名字編碼)算法來實(shí)現(xiàn)。Name Mangling算法是一種可逆的算法,既可以通過現(xiàn)有函數(shù)名計(jì)算出新函數(shù)名,也可以通過新函數(shù)名逆向推導(dǎo)出原有函數(shù)名。
Name Mangling算法可以確保新函數(shù)名的唯一性,只要命名空間、所屬的類、參數(shù)簽名等有一個(gè)不同,那么產(chǎn)生的新函數(shù)名也不同。
不同的編譯器有不同的?Name Mangling 算法,產(chǎn)生的函數(shù)名也不一樣。
成都創(chuàng)新互聯(lián)主要從事網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)辰溪,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
this指針屬性如下:
A、名稱屬性:標(biāo)識符this表示。
B、類型屬性:classname const
C、值屬性:表示當(dāng)前調(diào)用該函數(shù)對象的首地址。
D、作用域:this指針是編譯器默認(rèn)傳給類中非靜態(tài)函數(shù)的隱含形參,其作用域在非靜態(tài)成員函數(shù)的函數(shù)體內(nèi)。
E、鏈接屬性:在類作用域中,不同類的非靜態(tài)成員函數(shù)中,this指針變量的鏈接屬性是內(nèi)部的,但其所指對象是外部的,即this變量是不同的實(shí)體,但指向?qū)ο笫峭粋€(gè)。
F、存儲類型:this指針是由編譯器生成,當(dāng)類的非靜態(tài)成員函數(shù)的參數(shù)個(gè)數(shù)一定時(shí),this指針存儲在ECX寄存器中;若該函數(shù)參數(shù)個(gè)數(shù)未定(可變參數(shù)函數(shù)),則存放在棧中。
this指針并不是對象的一部分,this指針?biāo)嫉膬?nèi)存大小是不會反映在sizeof操作符上的。this指針的類型取決于使用this指針的成員函數(shù)類型以及對象類型。
類的成員函數(shù)默認(rèn)第一個(gè)參數(shù)為T const register this。
this在成員函數(shù)的開始執(zhí)行前構(gòu)造,在成員函數(shù)執(zhí)行結(jié)束后清除。
C++語言規(guī)定,成員函數(shù)指針具有contravariance特性,即基類的成員函數(shù)指針可以賦值給派生類的成員函數(shù)指針,C++語言提供了默認(rèn)的轉(zhuǎn)換方式,但反過來不行。
C++編譯器在代碼編譯階段會對類對象調(diào)用的成員函數(shù)進(jìn)行靜態(tài)綁定(虛函數(shù)進(jìn)行動(dòng)態(tài)綁定),類成員函數(shù)的地址在代碼編譯時(shí)就確定,類成員函數(shù)地址可以使用成員函數(shù)指針進(jìn)行保存。
成員函數(shù)指針定義語法如下:
ReturnType (ClassName::* pointerName) (ArgumentLList);
ReturnType:成員函數(shù)返回類型
ClassName: 成員函數(shù)所屬類的名稱
Argument_List: 成員函數(shù)參數(shù)列表
pointerName:指針名稱
class Test
{
public:
void print()
{
cout << "Test::print" << endl;
}
};
成員函數(shù)指針語法極其嚴(yán)格:
A、不能使用括號:例如&(Test::print)不對。
B、?必須有限定符:例如&print不對,即使在類ClassName作用域內(nèi)也不行。
C、必須使用取地址符號:直接寫Test::print不行,必須寫:&Test::print。
Test類的成員函數(shù)print的函數(shù)指針聲明如下:void (Test::*pFun)();
初始化如下:pFunc = &Test::print;
Test類的成員函數(shù)print的函數(shù)指針聲明及初始化如下:void (Test::* pFunc)() = &Test::print;
通常,為了簡化代碼,使用typedef關(guān)鍵字。
typedef void (Test::*pFunc)();
pFunc p = &Test::print;
可以通過函數(shù)指針調(diào)用成員函數(shù),示例代碼如下:
#include <iostream>
using namespace std;
class Test
{
public:
void print()
{
cout << "Test::print" << endl;
}
};
int main(int argc, char *argv[])
{
void (Test::* pFunc)() = &Test::print;
Test test;
//通過對象調(diào)用成員函數(shù)
(test.*pFunc)();//Test::print
Test* pTest = &test;
//通過指針調(diào)用成員函數(shù)
(pTest->*pFunc)();//Test::print
//pFunc();//error
//error: must use '.*' or '->*' to call pointer-to-member
//function in 'pFunc (...)', e.g. '(... ->* pFunc) (...)'
return 0;
}
上述代碼中,.*pFunc
將pFunc綁定到對象test,->*pFunc
綁定pFunc到pTest指針?biāo)赶虻膶ο蟆?br/>成員函數(shù)指針不是常規(guī)指針(保存的是某個(gè)確切地址),成員函數(shù)指針保存的是成員函數(shù)在類布局中的相對地址。
C++成員函數(shù)使用thiscall函數(shù)調(diào)用約定。C++靜態(tài)成員函數(shù)、普通成員函數(shù)的函數(shù)地址在代碼區(qū),虛成員函數(shù)地址是一個(gè)相對地址。
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(int i, int j)
{
m_i = i;
m_j = j;
cout << "Parent(int i, int j): " << this << endl;
}
virtual void print()
{
cout << "Parent::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
}
virtual void sayHello()
{
cout << "Parent::sayHello()" << endl;
}
virtual void func()
{
cout << "Parent::func()" << endl;
}
virtual ~Parent()
{
cout << "~Parent(): " << this << endl;
}
static void display()
{
cout << "Parent::display()" << endl;
}
int add(int v)
{
return m_i + m_j + v;
}
protected:
int m_i;
int m_j;
};
int main(int argc, char *argv[])
{
cout <<&Parent::display<<endl;
cout <<&Parent::print<<endl;
cout <<&Parent::sayHello<<endl;
cout <<&Parent::func<<endl;
return 0;
}
上述代碼中,打印出的所有的成員函數(shù)的地址為1。原因在于輸出操作符<<沒有對C++成員函數(shù)指針類型進(jìn)行重載,C++編譯器將C++成員函數(shù)指針類型轉(zhuǎn)換為bool類型進(jìn)行了輸出,所以所有的輸出為1。因此,C++成員函數(shù)地址進(jìn)行打印時(shí)不能使用cout,可以用printf輸出,因?yàn)閜rintf可以接收任意類型的參數(shù),包括__thiscall
類型。
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(int i, int j)
{
m_i = i;
m_j = j;
cout << "Parent(int i, int j): " << this << endl;
}
virtual void print()
{
cout << "Parent::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
}
virtual void sayHello()
{
cout << "Parent::sayHello()" << endl;
}
virtual void func()
{
cout << "Parent::func()" << endl;
}
virtual ~Parent()
{
cout << "~Parent(): " << this << endl;
}
static void display()
{
cout << "Parent::display()" << endl;
}
int add(int v)
{
return m_i + m_j + v;
}
protected:
int m_i;
int m_j;
};
int main(int argc, char *argv[])
{
//靜態(tài)成員函數(shù)
cout << "static member function addree:" << endl;
printf("0x%p\n", &Parent::display);
printf("0x%p\n", Parent::display);
//普通成員函數(shù)
cout << "normal member function addree:" << endl;
printf("0x%p\n", &Parent::add);
cout << "virtual member function addree:" << endl;
//虛成員函數(shù)
printf("%d\n", &Parent::print);//1
printf("%d\n", &Parent::sayHello);//5
printf("%d\n", &Parent::func);//9
return 0;
}
C++編譯器要實(shí)現(xiàn)成員函數(shù)指針,必須解決下列問題:
A、成員函數(shù)是不是虛函數(shù)。
B、成員?函數(shù)運(yùn)行時(shí),需不需要調(diào)整this指針,如何調(diào)整。
不需要調(diào)整this指針的情況如下:
A、繼承樹最頂層的類。
B、單繼承,若所有類都不含有虛函數(shù),那么繼承樹上所有類都不需要調(diào)整this指針。
C、單繼承,若最頂層的類含有虛函數(shù),那么繼承樹上所有類都不需要調(diào)整this指針。
可能需要進(jìn)行this指針調(diào)整的情況如下:
A、多繼承
B、單繼承,最頂?shù)腷ase class不含virtual function,但繼承類含虛函數(shù),繼承類可能需要進(jìn)行this指針調(diào)整。
Microsoft VC對C++成員函數(shù)指針的實(shí)現(xiàn)采用的是Microsoft一貫使用的Thunk技術(shù)。Microsoft將成員函數(shù)指針分為兩種:
struct pmf_type1{
void* vcall_addr;
};
struct pmf_type2{
void* vcall_addr;
int delta; //調(diào)整this指針用
};
vcall_addr是Microsoft?的Thunk技術(shù)核心所在。vcall_addr是一個(gè)指針,隱藏了它所指的函數(shù)是虛擬函數(shù)還是普通函數(shù)的區(qū)別。如果所指的成員函數(shù)是一個(gè)普通成員函數(shù),vcall_addr是成員函數(shù)的函數(shù)地址。如果所指的成員函數(shù)是虛成員函數(shù),那么vcall_addr指向一小段代碼,這段代碼會根據(jù)this指針和虛函數(shù)索引值尋找出真正的函數(shù)地址,然后跳轉(zhuǎn)到真實(shí)的函數(shù)地址處執(zhí)行。
Microsoft根據(jù)情況選用函數(shù)指針結(jié)構(gòu)表示成員函數(shù)指針,使用Thunk技術(shù)(vcall_addr)實(shí)現(xiàn)虛擬函數(shù)/非虛擬函數(shù)的自適應(yīng),在必要的時(shí)候進(jìn)行this指針調(diào)整(使用delta)。
GCC對于成員函數(shù)指針統(tǒng)一使用下面的結(jié)構(gòu)進(jìn)行表示:
struct
{
void* __pfn; //函數(shù)地址,或者是虛擬函數(shù)的index
long __delta; // offset, 用來進(jìn)行this指針調(diào)整
};
不管是普通成員函數(shù),還是虛成員函數(shù),信息都記錄在__pfn
。一般來說因?yàn)閷R的關(guān)系,函數(shù)地址都至少是4字節(jié)對齊的。即函數(shù)地址的最低位兩個(gè)bit總是0。?GCC充分利用了這兩個(gè)bit。如果是普通的函數(shù),__pfn
記錄函數(shù)的真實(shí)地址,最低位兩個(gè)bit就是全0,如果是虛成員函數(shù),最后兩個(gè)bit不是0,剩下的30bit就是虛成員函數(shù)在函數(shù)表中的索引值。
GCC先取出函數(shù)地址最低位兩個(gè)bit看看是不是0,若是0就使用地址直接進(jìn)行函數(shù)調(diào)用。若不是0,就取出前面30位包含的虛函數(shù)索引,通過計(jì)算得到真正的函數(shù)地址,再進(jìn)行函數(shù)調(diào)用。
GCC和Microsoft對成員函數(shù)指針實(shí)現(xiàn)最大的不同就是GCC總是動(dòng)態(tài)計(jì)算出函數(shù)地址,而且每次調(diào)用都要判斷是否為虛函數(shù),開銷自然要比Microsoft的實(shí)現(xiàn)要大一些。
在this指針調(diào)整方面,GCC和Mircrosoft的做法是一樣的。不過GCC在任何情況下都會帶上__delta
變量,如果不需要調(diào)整,__delta=0
。
GCC的實(shí)現(xiàn)比Microsoft簡單,在所有場合其實(shí)現(xiàn)方式都是一樣的。
C++語言的規(guī)定,基類的成員函數(shù)指針可以賦值給派生類的成員函數(shù)指針,不允許繼承類的成員函數(shù)指針賦值給基類成員函數(shù)指針。
?C++規(guī)定編譯器必須提供一個(gè)從基類成員函數(shù)指針到繼承類成員函數(shù)指針的默認(rèn)轉(zhuǎn)換。C++編譯器提供的默認(rèn)轉(zhuǎn)換最關(guān)鍵的就是this指針調(diào)整。
因此,一般情況下不要將繼承類的成員函數(shù)指針賦值給基類成員函數(shù)指針。不同C++編譯器可能有不同的表現(xiàn)。
解決方案:
A、不要使用static_cast將繼承類的成員函數(shù)指針賦值給基類成員函數(shù)指針,如果一定要使用,首先確定沒有問題。
B、如果一定要使用static_cast,注意不要使用多繼承。
C、如果一定要使用多繼承的話,不要把一個(gè)基類的成員函數(shù)指針賦值給另一個(gè)基類的函數(shù)指針。
D、單繼承要么全部不使用虛函數(shù),要么全部使用虛函數(shù)。不要使用非虛基類,卻讓子類包含虛函數(shù)。
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
Parent(int i, int j)
{
m_i = i;
m_j = j;
}
void print()
{
cout << "Parent::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
}
double sum()
{
cout << "Parent::" << __func__<< endl;
double ret = m_i + m_j;
cout <<ret << endl;
return ret;
}
void display()
{
cout << "Parent::display()" << endl;
}
int add(int value)
{
return m_i + m_j + value;
}
protected:
int m_i;
int m_j;
};
class ChildA : public Parent
{
public:
ChildA(int i, int j, double d):Parent(i, j)
{
m_d = d;
}
void print()
{
cout << "ChildA::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
cout << "m_d = "<< m_d << endl;
}
double sum()
{
cout << "ChildA::" << __func__<< endl;
double ret = m_i + m_j + m_d;
cout << ret << endl;
return ret;
}
private:
void display()
{
cout << "ChildA::display()" << endl;
}
private:
double m_d;
};
int main(int argc, char *argv[])
{
Parent parent(100,200);
ChildA childA(1,2,3.14);
Parent* pTestA = &childA;
typedef void (Parent::*pPrintFunc)();
pPrintFunc pPrint = &Parent::print;
typedef double (Parent::*pSumFunc)();
pSumFunc pSum = &Parent::sum;
typedef void (Parent::*pDisplayFunc)();
pDisplayFunc pDisplay = &Parent::display;
printf("0x%X\n",pPrint);
printf("0x%X\n",pSum);
printf("0x%X\n",pDisplay);
//不能將派生類的成員函數(shù)指針賦值給基類的函數(shù)指針
//pPrint = &ChildA::print;//error
//可以將基類的成員函數(shù)指針賦值給派生類
void (ChildA::*pChildPrintFunc)() = pPrint;
(childA.*pChildPrintFunc)();//Parent::print
void (*p)() = reinterpret_cast<void (*)()>(pPrint);
p();
return 0;
}
對于靜態(tài)成員函數(shù),函數(shù)體內(nèi)部沒有this指針,與類的其它成員函數(shù)共享類的命名空間,但靜態(tài)成員函數(shù)并不是類的一部分,靜態(tài)成員函數(shù)與常規(guī)的全局函數(shù)一樣,成員函數(shù)指針的語法針對靜態(tài)成員函數(shù)并不成立。
靜態(tài)成員函數(shù)的函數(shù)指針定義語法如下:
ReturnType (* pointerName) (ArgumentLList);
ReturnType:成員函數(shù)返回類型
Argument_List: 成員函數(shù)參數(shù)列表
pointerName:指針名稱
靜態(tài)成員函數(shù)的函數(shù)指針的使用與全局函數(shù)相同,但靜態(tài)成員函數(shù)指針保存的仍舊是個(gè)相對地址。
#include <iostream>
using namespace std;
class Test
{
public:
static void print()
{
cout << "Test::print" << endl;
}
};
int main(int argc, char *argv[])
{
void (* pFunc)() = &Test::print;
cout << pFunc << endl;//1
//直接調(diào)用
pFunc();//Test::print
(*pFunc)();//Test::print
Test test;
//(test.*pFunc)();//error
Test* pTest = &test;
//(pTest->*pFunc)();//error
return 0;
}
非靜態(tài)、非虛的普通成員函數(shù)指針不能直接調(diào)用,必須綁定一個(gè)類對象。
普通函數(shù)指針的值指向代碼區(qū)中的函數(shù)地址。如果強(qiáng)制轉(zhuǎn)換為普通函數(shù)指針后調(diào)用,成員函數(shù)內(nèi)部this指針訪問的成員變量將是垃圾值。
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
Parent(int i, int j)
{
m_i = i;
m_j = j;
}
void print()
{
cout << "Parent::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
}
double sum()
{
cout << "Parent::" << __func__<< endl;
double ret = m_i + m_j;
cout <<ret << endl;
return ret;
}
void display()
{
cout << "Parent::display()" << endl;
}
int add(int value)
{
return m_i + m_j + value;
}
protected:
int m_i;
int m_j;
};
class ChildA : public Parent
{
public:
ChildA(int i, int j, double d):Parent(i, j)
{
m_d = d;
}
void print()
{
cout << "ChildA::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
cout << "m_d = "<< m_d << endl;
}
double sum()
{
cout << "ChildA::" << __func__<< endl;
double ret = m_i + m_j + m_d;
cout << ret << endl;
return ret;
}
private:
void display()
{
cout << "ChildA::display()" << endl;
}
private:
double m_d;
};
int main(int argc, char *argv[])
{
Parent parent(100,200);
ChildA childA(1,2,3.14);
Parent* pTestA = &childA;
typedef void (Parent::*pPrintFunc)();
pPrintFunc pPrint = &Parent::print;
typedef double (Parent::*pSumFunc)();
pSumFunc pSum = &Parent::sum;
typedef void (Parent::*pDisplayFunc)();
pDisplayFunc pDisplay = &Parent::display;
printf("0x%X\n",pPrint);
printf("0x%X\n",pSum);
printf("0x%X\n",pDisplay);
//綁定類對象進(jìn)行調(diào)用
(pTestA->*pPrint)();
(pTestA->*pSum)();
(pTestA->*pDisplay)();
//強(qiáng)制轉(zhuǎn)換為普通函數(shù)指針
void (*p)() = reinterpret_cast<void (*)()>(pPrint);
p();//打印隨機(jī)值
return 0;
}
C++通過虛函數(shù)提供了運(yùn)行時(shí)多態(tài)特性,編譯器通常使用虛函數(shù)表實(shí)現(xiàn)虛函數(shù)。
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
Parent(int i, int j)
{
m_i = i;
m_j = j;
}
virtual void print()
{
cout << "Parent::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
}
virtual double sum()
{
cout << "Parent::" << __func__<< endl;
double ret = m_i + m_j;
cout <<ret << endl;
return ret;
}
virtual void display()
{
cout << "Parent::display()" << endl;
}
int add(int value)
{
return m_i + m_j + value;
}
protected:
int m_i;
int m_j;
};
class ChildA : public Parent
{
public:
ChildA(int i, int j, double d):Parent(i, j)
{
m_d = d;
}
virtual void print()
{
cout << "ChildA::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
cout << "m_d = "<< m_d << endl;
}
virtual double sum()
{
cout << "ChildA::" << __func__<< endl;
double ret = m_i + m_j + m_d;
cout << ret << endl;
return ret;
}
private:
void display()
{
cout << "ChildA::display()" << endl;
}
private:
double m_d;
};
class ChildB : public Parent
{
public:
ChildB(int i, int j, double d):Parent(i, j)
{
m_d = d;
}
virtual void print()
{
cout << "ChildB::" << __func__<< endl;
cout << "m_i = "<< m_i << endl;
cout << "m_j = "<< m_j << endl;
cout << "m_d = "<< m_d << endl;
}
virtual double sum()
{
cout << "ChildB::" << __func__<< endl;
double ret = m_i + m_j + m_d;
cout << ret << endl;
return ret;
}
private:
void display()
{
cout << "ChildB::display()" << endl;
}
private:
double m_d;
};
int main(int argc, char *argv[])
{
Parent parent(100,200);
ChildA childA(1,2,3.14);
//childA.display();//error,編譯時(shí)private不可訪問
ChildB childB(100,200,3.14);
Parent* pTestA = &childA;
Parent* pTestB = &childB;
typedef void (Parent::*pVPrintFunc)();
pVPrintFunc pPrint = &Parent::print;
(parent.*pPrint)();//Parent::print
(pTestA->*pPrint)();//ChildA::print,多態(tài)
(pTestB->*pPrint)();//ChildB::print,多態(tài)
typedef double (Parent::*pVSumFunc)();
pVSumFunc pSum = &Parent::sum;
(parent.*pSum)();//Parent::sum
(pTestA->*pSum)();//ChildA::sum,多態(tài)
(pTestB->*pSum)();//ChildB::sum,多態(tài)
typedef void (Parent::*pVDisplayFunc)();
pVDisplayFunc pDisplay = &Parent::display;
(parent.*pDisplay)();//Parent::display
(pTestA->*pDisplay)();//ChildA::display,多態(tài)
(pTestB->*pDisplay)();//ChildB::display,多態(tài)
printf("0x%X\n",pPrint);
printf("0x%X\n",pSum);
printf("0x%X\n",pDisplay);
return 0;
}
虛成員函數(shù)指針的值是一個(gè)相對地址,表示虛函數(shù)在虛函數(shù)表中,離表頭的偏移量+1。
當(dāng)一個(gè)對象調(diào)用虛函數(shù)時(shí),首先通過獲取指向虛函數(shù)表指針的值得到虛函數(shù)表的地址,然后將虛函數(shù)表的地址加上虛函數(shù)離表頭的偏移量即為虛函數(shù)的地址。?
成員函數(shù)指針的一個(gè)重要應(yīng)用是根據(jù)輸入來生成響應(yīng)事件,使用不同的處理函數(shù)來處理不同的輸入。
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
//虛擬打印機(jī)
class Printer {
public:
//復(fù)制文件
void Copy(char *buff, const char *source)
{
strcpy(buff, source);
}
//追加文件
void Append(char *buff, const char *source)
{
strcat(buff, source);
}
};
//菜單中兩個(gè)可供選擇的命令
enum OPTIONS { COPY, APPEND };
//成員函數(shù)指針
typedef void(Printer::*PTR) (char*, const char*);
void working(OPTIONS option, Printer *machine,
char *buff, const char *infostr)
{
// 指針數(shù)組
PTR pmf[2] = { &Printer::Copy, &Printer::Append };
switch (option)
{
case COPY:
(machine->*pmf[COPY])(buff, infostr);
break;
case APPEND:
(machine->*pmf[APPEND])(buff, infostr);
break;
}
}
int main() {
OPTIONS option;
Printer machine;
char buff[40];
working(COPY, &machine, buff, "Strings ");
working(APPEND, &machine, buff, "are concatenated!");
std::cout << buff << std::endl;
}
// Output:
// Strings are concatenated!
類中的成員函數(shù)存在于代碼段。調(diào)用成員函數(shù)時(shí),類對象的地址作為參數(shù)隱式傳遞給成員函數(shù),成員函數(shù)通過對象地址隱式訪問成員變量,C++語法隱藏了對象地址的傳遞過程。由于類成員函數(shù)內(nèi)部有一個(gè)this指針,類成員函數(shù)的this指針會被調(diào)用的類對象地址賦值。因此,如果類成員函數(shù)中沒有使用this指針訪問成員,則類指針為NULL時(shí)仍然可以成功對該成員函數(shù)進(jìn)行調(diào)用。static成員函數(shù)作為一種特殊的成員函數(shù),函數(shù)內(nèi)部不存在this指針,因此類指針為NULL時(shí)同樣可以成功對靜態(tài)成員函數(shù)進(jìn)行調(diào)用。
#include <iostream>
#include <string>
using namespace std;
namespace Core {
class Test
{
public:
Test(int i)
{
this->i = i;
}
void print()
{
cout << "i = " << i << endl;
}
void sayHello()
{
cout << "Hello,Test." << endl;
}
static void printHello()
{
cout << "Hello,Test." << endl;
}
private:
int i;
};
}
int main()
{
using namespace Core;
Core::Test* ptest = NULL;
ptest->sayHello();//Hello,Test.
ptest->printHello();
//定義函數(shù)指針類型
typedef void (Test::*pFunc)();
//獲取類的成員函數(shù)地址
pFunc p = &Test::print;
Test test(100);
//調(diào)用成員函數(shù)
(test.*p)();//i = 100
return 0;
}
普通成員函數(shù)通過函數(shù)地址直接調(diào)用。
#include <iostream>
using namespace std;
class Test
{
public:
void print()
{
cout << "Test::print" << endl;
}
};
int main()
{
Test test;
Test* p = &test;
p->print();
}
對于非虛、非靜態(tài)成員函數(shù)的調(diào)用,如p->print(),C++編譯器會生成如下代碼:
Test* const this = p;
void Test::print(Test* const this)
{
cout << "Test::print" << endl;
}
不管指針p是任何值,包括NULL,函數(shù)Test::print()都可以被調(diào)用,p被作為this指針并當(dāng)作參數(shù)傳遞給print函數(shù)。因此,當(dāng)傳入print函數(shù)體內(nèi)的p指針為NULL時(shí),只要不對p指針進(jìn)行解引用,函數(shù)就能正常調(diào)用而不發(fā)生異常退出。
#include <iostream>
using namespace std;
class Test
{
public:
void print()
{
cout << "Test::print" << endl;
sayHello();
}
void sayHello()
{
cout << "Test::sayHello" << endl;
}
};
int main()
{
Test* p = NULL;
p->print();
}
// output:
// Test::print
// Test::sayHello
靜態(tài)成員函數(shù)通過函數(shù)地址進(jìn)行調(diào)用,其調(diào)用方式同全局函數(shù)。
虛成員函數(shù)的調(diào)用涉及運(yùn)行時(shí)多態(tài)。
當(dāng)一個(gè)對象調(diào)用虛函數(shù)時(shí),首先通過運(yùn)行時(shí)對象獲取指向虛函數(shù)表指針的值得到虛函數(shù)表的地址,然后將虛函數(shù)表的地址加上虛函數(shù)離表頭的偏移量即為虛函數(shù)的地址。?基類對象內(nèi)部的虛函數(shù)表指針指向基類的虛函數(shù)表,派生類對象的虛函數(shù)表指針指向派生類的虛函數(shù)表,確保運(yùn)行時(shí)對象調(diào)用正確的虛函數(shù)。
文章題目:C++語言學(xué)習(xí)(十四)——C++類成員函數(shù)調(diào)用分析
當(dāng)前路徑:http://bm7419.com/article34/jccpse.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、微信小程序、網(wǎng)站排名、靜態(tài)網(wǎng)站、服務(wù)器托管、虛擬主機(jī)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)