调用派生类的虚函数时出现段错误

Segfault when calling virtual function of derived class

本文关键字:段错误 错误 派生 函数 调用      更新时间:2023-10-16

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

//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&);

我这里的问题是,如果我重命名parse()中的相机类似CameraParse()的东西,并完全忽略了有一个虚拟函数要实现的事实,代码工作完全正常!

你能解释一下为什么调用虚函数会导致段错误吗?我已经检查了Valgrind,看看是否有任何内存问题,它告诉我有一个无效的读/写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);

malloc不调用构造函数

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

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

camera = new Camera; // Now you camera object will be created and constructed.

Useless提供了正确的解释。以下是标准(第3.8节)中的要求:

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

类型为T的对象的生存期开始于:

  • T类型获得合适的对齐和大小的存储器,

  • 如果对象具有非平凡初始化,则表示初始化完成。

类型为T的对象的生存期结束于:

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

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

使用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