Действительно ли возможно разделить структуру C на подклассы в C++ и указателях использования на структуру в коде C?

24
задан Jonathan Leffler 30 May 2016 в 23:38
поделиться

10 ответов

Это полностью законно. В C ++ классы и структуры являются идентичными понятиями, за исключением того, что все члены структуры являются открытыми по умолчанию. Это единственная разница. Поэтому вопрос о том, можете ли вы расширить структуру, ничем не отличается от вопроса о том, можете ли вы расширить класс.

Здесь есть одна оговорка. Нет гарантии согласованности компоновки от компилятора к компилятору. Поэтому, если вы компилируете свой код C другим компилятором, чем ваш код C ++, вы можете столкнуться с проблемами, связанными с разметкой элементов (особенно с отступами). Это может произойти даже при использовании компиляторов C и C ++ от одного и того же поставщика.

У меня было , когда это происходило с gcc и g ++. Я работал над проектом, который использовал несколько больших структур. К сожалению, g ++ упаковал структуры значительно слабее, чем gcc, что вызвало значительные проблемы с совместным использованием объектов между кодом C и C ++. В конце концов нам пришлось вручную устанавливать упаковку и вставлять отступы, чтобы код C и C ++ обрабатывал структуры одинаково. Обратите внимание, что эта проблема может возникнуть независимо от подклассов. На самом деле мы не делали подклассы структуры C в этом случае.

28
ответ дан Derek Park 28 November 2019 в 23:10
поделиться

Я, конечно, не рекомендую использовать такое странное разделение на подклассы. Было бы лучше изменить Ваш дизайн для использования состава вместо наследования. Просто сделайте одного участника

нечто* m_pfoo;

в классе панели и это сделает то же задание.

Другая вещь, которую можно сделать, состоит в том, чтобы сделать еще один класс FooWrapper, содержа структуру сам по себе с соответствующим методом получателя. Тогда можно разделить обертку на подклассы. Таким образом, проблемы с виртуальным деструктором не стало.

11
ответ дан m_pGladiator 28 November 2019 в 23:10
поделиться

«Никогда не производные от конкретных классов». - Саттер

«Сделайте не листовые классы абстрактными». - Мейерс

Подкласс неинтерфейса просто неправильно классы. Вы должны реорганизовать свои библиотеки.

Технически, вы можете делать то, что вы хотите, если вы не вызываете неопределенное поведение, e. например, удаление указателя на производный класс указателем на его подобъект базового класса. Вам даже не нужно extern "C" для кода C ++. Да, это портативный. Но это плохой дизайн.

3
ответ дан Roman Odaisky 28 November 2019 в 23:10
поделиться

Это совершенно законно, хотя это могло бы сбивать с толку других программистов.

можно использовать наследование для расширения C-структур с помощью методов и конструкторов.

Образец:

struct POINT { int x, y; }
class CPoint : POINT
{
public:
    CPoint( int x_, int y_ ) { x = x_; y = y_; }

    const CPoint& operator+=( const POINT& op2 )
    { x += op2.x; y += op2.y; return *this; }

    // etc.
};

Расширяющиеся структуры могли бы быть "более" злыми, но не являются чем-то, что Вам запрещают сделать.

3
ответ дан Christopher 28 November 2019 в 23:10
поделиться

Ничего себе, это является злым.

действительно ли это портативно через компиляторы?

Совершенно определенно нет. Рассмотрите следующее:

foo* x = new bar();
delete x;

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

РЕДАКТИРОВАНИЕ/: С другой стороны, если код используется только в качестве в вопросе, наследование имеет преимущество перед составом. Просто последуйте совету, данному m_pGladiator.

2
ответ дан Konrad Rudolph 28 November 2019 в 23:10
поделиться

Это совершенно законно, и Вы видите его на практике с MFC CRect и классы CPoint. CPoint происходит из ТОЧКИ (определенный в windef.h), и CRect происходит из RECT. Вы просто украшаете объект функциями членства. Пока Вы не расширяете объект с помощью большего количества данных, Вы в порядке. На самом деле, если у Вас есть комплекс C структура, которая является болью для установки по умолчанию - инициализируют, расширяя его с помощью класса, который содержит конструктора по умолчанию, простой способ заниматься той проблемой.

, Даже если Вы делаете это:

foo *pFoo = new bar;
delete pFoo;

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

Вы также не должны обертывать свой объект C++ с 'экстерном "C"', так как Вы на самом деле не передаете C++ тип к функциям C.

2
ответ дан moswald 28 November 2019 в 23:10
поделиться

Я не думаю, что это обязательно проблема. Поведение четко определено, и до тех пор, пока вы будете осторожны с жизненными проблемами (не смешивайте и не сопоставляйте распределения между кодами C ++ и C), вы будете делать то, что хотите. Он должен быть идеально переносимым между компиляторами.

Проблема с деструкторами реальна, но применяется всякий раз, когда деструктор базового класса не является виртуальным, не только для структур Си. Это то, что вам нужно знать, но это не исключает использования этого паттерна.

1
ответ дан Rob Walker 28 November 2019 в 23:10
поделиться

Это будет работать, и портативно НО Вы не можете использовать виртуальные функции (который включает деструкторы).

я рекомендовал бы вместо того, чтобы делать это иметь Панель, содержат Нечто.

class Bar
{
private:
   Foo  mFoo;
};
1
ответ дан Airsource Ltd 28 November 2019 в 23:10
поделиться

Это, вероятно, будет работать, но я не полагаю, что этому гарантируют. Следующее является кавычкой от C++ ISO 10/5:

подобъект базового класса А мог бы иметь расположение (3.7) отличающийся от расположения большей части производного объекта того же типа.

трудно видеть, как в "реальном мире" это могло на самом деле иметь место.

РЕДАКТИРОВАНИЕ:

нижняя строка - то, что стандарт не ограничил количество мест, где подструктура объекта базового класса может отличаться от конкретного объекта с тем же самым Базовым типом. Результат состоит в том, что любые предположения, которые Вы можете иметь, такие как мыс POD и т.д., не обязательно верны для подобъекта базового класса.

РЕДАКТИРОВАНИЕ:

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

class bar {
public:    
   int my_bar() { 
       return ret_foo( foo_ ); 
   }

   // 
   // This allows a 'bar' to be used where a 'foo' is expected
   inline operator foo& () {
     return foo_;
   }

private:    
  foo foo_;
};
0
ответ дан Richard Corden 28 November 2019 в 23:10
поделиться

Я не понимаю, почему вы просто не делаете ret_foo методом-членом. Ваш текущий способ делает ваш код очень сложным для понимания. Что такого сложного в использовании реального класса, во-первых, с помощью переменной-члена и методов get / set?

Я знаю, что в C ++ можно создавать подклассы структур, но есть опасность, что другие не смогут понять то, что вы кодировали, потому что это так редко, что кто-то на самом деле делает это. Вместо этого я бы выбрал надежное и общее решение.

0
ответ дан Thorsten79 28 November 2019 в 23:10
поделиться