我的理解是,我们永远不能在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&#34;实例化&# 34;字符串)。
因此,如果std::vector::resize
为这些新对象分配空间,并允许我实际调用每个对象的方法,那么它们如何不完全实例化?我想我甚至无法在抽象类的向量上调用resize()
,但我可以,因为它们没有完全初始化。有人能解释一下这里发生了什么吗?
Brain fart..forgot指针的一个向量,当调整大小时,实际上并没有分配new
个元素,因为可以在没有分配元素的情况下创建指针,但是......为什么我仍然可以调用一个成员函数指向未实例化的类的指针?
答案 0 :(得分:2)
其他答案已经说出你的代码出了什么问题,我在这里回答编辑:
然而......为什么我仍然可以在指向a的指针上调用成员函数 没有实例化的类?
你的意思是这一部分:
v[0]->testMe();
v[0]->pureVirtualFunction(); //segfault on my machine
基本上,因为v[0]
未指向有效对象,但尝试调用testMe()
已经是未定义的行为。
它不是段错误的唯一原因是因为编译器重写了你的代码是不幸的。方法testMe()
的主体根本不需要*this
对象,因此编译器只需用cout
调用替换它。因为没有尝试访问某些无效的this
指针,所以还没有段错误。然后,当调用第二个方法时,编译器需要检查无效this
指针的vtable,这会导致段错误。不要永远依赖于testMe()
的行为,这仍然是未定义的行为,任何事情都可能发生。
答案 1 :(得分:2)
value_type
的{{1}}为vector
- 也就是说,该向量包含指针,可能是AbstractClass*
,指向a的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]
s vtable =&gt;崩溃。
答案 2 :(得分:0)
您制作了AbstractClass *
的向量,而不是AbstractClass
。
可以在没有指向对象的情况下创建指针。
由于函数是虚拟的,这些指针可以指向派生类&#39;不再是抽象的对象,所以调用是合法的 只有当您最终执行它时,系统才会跳过vptr表中的null并转储。