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

时间:2009-04-02 16:52:55

标签: c++ inheritance polymorphism segmentation-fault

我还在学习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中了解这些概念的人通常使用继承和多态性有任何其他提示,请告诉我们。谢谢!

7 个答案:

答案 0 :(得分:8)

name - 在Base

中未初始化

你还有另外一个问题:

  Base c = Child("2");

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

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

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

答案 1 :(得分:4)

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

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

除此之外,当你说

Base b = Child( "xxx" );

然后将Child实例切成一个Base,这可能不是你想要的。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

哇那里。

有一些问题,但你的段错误可能是因为你传递的是char* - 这只是一个指针,然后在{{1}中尝试cout }。问题是,指针不在disp()中,它位于disp()。您可能希望深度复制main(),或使用char*。这样做是行不通的。

编辑

参见编辑2

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

name

在析构函数中,你需要做类似的事情:

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

注意:我的语法可能完全错误,并且我意识到上面的代码本质上是不安全的,因为你没有检查delete [] this->name; 以确保它不是NULL,并且你没有检查返回值char*然而,这应该让你开始。

编辑2: 我纠正了。字符串文字被视为常量存储,因此在程序的持续时间内保持不变。 然而,我相信这一课很重要:一般来说,当不处理字符串文字,传递指针(或数组等)时,你需要分配它的存储和深层复制。在销毁所述对象时,您还需要适当地取消分配。

答案 4 :(得分:1)

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

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

会调用Child :: disp()。

答案 5 :(得分:0)

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

分段问题正在发生,因为它包含垃圾值。您需要在构造函数中初始化它。

答案 6 :(得分:0)

您的代码存在一些问题。

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

class Base
{
protected:
  char *name;

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

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

其次,你在这里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;
}