簡體   English   中英

C ++指針可以在沒有對象的情況下調用成員函數

[英]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 ++編譯器中,錯誤可能不是以其自身的語言出現的。

如前所述,這是未定義的行為,因此一切都會發生。

要回答實施方面的問題,為什么會看到這種行為?

  1. 非虛擬調用僅作為普通函數調用實現,並且this指針(值null )作為參數傳入。 該參數未取消引用(因為未使用任何成員變量),因此調用成功。

  2. 虛擬調用需要在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM