[英]C++ Pointer can call Member Function without Object
令人驚訝的是,人們可能將其稱為功能,但是我經常說它是C ++的另一個錯誤,我們可以通過指針調用成員函數而無需分配任何對象。 請參見以下示例:
class A{
public:
virtual void f1(){cout<<"f1\n";}
void f2(){cout<<"f2\n";};
};
int main(){
A *p=0;
p->f2();
return 0;
}
輸出:f2
我們已經在不同的編譯器和平台上進行了檢查,但是結果是相同的,但是,如果我們通過沒有對象的指針調用虛擬函數,則會發生運行時錯誤。 在檢查對象時,虛函數的原因很明顯,因此找不到錯誤。
這不是錯誤。 您觸發了未定義的行為。 您可能會得到任何結果,包括預期的結果。
取消引用NULL
指針是未定義的行為。
順便說一句,沒有諸如“ C ++的bug”之類的東西。 在C ++編譯器中,錯誤可能不是以其自身的語言出現的。
如前所述,這是未定義的行為,因此一切都會發生。
要回答實施方面的問題,為什么會看到這種行為?
非虛擬調用僅作為普通函數調用實現,並且this
指針(值null
)作為參數傳入。 該參數未取消引用(因為未使用任何成員變量),因此調用成功。
虛擬調用需要在vtable
進行查找,以獲取要調用的實際函數的地址。 vtable
地址存儲在對象本身數據中的指針中。 因此要讀取它,必須取消引用this
指針-分段錯誤。
當您通過創建課程時
class A{
public:
virtual void f1(){cout<<"f1\n";}
void f2(){cout<<"f2\n";};
};
編譯器將成員函數的代碼放在文本區域中。 當您執行p->MemberFunction()
,編譯器僅p->MemberFunction()
p
並嘗試使用p
的類型信息(即Class A
查找函數MemberFunction
。
現在,由於函數的代碼存在於文本區域中,因此將其調用。 如果函數具有對某些類變量的引用,則在訪問它們時,您可能會遇到“ Segmentation Fault
因為沒有對象,但是由於並非如此,因此該函數可以正確執行。
注意 :這完全取決於編譯器如何實現成員函數訪問。 一些編譯器可以選擇在訪問成員函數之前查看對象的指針是否為null,但是指針可能具有一些垃圾值而不是0
,這是編譯器無法檢查的,因此通常編譯器會忽略此檢查。
通過未定義的行為,您可以取得很多成就。 您甚至可以調用一個僅接受1個參數的函數,然后接收第二個參數,如下所示:
#include <iostream>
void Func(int x)
{
uintptr_t ptr = reinterpret_cast<uintptr_t>(&x) + sizeof(x);
uintptr_t* sPtr = (uintptr_t*)ptr;
const char* secondArgument = (const char*)*sPtr;
std::cout << secondArgument << std::endl;
}
int main()
{
typedef void(*PROCADDR)(int, const char*);
PROCADDR ext_addr = reinterpret_cast<PROCADDR>(&Func);
//call the function
ext_addr(10, "arg");
return 0;
}
在Windows下編譯並運行,第二個參數的結果為“ arg”。 這不是C ++的錯,對我而言只是愚蠢的:)
這將適用於大多數編譯器。 調用方法(非虛擬方法)時,編譯器會翻譯:
obj.foo();
到某事:
foo(&obj);
其中&obj
成為foo
方法的this
指針。 使用指針時:
Obj *pObj = NULL;
pObj->foo();
對於編譯器,它不過是:
foo(pObj);
即:
foo(NULL);
用空指針調用任何函數都不構成犯罪,空指針(即具有空值的指針)將被壓入調用堆棧。 由目標函數檢查是否將null傳遞給它。 就像調用:
strlen(NULL);
如果得到處理,它將編譯並運行 :
size_t strlen(const char* ptr) {
if (ptr==NULL) return 0;
... // rest of code if `ptr` is not null
}
因此,這非常有效:
((A*)NULL)->f2();
只要f2
非虛,如果f2
不讀/寫東西出來的this
,包括任何虛函數調用。 靜態數據和功能訪問仍然可以。
但是 ,如果方法是虛擬的,則函數調用並不像看起來那樣簡單。 編譯器放入一些其他代碼來執行給定功能的后期綁定 。 后期綁定完全基於this
指針所指向的內容。 它取決於編譯器,但調用類似於:
obj->virtual_fun();
將涉及通過虛函數表查找來查找obj
的當前類型。 因此, obj
不能為null。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.