Ist es möglich, eine C-Struktur in C ++ und Verwendung Zeiger auf die Struktur in C-Code zu Unterklasse?

StackOverflow https://stackoverflow.com/questions/127290

  •  02-07-2019
  •  | 
  •  

Frage

Gibt es eine Nebenwirkung in dies zu tun:

C-Code:

struct foo {
      int k;
};

int ret_foo(const struct foo* f){ 
    return f.k; 
}

C ++ Code:

class bar : public foo {

   int my_bar() { 
       return ret_foo( (foo)this ); 
   }

};

Es gibt eine extern "C" um den C ++ Code und jeder Code ist innerhalb der eigenen Übersetzungseinheit.

Diese portable über Compiler?

War es hilfreich?

Lösung

Dies ist völlig legal. In C ++ Klassen und Strukturen sind identische Begriffe, mit der Ausnahme, dass alle Strukturkomponenten standardmäßig öffentlich. Das ist der einzige Unterschied. So gefragt, ob Sie eine Struktur erweitern können, ist nicht anders als zu fragen, ob Sie eine Klasse erweitern können.

Es gibt eine Einschränkung hier. Es ist keine Garantie von Layout-Konsistenz von Compiler zu Compiler. Also, wenn Sie Ihren C-Code mit einem anderen Compiler als C ++ Code kompilieren, können Sie Probleme an dem Mitglied Layout bezogen (Polsterung vor allem). Dies kann auch auftreten, wenn C und C ++ Compiler vom gleichen Hersteller verwenden.

I Haben hatte dies mit gcc passieren und g ++. Ich arbeitete an einem Projekt, das mehrere große Strukturen verwendet. Leider verpackt, g ++ die structs deutlich lockerer als gcc, die den Austausch von Objekten zwischen C und C ++ Code erhebliche Probleme verursacht. Wir hatten schließlich zur Verpackung von Hand eingestellt und legen padding die C- und C ++ Code zu machen, die structs gleich behandeln. Beachten Sie jedoch, dass dieses Problem unabhängig von Subklassifizieren auftreten können. In der Tat waren wir Subklassen die C-Struktur in diesem Fall nicht.

Andere Tipps

Ich empfehle auf jeden Fall nicht so seltsame Subklassifizieren verwenden. Es wäre besser, Ihr Design zu ändern Zusammensetzung zu verwenden, anstatt der Vererbung. So stellen Sie ein Mitglied

  

foo * m_pfoo;

in der Bar-Klasse, und es wird die gleiche Arbeit tun.

Andere Sache, die Sie tun können, ist eine weitere Klasse FooWrapper zu machen, die Struktur an sich mit dem entsprechenden Getter-Methode enthält. Dann können Sie die Wrapper-Unterklasse. Auf diese Weise ist das Problem mit dem virtuellen destructor ist weg.

  

„ableiten nie von konkreten Klassen.“ - Sutter

     

„Machen Sie nicht-Blatt Klassen abstrakt.“ - Meyers

Es ist einfach falsch nicht-Interface-Klassen, Unterklasse. Sie sollten Ihre Bibliotheken Refactoring.

Technisch können Sie tun, was Sie wollen, so lange, wie Sie durch, e nicht undefinierten Verhalten aufrufen. g. einen Zeiger auf die abgeleitete Klasse durch einen Zeiger auf seine Basisklasse Subobjekt zu löschen. Sie brauchen nicht einmal extern "C" für den C ++ Code. Ja, es ist tragbar. Aber es ist schlechtes Design.

Das ist vollkommen legal, obwohl es für andere Programmierer verwirrend sein könnte.

Sie Vererbung verwenden können, C-Strukturen mit Methoden und Konstruktoren.

erweitern

Beispiel:

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.
};

structs Erweiterung könnte „mehr“ böse sein, aber es ist nicht etwas, was Sie sind verboten zu tun.

Wow, das ist böse.

  

Diese portable über Compiler?

Die meisten definitiv nicht. Betrachten Sie das folgende:

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

Damit dies funktioniert, Destruktor foo muss virtuell sein, die es nicht eindeutig ist. Solange Sie nicht new und solange die abgeleitete ObjectD haben keine benutzerdefinierten Destruktoren nicht verwenden, obwohl, könnten Sie Glück haben.

/ EDIT: Auf der anderen Seite, wenn der Code nur wie in der Frage verwendet wird, Vererbung keinen Vorteil gegenüber Zusammensetzung hat. Folgen Sie einfach dem Rat gegeben durch m_pGladiator.

Das ist vollkommen legal, und man kann es in der Praxis mit den MFC CRect und CPoint Klassen sehen. CPoint leitet sich von dem Punkt (in windef.h definiert sind), und leitet sich von CRect RECT. Sie sind einfach ein Objekt mit Elementfunktionen zu dekorieren. Solange Sie mit mehr Daten das Objekt nicht verlängern, sie ist in Ordnung. In der Tat, wenn Sie eine komplexe C-Struktur haben, die ein Schmerz auf Standard-initialisieren, es mit einer Klasse erstreckt, die ein Standard-Konstruktor enthält, ist eine einfache Möglichkeit, mit diesem Problem fertig zu werden.

Auch wenn Sie dies tun:

foo *pFoo = new bar;
delete pFoo;

dann bist du in Ordnung, da Ihr und Destruktor trivial sind, und Sie haben keinen zusätzlichen Speicher zugewiesen.

Sie müssen auch nicht Ihre C ++ wickeln mit 'extern "C"' Objekt, da du nicht wirklich ein C ++ vorbei type zu den C-Funktionen.

Ich glaube nicht, es unbedingt ein Problem ist. Das Verhalten ist gut definiert, und so lange, wie Sie mit Lebenszeitfragen vorsichtig sind (nicht und zwischen den C ++ und C-Code übereinstimmen Zuweisungen mischt) werden tun, was Sie wollen. Es sollte über Compiler durchaus tragbar sein.

Das Problem mit Destruktoren ist real, aber gilt jederzeit die Basisklasse Destruktor nicht virtuell ist nicht nur für C structs. Es ist etwas, was Sie beachten müssen, der aber nicht ausschließen, dieses Muster verwendet wird.

Es wird funktionieren, und portabel, aber Sie können keine virtuellen Funktionen verwenden (die Destruktoren enthält).

Ich würde empfehlen, dass anstatt dies zu tun, Sie haben Bar enthalten eine Foo.

class Bar
{
private:
   Foo  mFoo;
};

ich nicht, warum Sie einfach nicht ein Mitglied Verfahren machen ret_foo. Ihre aktuelle Weise macht Ihren Code furchtbar schwer zu verstehen. Was ist so schwierig, über eine echte Klasse in erster Linie mit einem Mitglied Variable und get / set-Methoden?

Ich weiß, es zu Unterklasse structs in C ++ möglich ist, aber die Gefahr ist, dass andere in der Lage, nicht zu verstehen, was Sie codiert, weil es so selten ist, dass jemand es tatsächlich der Fall ist. Ich würde gehen für eine robuste und gemeinsame Lösung statt.

Es wird wahrscheinlich funktionieren, aber ich glaube nicht, es ist garantiert. Das Folgende ist ein Zitat von ISO C ++ 05.10:

  

Eine Basisklasse subobject könnte ein Layout haben (3.7) unterscheidet sich von dem Layout eines am meisten abgeleitete Objekt des gleichen Typs.

Es ist schwer zu sehen, wie in der „realen Welt“ dies tatsächlich der Fall sein könnte.

EDIT:

Das Endergebnis ist, dass der Standard die Anzahl der Plätze begrenzt ist, wo eine Basisklasse subobject Layout unterscheidet sich von einem konkreten Objekt mit dem gleichen Basistyp sein kann. Das Ergebnis ist, dass alle Annahmen, die Sie können, wie POD-ness usw. sind nicht unbedingt wahr für die Basisklasse subobject haben.

EDIT:

Ein alternativer Ansatz, und ein, dessen Verhalten gut definiert ist ‚foo‘ Mitglied von ‚bar‘ zu machen und einen Umwandlungsoperator zur Verfügung zu stellen, wo es notwendig ist.

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_;
};
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top