3 回答

TA貢獻1757條經驗 獲得超7個贊
答案取決于您的觀點:
如果按照C ++標準進行判斷,則無法獲得空引用,因為您首先會獲得未定義的行為。在第一次發生未定義行為之后,該標準允許發生任何事情。因此,如果您編寫*(int*)0,那么從語言標準的角度來看,您已經具有未定義的行為,即取消引用空指針。程序的其余部分無關緊要,一旦執行了此表達式,您就退出了游戲。
但是,實際上,可以很容易地從空指針創建空引用,直到您真正嘗試訪問空引用后面的值時,您才會注意到。您的示例可能有點太簡單了,因為任何好的優化編譯器都會看到未定義的行為,并簡單地優化掉所有依賴于它的內容(甚至不會創建null引用,它也會被優化掉)。
但是,優化工作取決于編譯器來證明未定義的行為,這可能是不可能做到的??紤]一下文件內的這個簡單函數converter.cpp:
int& toReference(int* pointer) {
return *pointer;
}
當編譯器看到此函數時,它不知道指針是否為空指針。因此,它只是生成將任何指針轉換為相應引用的代碼。(順便說一句,這是一個小問題,因為指針和引用在匯編程序中是完全相同的野獸。)現在,如果您還有另一個user.cpp包含代碼的文件
#include "converter.h"
void foo() {
int& nullRef = toReference(nullptr);
cout << nullRef; //crash happens here
}
編譯器不知道toReference()將取消對傳遞的指針的引用,并假定它返回有效的引用,實際上,該引用將為空引用。調用成功,但是當您嘗試使用引用時,程序崩潰。希望。該標準允許發生任何事情,包括出現粉紅色大象。
您可能會問為什么這是相關的,畢竟,內部已經觸發了未定義的行為toReference()。答案是調試:空引用可能像空指針一樣傳播和擴散。如果您不知道可以存在空引用,并學會避免創建空引用,則可能要花費大量時間來弄清楚為什么僅在嘗試讀取普通舊int成員時成員函數似乎崩潰了(答案:實例在該成員的調用中是一個空引用,this一個空指針也是如此,并且您的成員被計算為位于地址8)。
那么如何檢查空引用呢?你給了電話
if( & nullReference == 0 ) // null reference
在你的問題。嗯,那是行不通的:按照標準,如果取消引用空指針,您將具有未定義的行為,并且不創建空引用就無法創建空引用,因此,空引用僅存在于未定義行為的領域內。由于編譯器可能會假設您未觸發未定義的行為,因此它可以假設不存在空引用(即使它很容易發出生成空引用的代碼?。_@樣,它看到了if()條件,得出結論說它不可能成立,而只是丟棄了整個if()語句。隨著鏈接時間優化的引入,以魯棒的方式檢查空引用已變得毫無可能。
TL; DR:
空引用有些可怕地存在:
它們的存在似乎是不可能的(按標準而言),
但是它們卻存在(按生成的機器代碼來確定),
但是您看不到它們是否存在(=您的嘗試將被優化),
但無論如何它們可能會殺死您(=您的程序在怪異的點崩潰,甚至更糟)。
您唯一的希望是它們不存在(=編寫您的程序以不創建它們)。
我希望那不會困擾您!

TA貢獻1825條經驗 獲得超4個贊
如果您打算找到一種在單例對象枚舉中表示空值的方法,那么(取消)引用空值(C ++ 11,nullptr)是一個壞主意。
為什么不按如下所示在類中聲明表示NULL的靜態單例對象,并添加一個返回nullptr的強制轉換指針運算符?
編輯:更正了幾種錯誤類型,并在main()中添加了if語句,以測試實際工作的強制轉換指針操作符(我忘了..我的錯)-2015年3月10日-
// Error.h
class Error {
public:
static Error& NOT_FOUND;
static Error& UNKNOWN;
static Error& NONE; // singleton object that represents null
public:
static vector<shared_ptr<Error>> _instances;
static Error& NewInstance(const string& name, bool isNull = false);
private:
bool _isNull;
Error(const string& name, bool isNull = false) : _name(name), _isNull(isNull) {};
Error() {};
Error(const Error& src) {};
Error& operator=(const Error& src) {};
public:
operator Error*() { return _isNull ? nullptr : this; }
};
// Error.cpp
vector<shared_ptr<Error>> Error::_instances;
Error& Error::NewInstance(const string& name, bool isNull = false)
{
shared_ptr<Error> pNewInst(new Error(name, isNull)).
Error::_instances.push_back(pNewInst);
return *pNewInst.get();
}
Error& Error::NOT_FOUND = Error::NewInstance("NOT_FOUND");
//Error& Error::NOT_FOUND = Error::NewInstance("UNKNOWN"); Edit: fixed
//Error& Error::NOT_FOUND = Error::NewInstance("NONE", true); Edit: fixed
Error& Error::UNKNOWN = Error::NewInstance("UNKNOWN");
Error& Error::NONE = Error::NewInstance("NONE");
// Main.cpp
#include "Error.h"
Error& getError() {
return Error::UNKNOWN;
}
// Edit: To see the overload of "Error*()" in Error.h actually working
Error& getErrorNone() {
return Error::NONE;
}
int main(void) {
if(getError() != Error::NONE) {
return EXIT_FAILURE;
}
// Edit: To see the overload of "Error*()" in Error.h actually working
if(getErrorNone() != nullptr) {
return EXIT_FAILURE;
}
}
- 3 回答
- 0 關注
- 860 瀏覽
添加回答
舉報