![](/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.