Segfault时调用虚函数的派生类 [英] Segfault when calling virtual function of derived class

查看:136
本文介绍了Segfault时调用虚函数的派生类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  // in main 
//初始化场景对象
// camera
if((camera =(Camera *)malloc(sizeof(Camera)))== NULL){
cout< 不能为相机分配存储器< endl
}
//...code in middle
//内部文件解析...
// infile是一个ifstream
// nextString是一个char *
if(!strcmp(nextString,camera)){
camera-> parse(infile); // segfault here
}

这里是基类头在构造函数中的变量):

  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()和完全忽略的事实,有一个虚拟的函数要实现,代码工作完全正常! p>

您能否了解为什么调用虚函数会导致segfault?我已经检查Valgrind,看看是否有任何内存问题,它告诉我有一个无效的读/写8字节。我明白这意味着我没有为我的对象正确分配内存,但我不知道我的分配错误。



任何帮助)

解决方案

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



这是因为 malloc 保留正确的空间量,但不会构造对象,即使构造函数是默认的,对于任何具有虚函数的类也不重要。



现在,当你进行虚拟函数调用时,特定的问题只出现在这里,因为这取决于 new ,但是使用任何非POD类型的未构建实例仍然是错误的。






'm使用POD(普通旧数据)作为一个懒惰的简写只是简单的初始化。一般来说,一个类(或结构体)如果没有,或者它的任何成员或基类都有一个做某事的构造函数,它是可以初始化的。对于我们的目的,每个具有一个或多个虚方法的类(即使它们是继承的,或在数据成员中)需要非平凡的初始化。



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



  • 获得类型T的正确对齐和大小的存储,


当您调用 malloc

时发生



  • 如果对象具有不重要的初始化,则其初始化完成


>




$ b

时,只有发生在非简单初始化类型
$ b

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

  =新相机; 
//不需要检查NULL,这将抛出std :: bad_alloc如果失败
camera-> parse(file);
//不要忘记:
delete camera;

这是更好的风格,但:

  std :: unique_ptr< Camera>相机(新相机); 
camera-> parse(file);
//为您处理的销毁

且仅当真的需要使用 malloc 或其他特定分配器:

  camera =(Camera *)malloc(sizeof(* camera)); 
new(camera)相机; //将你的指针变成一个真实的对象
camera-> parse(file);
//破坏变得丑陋虽然
camera->〜Camera();
免费(相机);


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):

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:

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

parse() is declared in the header file as virtual 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!

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. 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.

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.

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.


Note that I'm using POD (Plain Old Data) as a lazy shorthand for anything with only trivial initialization. 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):

  • storage with the proper alignment and size for type T is obtained,

which happens when you call 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.


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:

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

这篇关于Segfault时调用虚函数的派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