简体   繁体   English

Segfault在调用派生类的虚函数时

[英]Segfault when calling virtual function of derived class

I've been having a problem with segfaults when I call virtual function of a derived class. 当我调用派生类的虚函数时,我遇到了段错误的问题。 However, these segfaults do not occur if I change the name of the function to be different from the name of the virtual function in the base class. 但是,如果我将函数的名称更改为与基类中的虚函数的名称不同,则不会发生这些段错误。 Here's some code: 这是一些代码:

//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
}

Here is the base class header (the .cpp only instantiates variables in the constructor): 这是基类头(.cpp仅在构造函数中实例化变量):

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

And here is the code inside my Camera class I use to write the virtual function: 这是我用来编写虚函数的Camera类中的代码:

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

parse() is declared in the header file as virtual void parse(ifstream&); parse()在头文件中声明为虚拟void parse(ifstream&);

My problem here is that if I rename parse() inside Camera to something like CameraParse() and completely ignore the fact that there is a virtual function to be implemented, the code works completely fine! 我的问题是,如果我将Camera中的parse()重命名为CameraParse()并完全忽略了要实现的虚函数这一事实,那么代码完全正常!

Could you shed some light on why calling the virtual function causes a segfault? 你能否说明为什么调用虚函数导致段错? I've checked with Valgrind to see if there are any memory issues, and it tells me that there's an invalid read/write of 8 bytes. 我已经检查过Valgrind,看看是否有任何内存问题,它告诉我有8个字节的无效读/写。 I understand this means that I haven't allocated memory properly for my objects, but I don't know where I'm going wrong with the allocation. 我明白这意味着我没有为我的对象正确分配内存,但我不知道我在哪里分配错误。

Any help would be appreciated :) 任何帮助,将不胜感激 :)

You can't (just) malloc a non-POD object, you have to new it. 你不能(只) malloc一个非POD对象,你必须new它。

This is because malloc reserves the right amount of space, but doesn't construct the object, which is non-trivial for any class with virtual functions even if the constructor is defaulted. 这是因为malloc保留了适当的空间量,但是没有构造对象,这对于具有虚函数的任何类都是非平凡的,即使构造函数是默认的。

Now, the specific issue only arises here when you make a virtual function call, because this depends on the extra initialization carried out by new , but it's still wrong to use an un-constructed instance of any non-POD type. 现在,只有当您进行虚函数调用时才会出现特定问题,因为这取决于new执行的额外初始化,但使用任何非POD类型的未构造实例仍然是错误的。


Note that I'm using POD ( Plain Old Data ) as a lazy shorthand for anything with only trivial initialization. 请注意,我正在使用POD( 普通旧数据 )作为懒惰的简写,只需要进行简单的初始化。 In general, a class (or struct) is trivially initializable if neither it, nor any of its members or base classes have a constructor that does something . 通常,如果一个类(或结构)既不是它,也不是它的任何成员或基类都有一个构造函数可以执行某些操作 ,那么它就可以简单地初始化。 For our purposes, every class with one or more virtual methods (even if they're inherited, or in a data member) requires non-trivial initialization. 出于我们的目的,每个具有一个或多个虚方法的类(即使它们被继承,或者在数据成员中)都需要非平凡的初始化。

Specifically, the standard quote in Ben Voigt's answer describes two stages beginning the lifetime of an object (the time during which you can safely make method calls, especially virtual ones): 具体来说,Ben Voigt的答案中的标准引用描述了从对象的生命周期开始的两个阶段(您可以安全地进行方法调用的时间,尤其是虚拟方法):

  • storage with the proper alignment and size for type T is obtained, 获得具有适当对齐和T型尺寸的存储,

which happens when you call malloc 当你调用malloc时会发生这种情况

  • if the object has non-trivial initialization, its initialization is complete 如果对象具有非平凡的初始化,则其初始化完成

which only happens for a non-trivially-initialized type when you use new . 当您使用new时, 只会发生非平凡初始化类型。


For reference, this is the normal use closest to your existing code: 作为参考,这是最接近现有代码的常规用法:

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;

this is better style, though: 但这是更好的风格:

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

and only if you really need to use malloc or some other specific allocator: 并且只有当你真的需要使用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);

Use new, not malloc. 使用new,而不是malloc。 You allocated memmory, but did not created the object. 你分配了memmory,但没有创建对象。 Using new, with the respective constructor the virtual functions dispatch table will be created, and only then you can use it. 使用new,使用相应的构造函数,将创建虚函数调度表,然后才能使用它。 For example: 例如:

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

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

Alternatively you can use simple 或者你可以使用简单

camera = new Camera;

And somewhere catch the posible exception bad_alloc. 并在某处抓住可靠的异常bad_alloc。

malloc doesn't call constructors. malloc不会调用构造函数。

You have to change (The C++ way of creating objects) 你必须改变(C ++创建对象的方式)

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

to

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

Useless has provided the correct explanation. 无用已提供正确的解释。 Here is the requirement in the Standard (section 3.8): 以下是标准中的要求(3.8节):

The lifetime of an object is a runtime property of the object. 对象的生命周期是对象的运行时属性。 An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. 如果一个对象属于类或聚合类型,并且它或其成员之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非平凡的初始化。 [ Note: initialization by a trivial copy/move constructor is non-trivial initialization. [注意:通过简单的复制/移动构造函数进行初始化是非平凡的初始化。 — end note ] - 结束说明]

The lifetime of an object of type T begins when: 类型T对象的生命周期始于:

  • storage with the proper alignment and size for type T is obtained, and 获得具有适当对齐和T型尺寸的存储, 并且

  • if the object has non-trivial initialization, its initialization is complete. 如果对象具有非平凡的初始化,则其初始化完成。

The lifetime of an object of type T ends when: 类型T的对象的生命周期在以下情况下结束:

  • if T is a class type with a non-trivial destructor, the destructor call starts, or 如果T是具有非平凡析构函数的类类型,则析构函数调用将启动,或者

  • the storage which the object occupies is reused or released. 对象占用的存储器被重用或释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM