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

Lesezeit: 8 Minuten

Benutzer-Avatar
Ed Felipe

Gibt es eine Nebenwirkung dabei:

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

};

Dort ist ein extern "C" um den C++-Code und jeder Code befindet sich in einer eigenen Kompilierungseinheit.

Ist dies über Compiler portierbar?

  • Ich vermute du meinst (foo*)dies, oder noch besser: static_cast(this)

    – Richard Corden

    24. September 2008 um 14:18 Uhr

  • Ich bin neugierig auf die Wirkung eines externen “C” um eine Klasse mit einer Methode … Der Grund, warum Sie dies tun möchten, würde uns jedoch helfen, Informationen oder alternative Lösungen anzubieten.

    – Paercebal

    24. September 2008 um 17:25 Uhr

  • Wenn Sie “über Compiler portierbar” sagen, meinen Sie damit, den C-Code mit einem Compiler und C++ mit einem anderen zu kompilieren und die beiden miteinander zu verknüpfen?

    – Roddy

    24. September 2008 um 22:19 Uhr

Das ist völlig legal. In C++ sind Klassen und Strukturen identische Konzepte, mit der Ausnahme, dass alle Strukturmitglieder standardmäßig öffentlich sind. Das ist der einzige Unterschied. Die Frage, ob Sie eine Struktur erweitern können, unterscheidet sich also nicht von der Frage, ob Sie eine Klasse erweitern können.

Hier gibt es eine Einschränkung. Es gibt keine Garantie der Layoutkonsistenz von Compiler zu Compiler. Wenn Sie also Ihren C-Code mit einem anderen Compiler als Ihren C++-Code kompilieren, können Sie auf Probleme im Zusammenhang mit dem Member-Layout (insbesondere Padding) stoßen. Dies kann sogar auftreten, wenn C- und C++-Compiler desselben Anbieters verwendet werden.

ich haben hatte dies mit gcc und g++ passieren. Ich habe an einem Projekt gearbeitet, das mehrere große Strukturen verwendet hat. Leider hat g++ die Strukturen wesentlich lockerer gepackt als gcc, was zu erheblichen Problemen bei der gemeinsamen Nutzung von Objekten zwischen C- und C++-Code führte. Wir mussten schließlich das Packen und Einfügen von Padding manuell festlegen, damit der C- und der C++-Code die Strukturen gleich behandeln. Beachten Sie jedoch, dass dieses Problem unabhängig von Unterklassen auftreten kann. Tatsächlich haben wir in diesem Fall keine Unterklassen der C-Struktur erstellt.

  • Tatsächlich gibt es noch einen weiteren Unterschied zwischen Strukturen und Klassen: Die standardmäßige Sichtbarkeit von Basisklassen ist in Strukturen öffentlich, in Klassen jedoch privat.

    – Aconcagua

    17. August 2016 um 9:58 Uhr

Benutzer-Avatar
m_pGladiator

Ich empfehle sicherlich nicht, solch seltsame Unterklassen zu verwenden. Es wäre besser, Ihr Design so zu ändern, dass Komposition statt Vererbung verwendet wird. Machen Sie einfach ein Mitglied

foo* m_pfoo;

in der Bar-Klasse und es wird den gleichen Job machen.

Eine andere Sache, die Sie tun können, ist, eine weitere Klasse FooWrapper zu erstellen, die die Struktur selbst mit der entsprechenden Getter-Methode enthält. Dann können Sie den Wrapper unterklassen. Auf diese Weise ist das Problem mit dem virtuellen Destruktor behoben.

  • das ist die beste antwort. Im Gegensatz zur akzeptierten Antwort helfen Ihnen einige aufgezeigte Optionen, Probleme mit vtables und dem Strukturlayout zu vermeiden.

    – Alexander Ach

    22. Januar 2014 um 10:30 Uhr

Benutzer-Avatar
Roman Odaisky

„Niemals von konkreten Klassen ableiten.“ — Sutter

