调整 std::vector<AbstractClass*> 的大小

Resize a std::vector<AbstractClass*>

本文关键字:gt AbstractClass std lt 调整 vector      更新时间:2023-10-16

这是我的理解,我们永远不能实例化一个抽象类在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也没有显示100倍的"实例化"字符串)。

所以如果std::vector::resize为这些新对象分配空间,并允许我在每个对象上实际调用方法,它们如何没有完全实例化?我想我甚至不能在抽象类的向量上调用resize(),但我可以,我认为因为它们没有完全初始化。有人能解释一下这里发生了什么吗?

编辑:

Brain放屁…忘记了指针向量,当调整大小时,实际上并不分配new元素,因为指针可以在没有分配元素的情况下创建,但是…为什么我仍然可以调用一个指针上的成员函数到一个类,其中没有实例化?

其他的答案已经说了你的代码有什么问题,我在这里回答编辑:

然而

…为什么我仍然可以在指针上调用成员函数类中未实例化的?

你是指这部分吗?

  v[0]->testMe();
  v[0]->pureVirtualFunction(); //segfault on my machine

基本上,因为v[0]没有指向一个有效的对象,但试图调用testMe()已经是未定义的行为。

它没有分段错误的唯一原因是因为你(不)幸运,编译器重写了你的代码。方法testMe()的主体根本不需要*this对象,因此编译器只会将其替换为cout调用。因为没有尝试访问一些无效的this指针,所以还没有段故障。然后,在调用第二个方法时,编译器需要检查无效this指针的虚函数表,这会导致段错误。不要依赖testMe()的行为,这仍然是未定义的行为,任何事情都可能发生

您的vectorvalue_typeAbstractClass* -也就是说,向量包含指针可以是nullptr,指向AbstractClass派生的有效实例或内存中的随机位置的指针。

vector::resize(N)创建N默认初始化的元素,同样,向量的value_type*,所以它将创建nullptrN实例。

AbstractClass::testMe是非虚成员函数。因此,在底层,它只是一个简单的函数,将一个隐藏的this指针作为其第一个参数。

testMe的主体本身不访问抽象类的任何元素,所以它永远不会 this解除引用。

因此,

v[0]->testMe();

调用AbstractClass::testMe(nullptr, );,不取消对this的引用。

v[0]->pureVirtualFunction(); //segfault on my machine

尝试解引用nullptr以找到v[0] s vtable => crash.

您制作的矢量是AbstractClass *,而不是AbstractClass

指针可以在没有对象指向的情况下创建。

由于函数是虚函数,这些指针可以指向派生类的对象,这些对象不再是抽象的,因此调用将是合法的。
只有当您最终执行它时,系统才会跳过vptr表中的null并转储