2

当我调用派生类的虚函数时,我遇到了段错误问题。但是,如果我将函数的名称更改为与基类中的虚拟函数的名称不同,则不会发生这些段错误。这是一些代码:

//in main
//initialize scene objects
//camera
if((camera = (Camera*)malloc(sizeof(Camera))) == NULL){
  cout << "Could not allocate memory for camera" << endl;
}
//...code in middle
//inside file parsing...
//infile is an ifstream
//nextString is a char*
if(!strcmp(nextString,"camera")){
  camera->parse(infile); //segfault here
}

这是基类头文件(.cpp 只在构造函数中实例化变量):

class WorldObj{
public:
  WorldObj();
  ~WorldObj();
  virtual void parse(ifstream&) =0;
  vec3 loc; //location
};

这是我用来编写虚函数的 Camera 类中的代码:

void Camera::parse(ifstream &infile){
  //do parsing stuff
}

parse() 在头文件中声明为 virtual void parse(ifstream&);

我的问题是,如果我将 Camera 中的 parse() 重命名为 CameraParse() 之类的名称,并且完全忽略了要实现的虚函数这一事实,则代码完全可以正常工作!

你能解释一下为什么调用虚函数会导致段错误吗?我检查了 Valgrind 是否有任何内存问题,它告诉我有 8 个字节的无效读/写。我理解这意味着我没有为我的对象正确分配内存,但我不知道分配哪里出错了。

任何帮助,将不胜感激 :)

4

4 回答 4

8

你不能(只是)malloc一个非 POD 对象,你必须这样new做。

这是因为malloc保留了适量的空间,但不构造对象,这对于任何具有虚函数的类来说都是不平凡的,即使构造函数是默认的。

现在,具体问题仅在您进行虚函数调用时出现,因为这取决于执行的额外初始化new,但使用任何非 POD 类型的未构造实例仍然是错误的。


请注意,我使用 POD(Plain Old Data)作为只有微不足道初始化的任何事物的惰性速记。通常,如果一个类(或结构)或其任何成员或基类都没有执行某些操作的构造函数,则该类(或结构)是可以简单初始化的。出于我们的目的,每个具有一个或多个虚拟方法的类(即使它们是继承的,或者在数据成员中)都需要非平凡的初始化。

具体来说,Ben Voigt 的回答中的标准引用描述了开始对象生命周期的两个阶段(您可以安全地进行方法调用,尤其是虚拟方法调用的时间):

  • 获得类型 T 的具有适当对齐和大小的存储,

当你打电话时会发生这种情况malloc

  • 如果对象有非平凡的初始化,它的初始化就完成了

您使用new.


作为参考,这是最接近您现有代码的正常用法:

Camera *camera = new Camera;
// don't need to check for NULL, this will throw std::bad_alloc if it fails
camera->parse(file);
// don't forget to:
delete camera;

不过,这是更好的风格:

std::unique_ptr<Camera> camera(new Camera);
camera->parse(file);
// destruction handled for you

并且仅当您确实需要使用malloc或其他特定分配器时:

Camera *camera = (Camera *)malloc(sizeof(*camera));
new (camera) Camera; // turn your pointer into a real object
camera->parse(file);
// destruction becomes uglier though
camera->~Camera();
free(camera);
于 2013-04-08T21:01:35.297 回答
1

使用 new,而不是 malloc。您分配了内存,但没有创建对象。使用 new 时,将使用相应的构造函数创建虚拟函数调度表,然后您才能使用它。例如:

if((camera = new (nothrow) Camera()) == NULL){
  cout << "Could not allocate memory for camera" << endl;
}

if(!strcmp(nextString,"camera")){
  camera->parse(infile); //segfault here
}

或者,您可以使用简单的

camera = new Camera;

在某处捕获可能的异常 bad_alloc。

于 2013-04-08T21:02:29.900 回答
1

malloc不调用构造函数。

你必须改变(创建对象的 C++ 方式)

camera = (Camera*)malloc(sizeof(Camera)))

camera = new Camera; // Now you camera object will be created and constructed.
于 2013-04-08T21:03:15.157 回答
1

无用提供了正确的解释。这是标准中的要求(第 3.8 节):

对象的生命周期是对象的运行时属性。如果一个对象是一个类或聚合类型,并且它或它的一个成员由一个普通默认构造函数以外的构造函数初始化,则称该对象具有非普通初始化。[注意:通过简单的复制/移动构造函数进行初始化是非平凡的初始化。——尾注]

类型对象的生命周期T开始于:

  • 获得具有适当对齐和大小的类型T的存储,并且

  • 如果对象有非平凡的初始化,它的初始化就完成了。

类型对象的生命周期在以下情况下T结束:

  • 如果T是具有非平凡析构函数的类类型,则析构函数调用开始,或者

  • 对象占用的存储空间被重用或释放。

于 2013-04-08T21:07:15.617 回答