2012-05-10 5 views
2

У меня есть класс, который происходит из C-структуры. Класс не делает ничего особенного, кроме инициализации в конструкторе, функции деинициализации во время деструктора и нескольких других методов, которые вызывают функции C. По сути, это обертка для прокатки. Используя GCC, он жаловался, что мой деструктор не был виртуальным, поэтому я сделал это. Теперь я столкнулся с segfaults.Виртуальные функции в классах производных форм structs

/* C header file */ 
struct A 
{ 
    /* ... */ 
} 

// My C++ code 
class B : public A 
{ 
public: 
    B() { /* ... init ... */ } 
    virtual ~B() { /* ... deinit ... */ } 

    void do() 
    { 
    someCFunction(static_cast<A *>(this)); 
    } 
}; 

Я всегда был в предположении, что static_cast будет возвращать правильный указатель на базовый класс, отсечение от указателя виртуальной таблицы. Так что это может быть не так, так как я получаю segfault в функции C.

Удалив ключевое слово virtual, код работает нормально, за исключением того, что я получаю предупреждение gcc. Какая для этого лучшая работа? Не стесняйтесь просвещать меня :).

+1

Можете ли вы сделать A :: ~ A виртуальным? –

+0

Нет, я не могу. Это общедоступный заголовок С. – MarkP

+0

Почему бы не использовать класс вместо структуры? Разница очевидна, но та же цель достигнута. – Poni

ответ

4

И явное и неявное сотрудничество nversion до A* являются безопасными.Нет необходимости в явном приведении, и он не собирается вводить vtables нигде, или что-то в этом роде. Язык был бы принципиально непригодным, если бы это было не так.

Я всегда был в предположении, что static_cast вернется правильный указатель на базовый класс, отсечение от виртуальной таблицы указателя.

Совершенно верно.

деструктор должен быть virtual только если delete ptr; называется где ptr имеет тип A* - или деструктор вызывается вручную. И это был бы деструктор A, который должен был бы быть виртуальным, а это не так.

Независимо от проблемы в коде, это не имеет никакого отношения к показанному коду. Вам нужно значительно расширить свой выбор.

+1

Это, наверное, единственный разумный ответ в свете плохо представленного и исследованного вопроса. –

1

Деструктор базовых классов должен быть виртуальным. В противном случае есть вероятность, что вы столкнетесь с неопределенным поведением. Это просто предположение, поскольку кода недостаточно, чтобы рассказать о реальной причине.

Попробуйте сделать деструктор A виртуальным и посмотреть, не сработает ли он.

Обратите внимание, что class и struct - это то же самое, кроме уровня доступа по умолчанию, поэтому тот факт, что его класс и другая структура не имеют к нему никакого отношения.

EDIT: Если A является C-структурой, используйте композицию вместо наследования - т. Е. Имеет элемент A внутри B вместо его расширения. Нет смысла извлекать, поскольку полиморфизм не может быть и речи.

+0

Я не могу, он определен в общедоступном C-заголовке. – MarkP

+0

@MarkP ok, см. Отредактированный ответ. –

+1

Не могли бы вы объяснить, почему наследование с C-структуры нежелательно? Я понимаю, что при введении виртуального ключевого слова добавляется виртуальная таблица. Это также добавляет vtable в структуру? (как, например, при выполнении static_cast, он возвращает A с vtable?) – MarkP

1

Это не так, как static_cast работ. Указатель на объект по-прежнему остается одним и тем же указателем, только с другим типом. В этом случае вы конвертируете указатель в производный тип (B) в указатель на базовый тип (A).

Я полагаю, что наведение указателя фактически не изменяет значение указателя, т. Е. Оно все еще указывает на тот же адрес памяти, даже если он был введен в тип указателя A*. Помните, что struct и class являются синонимами на C++.

Как сказал @Luchian, если вы смешиваете C и C++, лучше сохранить простые старые C-структуры (и их указатели) как простые старые C-структуры и использовать тип композиции вместо наследования. В противном случае вы смешиваете различные реализации указателей под обложками. Нет никакой гарантии, что внутреннее расположение C-структуры и класса C++ одинаково.

UPDATE

Вы должны окружать декларацию C STRUCT с extern "C" спецификации, так что компилятор C++ знает, что структура является чисто C структура:

extern "C" 
{ 
    struct A 
    { 
     ... 
    }; 
} 

Или:

extern "C" 
{ 
#include "c_header.h" 
} 
+0

Так наследует ли от структуры C класс с виртуальным изменением макета самой структуры? – MarkP

+0

Если ничего больше, он добавляет скрытый элемент 'vtable'. –

+0

@Loadmaster: нет, структура не будет иметь vtable. 'static_cast' должен возвращать указатель на' A', без vtable. 'Static_cast' не должен быть проблемой. –

Смежные вопросы