![](/img/trans.png)
[英]Why can't we declare a std::vector<AbstractClass>?
[英]Resize a std::vector<AbstractClass*>
據我了解,我們永遠無法實例化C++
的抽象類或Java
的接口。 這是有道理的,因為我們有不提供任何實現的“純虛擬”函數,因此我們只能實例化此類類/接口的子類,這些子類遵守由抽象類形成的協定。 考慮以下代碼:
#include <iostream>
#include <vector>
using namespace std;
class AbstractClass {
public:
AbstractClass() {
cout << "Instantiating" << endl;
}
virtual void pureVirtualFunction() = 0;
void testMe() {
cout << "test" << endl;
}
};
int main() {
vector<AbstractClass*> v;
v.resize(100);
cout << v.size() << endl;
v[0]->testMe();
v[0]->pureVirtualFunction(); //segfault on my machine
return 0;
}
當我在計算機上運行此命令時,令我感到困惑的是我實際上可以調整向量的大小。 我一直以為std::vector::resize
實例化了一些元素,因此調用了每個元素的構造函數,但進一步的研究表明該構造函數並未被實際調用(我的stdout也沒有顯示100x“ Instantiating”字符串也很明顯)。
因此,如果std::vector::resize
為這些新對象分配空間,並允許我實際在每個對象上調用方法,那么它們如何不完全實例化? 我以為我什至不能在抽象類的向量上調用resize()
,但是我可以,因為它們沒有完全初始化。 有人可以解釋這里發生了什么嗎?
腦子放屁..忘記了指針向量在調整大小時實際上並沒有分配new
元素,因為可以在沒有后面分配元素的情況下創建指針,但是...為什么我仍然可以在指向類的指針上調用成員函數在哪個沒有實例化?
其他答案已經說明您的代碼出了什么問題,我在這里回答您的修改:
但是...為什么我仍然可以在未實例化的類的指針上調用成員函數?
您特別是指這部分:
v[0]->testMe();
v[0]->pureVirtualFunction(); //segfault on my machine
基本上,因為v[0]
並不指向有效的對象,但是嘗試調用testMe()
行為已經是未定義的行為。
它不會進行段錯誤的唯一原因是因為您很不幸編譯器重寫了您的代碼。 您的方法testMe()
主體根本不需要*this
對象,因此編譯器將僅將其替換為cout
調用。 因為沒有嘗試訪問一些無效的this
指針是由沒有段錯誤呢。 然后,在調用第二種方法時,編譯器需要檢查無效的this
指針的vtable,這將導致segfault。 永遠不要依賴testMe()
的行為,這仍然是不確定的行為,任何事情都可能發生。
vector
的value_type
為AbstractClass*
-也就是說,向量包含的指針可能為nullptr
,即指向AbstractClass
派生的有效實例或內存中隨機位置的指針。
vector::resize(N)
創建N
默認初始化的元素,並且向量的value_type
同樣是*
,因此它將創建N
個nullptr
實例。
AbstractClass::testMe
是一個非虛擬成員函數。 因此,在底層,它只是一個簡單的函數,將隱藏的this
指針作為其第一個參數。
testMe
主體本身不訪問抽象類的任何元素,因此它永遠不會取消引用 this
。
從而
v[0]->testMe();
調用AbstractClass::testMe(nullptr, );
並且從不取消引用this
。
v[0]->pureVirtualFunction(); //segfault on my machine
嘗試取消引用nullptr
來查找v[0]
的vtable =>崩潰。
您創建了AbstractClass *
的向量,而不是AbstractClass
的向量。
可以創建沒有指向對象的指針。
由於該函數是虛擬的,所以這些指針可能指向不再抽象的派生類的對象,因此該調用將是合法的。
僅當最終執行它時,系統才會越過vptr表中的null並轉儲。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.