„Nicht-Blatt-Klassen abstrakt machen.“ — Meyers

Es ist einfach falsch, Nicht-Schnittstellenklassen zu unterteilen. Sie sollten Ihre Bibliotheken umgestalten.

Technisch gesehen können Sie tun, was Sie wollen, solange Sie kein undefiniertes Verhalten hervorrufen, indem Sie zB einen Zeiger auf die abgeleitete Klasse durch einen Zeiger auf das Unterobjekt der Basisklasse löschen. Du brauchst nicht einmal extern "C" für den C++-Code. Ja, es ist tragbar. Aber es ist schlechtes Design.

  • Ich bin mit solchen definitiven Aussagen von Meyer und Sutter überhaupt nicht einverstanden. Sie können viel tun, indem Sie von konkreten Klassen erben, insbesondere wenn Sie den abgeleiteten Klassen nur Methoden hinzufügen, keine Datenelemente. Auf diese Weise haben Sie nicht das Problem des Schneidens.

    – QBziZ

    24. September 2008 um 18:36 Uhr

  • Ich glaube, du verfehlst das Wesentliche. Ich vermute, dass das OP über vorhandene C-Module verfügt und einige der Strukturen in Klassen „verpacken“ möchte, während es weiterhin eine C-Schnittstelle für Legacy-Systeme anbietet. Wenn er bei C für die Strukturen feststeckt, sind Sutter und Meyer überhaupt keine Hilfe.

    – Roddy

    24. September 2008 um 22:16 Uhr

Dies ist völlig legal, obwohl es für andere Programmierer verwirrend sein könnte.

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

Probe :

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

Das Erweitern von Structs könnte “mehr” böse sein, ist aber nicht etwas, das Ihnen verboten ist.

Benutzer-Avatar
Konrad Rudolf

Wow, das ist böse.

Ist dies über Compiler portierbar?

Ganz sicher nicht. Folgendes berücksichtigen:

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

Damit dies funktioniert, muss der Destruktor von foo virtuell sein, was er eindeutig nicht ist. Solange Sie nicht verwenden new und solange das abgeleitete Objekt keine benutzerdefinierten Destruktoren hat, könnten Sie Glück haben.

/EDIT: Andererseits, wenn der Code nur wie in der Frage verwendet wird, hat die Vererbung keinen Vorteil gegenüber der Komposition. Folgen Sie einfach den Ratschlägen von m_pGladiator.

  • Aber das ist nicht der Code, den ich schreiben möchte. bar kann nur für Funktionsaufrufe nach foo gecastet werden, und alle C-Funktionen, die mit foo arbeiten, sind const. (wie im Beispiel.)

    – Ed Felipe

    24. September 2008 um 14:05 Uhr

  • Ja, in diesem Fall ist der Code irgendwie in Ordnung. Es schreit nur nach Missbrauch. 😉

    – Konrad Rudolf

    24. September 2008 um 14:11 Uhr

  • Es scheint, dass die foo*-Schnittstelle nur von C verwendet wird. Der C-Code könnte es sowieso nicht löschen: [1] es ist nicht der Besitzer und [2] es ist nicht C++.

    – MSalter

    24. September 2008 um 14:52 Uhr

  • Es sollte beachtet werden, dass jede C++-Klasse mit nicht virtuellem Destruktor das gleiche Problem auferlegt. Das ist kein Problem der Portabilität, sondern tritt auch innerhalb ein und derselben Sprache (C++) und ein und demselben Compiler auf, wenn die Basisklasse nicht dazu bestimmt ist, von ihr geerbt zu werden.

    – Aconcagua

    17. August 2016 um 10:05 Uhr

Benutzer-Avatar
Moswald

