为啥在此代码中调用虚拟方法时会出现分段错误?

Posted

技术标签:

【中文标题】为啥在此代码中调用虚拟方法时会出现分段错误?【英文标题】:Why do I get a segmentation fault when calling a virtual method in this code?为什么在此代码中调用虚拟方法时会出现分段错误? 【发布时间】:2009-04-02 16:52:54 【问题描述】:

我还在学习 C++;我正在尝试多态性的工作原理,但在调用虚拟方法时遇到了分段错误。

(注意:我没有将析构函数标记为虚拟,我只是想看看会发生什么。)这是代码:

#include <iostream>

using namespace std;

class Base

protected:
  char *name;

public:
  Base(char *name)
  
    cout << name << ": Base class cons" << endl;
  

  ~Base()
  
    cout << name << ": Base class des" << endl;
  

  virtual void disp();
;

void Base::disp()

  cout << name << ": Base disp()" << endl;


class Child : public Base

public:
  Child(char *name):
    Base(name)
  
    cout << name << ": Child class cons" << endl;
  

  ~Child()
  
    cout << name << ": Child class des" << endl;
  

  virtual void disp()
  
    cout << name << ": Child disp()" << endl;
  
;


int main()

  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();

另外,如果您对了解 Java 中这些概念的人有任何其他关于继承和多态使用的一般提示,请告诉我。谢谢!

【问题讨论】:

请显示导致错误的调用代码。 bayda 称之为。要修复分段错误,您需要重写 Base 构造函数以读取 Base(char *name) : name(name) ... 【参考方案1】:

name - 在 Base 中未初始化

还有一个问题:

  Base c = Child("2");

我不认为这是你想要的。您的代码将从强制转换的 Child 创建一个 Base 实例。但我认为您希望使用基于 Base 接口的 Child 实例;你应该写:

  Base *c = new Child("2");

另外,为避免将来出现错误,请将 base 中的析构函数声明为虚拟。

【讨论】:

那么,如果我需要多态行为,那么我是否必须将我的对象存储在堆中? 并非如此。你可以这样。孩子 a("2");基 *ptr = &a; 没有。您可以在堆栈上创建对象,然后通过 Base.. 上的引用将其作为参数传递给某个函数,并且您将具有多态行为。或如@chappar 回答(在之前的评论中)。 不要停止思考 :-) 这只是因为对象的存储位置在 Java 和 .NET 中始终相同,而在 C++ 中,您可以决定将其存储在堆或堆栈上。 (也可以将对象放在其他内存位置[共享内存等],但那是给怪胎的:-)) @Srikanth:问你有没有问题。【参考方案2】:

您永远不会初始化基础 nenber 变量 - 您的基础构造函数应该是:

Base(char * aname) : name( aname )
  
    cout << name << ": Base class cons" << endl;
  

还有,当你说

Base b = Child( "xxx" );

然后 Child 实例将被切分成 Base,这可能不是您想要的。

【讨论】:

【参考方案3】:

我认为您没有将成员 char * 名称分配给您的 ctors 中的任何内容。

【讨论】:

【参考方案4】:

永远不会调用 Child::disp() 方法 - c 是 Base 类型的变量,而不是指针或引用,因此它不会检查虚拟方法。

Base * c = new Child("1");
c->disp();
delete c;

会调用 Child::disp()。

【讨论】:

【参考方案5】:

哇哦。

有一些问题,但是你的段错误可能是因为你传递了一个char*——它只是一个指针,然后试图在disp()cout它。问题是,该指针不存在于disp(),它存在于main()。您可能想要深度复制char*,或者使用std::string。这样做是行不通的。

编辑

见编辑 2

您不能只为类的name 变量分配名称。如果你这样做,你会得到不可预知的结果——你可能仍然会出现段错误。请记住:在 C/C++ 中,除非在堆上分配对象,否则对象是局部范围的。在这种情况下,在您的 ctor 中,您需要执行以下操作:

this->name = new char[ strlen( name ) + 1 ];
strcpy( this->name, name );

在析构函数中,您需要执行以下操作:

delete [] this->name;

注意:我的语法可能完全错误,并且我意识到上面的代码本质上是不安全的,因为您没有检查 char* 以确保它不是 NULL,并且您没有检查 @987654332 的返回值@ 不过,这应该可以帮助您入门。

编辑 2: 我站得更正了。字符串文字被视为常量存储,因此在程序期间存在。 尽管如此,我相信这个教训很重要:一般来说,当不处理字符串文字时,传递一个指针(或数组等),你需要分配存储它和深拷贝。您还需要在销毁所述对象时适当地取消分配。

【讨论】:

这不是问题(如果构造函数实际存储了参数,也不会出现问题)。字符串文字具有静态存储持续时间。当然,如果有人从具有动态或自动存储持续时间的字符串创建了 Child,并且 Child 的寿命超过了字符串,这将是一个问题。 @FreeMemory - 您在这里所说的几乎所有内容都是错误的。保存指向字符串文字的指针是完全可以的 - 无需复制它。 具体见标准中的2.13.4/1和3.7.1/1。 @Neil Butterworth,@onebyone:我的立场是正确的。您对字符串文字的引用是正确的。 @FreeMemory:即使在edit2之后,将指针存储到不同的对象中也没有错,甚至在许多情况下都需要。也就是说,只要您知道指向的对象比指针的时间跨度长。【参考方案6】:

这里的问题很少。首先是你的基类析构函数必须是虚拟的。否则,即使它指向派生对象,也将始终调用您的基类析构函数。其次是您不应该将派生类对象分配给基类对象。这称为对象切片。 所以,赋值应该通过指针或引用来完成。

发生分段问题是因为它包含垃圾值。您需要在构造函数中对其进行初始化。

【讨论】:

【参考方案7】:

您的代码存在一些问题。

首先,您得到段错误的原因是 Base ctor 的实现采用与类的成员变量之一同名的参数:

class Base

protected:
  char *name;

public:
  Base(char ***name**)
  
    cout << name << ": Base class cons" << endl;
  

ctor的参数'name'hides类的成员变量同名,erm...名字。

其次,你是slicing你的对象:

int main()

  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();

'c' 是 Base 类型,您正在尝试为其分配一个 Child。当您将 ogbject 分配给基类时,Child 独有的所有内容都将被切掉。

下面是解决这两个问题的代码:

#include <iostream>
#include <string>

using namespace std;

class Base

protected:
    std::string name_;

public:
  Base(char *name)
      : name_(name) 
    cout << name_ << ": Base class cons" << endl;
  

  ~Base()
  
    cout << name_ << ": Base class des" << endl;
  

  virtual void disp();
;

void Base::disp()

  cout << name_ << ": Base disp()" << endl;


class Child : public Base

public:
  Child(char *name):
    Base(name)
  
    cout << name_ << ": Child class cons" << endl;
  

  ~Child()
  
    cout << name_ << ": Child class des" << endl;
  

  virtual void disp()
  
    cout << name_ << ": Child disp()" << endl;
  
;


int main()

  //Base b;
  //b.disp();
  Base * c = new Child("2");
  c->disp();
  delete c;

【讨论】:

以上是关于为啥在此代码中调用虚拟方法时会出现分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在此 C++ 代码中出现分段错误?

为啥存储此向量时会出现分段错误?

为啥在使用 Python/C API 时会出现此段错误?

为啥 C++ 标准向量在分配或调整大小时会出现段错误? [关闭]

为啥当我发出信号时会出现分段错误

为啥我使用 react-native juspay 开始付款时会出现此错误?