Почему я получаю ошибку сегментации при вызове виртуального метода в этом коде? - PullRequest
1 голос
/ 02 апреля 2009

Я все еще изучаю 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 ]

8 голосов
/ 02 апреля 2009

имя - неустановлено в Base

также у вас есть другая проблема:

  Base c = Child("2");

Не думаю, что ты этого хочешь. Ваш код создаст экземпляр Base из приведённого Child. Но я думаю, что вы хотите работать с экземпляром Child на основе интерфейса Base; вместо этого вы должны написать:

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

также, чтобы избежать будущих ошибок, объявите деструктор в базе как виртуальный.

4 голосов
/ 02 апреля 2009

Вы никогда не инициализируете переменную base nenber - ваш базовый конструктор должен быть:

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

Как и то, когда вы говорите

Base b = Child( "xxx" );

тогда экземпляр Child будет разделен на Base, что, вероятно, не то, что вы хотите.

1 голос
/ 02 апреля 2009

Метод Child :: disp () никогда не будет вызван - c является переменной типа Base, а не указателем или ссылкой, поэтому он не проверяет виртуальные методы.

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

вызовет Child :: disp ().

1 голос
/ 02 апреля 2009

Ух ты.

Есть несколько проблем, , но ваш segfault, вероятно, из-за того, что вы передаете char* - просто указатель, а затем пытаетесь cout ввести его в disp(). Проблема в том, что указатель не живет в 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, и вы не проверяете возвращаемое значение new. Тем не менее, это должно начать вас.

РЕДАКТИРОВАТЬ 2: Я стою исправлено. Строковые литералы обрабатываются как постоянное хранилище и, таким образом, живут в течение всей программы Тем не менее , урок, на мой взгляд, важен: в общем случае, когда не имеет дело со строковыми литералами , передавая указатель (или массив и т. Д.), Вам нужно выделить память для это и глубокая копия. Вам также необходимо соответствующим образом отменить выделение при уничтожении указанного объекта.

1 голос
/ 02 апреля 2009

Я не думаю, что вы присваиваете имя члена char * чему-либо в ваших ctors.

0 голосов
/ 02 апреля 2009

У вас есть пара проблем с вашим кодом.

Во-первых, причина, по которой вы получаете ошибку, заключается в том, что базовый ctor принимает параметр с тем же именем, что и у одной из переменных-членов класса:

class Base
{
protected:
  char *name;

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

Параметр ctor 'name' скрывает переменную-член класса с тем же, erm ... name.

Во-вторых, вы нарезаете ваш объект здесь:

int main()
{
  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();
}

'c' имеет тип Base, и вы пытаетесь присвоить ему Child. Все вещи, которые являются уникальными для Child, будут вырезаны, когда вы назначите ogbject базовому классу.

Вот код, который решает обе эти проблемы:

#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;
}
0 голосов
/ 02 апреля 2009

Здесь мало проблем. Прежде всего, ваш деструктор базового класса должен быть виртуальным. В противном случае ваш деструктор базового класса будет вызываться всегда, даже если он указывает на производный объект. Во-вторых, вы не должны назначать объект производного класса объекту базового класса. Это называется разрезанием объектов. Таким образом, назначение должно быть сделано через указатель или ссылку.

Проблема сегментации происходит из-за того, что она содержит значение мусора. Вам нужно инициализировать его в конструкторе.

...