Das ist völlig legal und Sie können es in der Praxis mit den MFC-Klassen CRect und CPoint sehen. CPoint leitet sich von POINT ab (definiert in windef.h) und CRect leitet sich von RECT ab. Sie schmücken einfach ein Objekt mit Mitgliedsfunktionen. Solange Sie das Objekt nicht mit weiteren Daten erweitern, ist alles in Ordnung. Wenn Sie eine komplexe C-Struktur haben, deren Standard-Initialisierung mühsam ist, können Sie dieses Problem leicht lösen, indem Sie sie um eine Klasse erweitern, die einen Standardkonstruktor enthält.

Auch wenn Sie dies tun:

foo *pFoo = new bar;
delete pFoo;

dann ist alles in Ordnung, da Ihr Konstruktor und Destruktor trivial sind und Sie keinen zusätzlichen Speicher zugewiesen haben.

Sie müssen Ihr C++-Objekt auch nicht mit ‘extern “C”‘ umschließen, da Sie eigentlich kein C++ übergeben Typ zu den C-Funktionen.

  • Aber das ist nicht der Code, den ich schreiben möchte. bar kann nur für Funktionsaufrufe nach foo gecastet werden, und alle C-Funktionen, die mit foo arbeiten, sind const. (wie im Beispiel.)

    – Ed Felipe

    24. September 2008 um 14:05 Uhr

  • Ja, in diesem Fall ist der Code irgendwie in Ordnung. Es schreit nur nach Missbrauch. 😉

    – Konrad Rudolf

    24. September 2008 um 14:11 Uhr

  • Es scheint, dass die foo*-Schnittstelle nur von C verwendet wird. Der C-Code könnte es sowieso nicht löschen: [1] es ist nicht der Besitzer und [2] es ist nicht C++.

    – MSalter

    24. September 2008 um 14:52 Uhr

  • Es sollte beachtet werden, dass jede C++-Klasse mit nicht virtuellem Destruktor das gleiche Problem auferlegt. Das ist kein Problem der Portabilität, sondern tritt auch innerhalb ein und derselben Sprache (C++) und ein und demselben Compiler auf, wenn die Basisklasse nicht dazu bestimmt ist, von ihr geerbt zu werden.

    – Aconcagua

    17. August 2016 um 10:05 Uhr

Benutzer-Avatar
Rob Walker

Ich glaube nicht, dass es unbedingt ein Problem ist. Das Verhalten ist gut definiert, und solange Sie mit Lebensdauerproblemen vorsichtig sind (vermischen und passen Sie keine Zuordnungen zwischen dem C++- und dem C-Code an), wird es tun, was Sie wollen. Es sollte perfekt über Compiler portierbar sein.

Das Problem mit Destruktoren ist real, gilt aber immer dann, wenn der Destruktor der Basisklasse nicht virtuell ist, nicht nur für C-Strukturen. Es ist etwas, dessen Sie sich bewusst sein müssen, schließt die Verwendung dieses Musters jedoch nicht aus.

  • Das Verhalten ist nicht gut definiert – zumindest in meiner Lektüre des std. Haben Sie einen Verweis auf den Text, der besagt, dass es legal ist?

    – Richard Corden

    24. September 2008 um 14:12 Uhr

  • Keine Referenz … aber ich verstehe das Zitat, das Sie unten verwendet haben, nicht vollständig. Würde das kein nützliches Downcasting verhindern, sehe ich nicht, wie es spezifisch für den Fall ist, dass die Basisklasse eine C-Struktur ist

    – Rob Walker

    24. September 2008 um 14:54 Uhr

  • Leider kann ich keine normative Referenz finden. Aber ein Mitglied des ISO-C++-Komitees hat mich darauf hingewiesen, dass ein Objekt nur dann ein POD ist, wenn es eine konkrete Instanz eines POD-Typs ist. dh. Es gibt kein POD-Basis-Unterobjekt. Ich werde meiner Antwort unten weitere Einzelheiten hinzufügen.

    – Richard Corden

    24. September 2008 um 16:08 Uhr

1352350cookie-checkIst es möglich, eine C-Struktur in C++ abzuleiten und Zeiger auf die Struktur in C-Code zu verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy