Segfault со встроенными структурами и виртуальными функциями - PullRequest
0 голосов
/ 30 января 2011

У меня есть такие структуры:

struct A
{
     int a;
     virtual void do_stuff(A*a)
     {
          cout << "I'm just a boring A-struct: " << a << endl;
     }
}

struct B
{
     A a_part;
     char * bstr;
     void do_stuff(B*bptr)
     {
          cout << "I'm actually a B-struct! See? ..." << bptr->bstr << endl;
     }
}

B * B_new(int n, char * str)
{
     B * b = (B*) malloc(sizeof(struct B));
     b->a_part.a = n;
     b->bstr = strdup(str);
     return b;
}

Теперь, когда я сделаю это:

char * blah = strdup("BLAAARGH");
A * b = (A*) B_new(5, blah);
free(blah);
b->do_stuff(b);

Когда я вызываю do_stuff, у меня возникает ошибка на последней строке, и я не знаю почему. Я впервые работаю с виртуальными функциями в таких структурах, поэтому я совершенно растерялся. Любая помощь будет принята с благодарностью!

Примечание: вызовы функций ДОЛЖНЫ быть в том же формате, что и последняя строка с точки зрения типа аргумента, поэтому я не использую классы или наследование.

Ответы [ 3 ]

2 голосов
/ 30 января 2011

Вы смешиваете идиому C (встроенные структуры) с концепциями C ++ (виртуальные функции).В C ++ необходимость встроенных структур устраняется за счет классов и наследования.virtual функции влияют только на классы в одной иерархии наследования.В вашем случае нет никакой связи между A и B, поэтому A s doStuff всегда будет вызываться.

Ваша ошибка, вероятно, вызвана тем, что b являетсядействительно B, но присваивается A*.Когда компилятор видит b->doStuff, он пытается перейти к vtable, чтобы посмотреть, какую версию doStuff вызвать.Однако B не имеет vtable, поэтому ваша программа аварийно завершает работу.

В C ++ класс без виртуальных функций, который не наследуется от каких-либо других классов, выложен в точности как структура C.

class NormalClass
{
      int a;
      double b;

 public:
      NormalClass(int x, double y);
};

выглядит следующим образом:

+------------------------------------+
| a (4 bytes) | b (8 bytes)          |
+------------------------------------+

Однако класс (или структура) с виртуальными функциями также имеет указатель на vtable, что позволяет использовать версию полиморфизма C ++.Таким образом, такой класс:

 class ClassWithVTable
 {
      int a;
      double b;

   public:
       ClassWithVTable();
       virtual void doSomething();
 };

размещается в памяти следующим образом:

  +-----------------------------------------------------------+
  | vptr (sizeof(void *)) | a (4 bytes) | b (8 bytes)         |
  +-----------------------------------------------------------+

и vptr указывают на таблицу, определяемую реализацией, которая называется vtable,по сути, это массив указателей на функции.

1 голос
/ 30 января 2011

Приведение B * к A * с последующей попыткой разыменования его с помощью вызова функции-члена является неопределенным поведением.Одной из возможностей является ошибка сегмента.Я не говорю, что это определенно причина, но это не очень хорошее начало.

Я не понимаю, почему вы здесь не используете наследование!

0 голосов
/ 30 января 2011

Для полиморфных объектов указатель на виртуальную таблицу хранится внутри объекта.
Таким образом, во время выполнения метод, который должен быть вызван на самом деле, определяется путем разыменования и перехода в виртуальную таблицу.
В вашем случае вы приводите *От 1003 * до A *.
Поскольку A является полиморфным, вызов метода будет определен через vtable, но поскольку используемый объект на самом деле B, используемый vpointer фактически является мусором, и вы получаете ошибку сегмента,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...