5 Stimmen

C++-Klassen als Instanzvariablen einer Objective-C-Klasse

Ich muss Objective-C und C++ mischen. Ich möchte alle C++-Sachen in einer Klasse verstecken und alle anderen in Objective-C belassen. Das Problem ist, dass ich einige C++-Klassen als Instanzvariablen haben möchte. Das bedeutet, dass sie in der Header-Datei erwähnt werden müssen, die von anderen Klassen eingebunden wird, und C++ beginnt, sich in der gesamten Anwendung auszubreiten. Die beste Lösung, die ich bisher gefunden habe, sieht so aus:

#ifdef __cplusplus
#import "cppheader.h"
#endif

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    #ifdef __cplusplus
    CPPClass cppStuff;
    #endif
}

@end

Das funktioniert. Die Implementierungsdatei hat eine mm Erweiterung, so dass sie als Objective-C gemischt mit C++ kompiliert wird, die #ifdef schaltet die C++-Sachen frei und los geht's. Wenn eine andere, reine Objective-C-Klasse den Header importiert, wird der C++-Kram ausgeblendet und die Klasse sieht nichts Besonderes. Das sieht wie ein Hack aus, gibt es eine bessere Lösung?

1 Stimmen

Das ist im Grunde das, was mir eingefallen ist, als ich das gleiche Problem hatte. Aber achten Sie auf Ihre ifdef: Sie müssen Padding für den non-cpp-Zweig einfügen. Ansonsten würde der Compiler die Größe der Foo-Objekte nicht kennen. Während dies bei nicht-fragilen Instance-Var-Builds vielleicht nicht scheitert, ist es definitiv ein Problem für Ziele im alten Stil.

0 Stimmen

Ich habe die von Ihnen oben beschriebene Vorgehensweise kopiert. Schien ziemlich nett und einfach, aber dann verursachte es einige verrückte Probleme mit der Speicherkorruption: stackoverflow.com/questions/2458652/

8voto

Barry Wark Punkte 106328

Das klingt nach einer klassischen Verwendung für ein Interface/@Protokoll. Definieren Sie ein Objective-C-Protokoll für die API und stellen Sie dann eine Implementierung dieses Protokolls mit Ihrer Objective-C++-Klasse bereit. Auf diese Weise brauchen die Clients nur das Protokoll zu kennen und nicht den Header der Implementierung. Angenommen, die ursprüngliche Implementierung

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    CPPClass cppStuff;

}

@end

Ich würde ein Protokoll definieren

//Extending the NSObject protocol gives the NSObject
// protocol methods. If not all implementations are
// descended from NSObject, skip this.
@protocol IFoo <NSObject>

// Foo methods here
@end

und ändern Sie das Original Foo Erklärung gegenüber

@interface Foo : NSObject <IFoo>
{
    id regularObjectiveCProperty;
    CPPClass cppStuff;
}

@end

Der Client-Code kann dann mit dem Typ id<IFoo> und muss nicht als Objective-C++ kompiliert werden. Offensichtlich können Sie eine Instanz von Foo für diese Kunden.

0 Stimmen

Wenn die @Schnittstelle Foo in der Datei .h steht, ist es dann möglich, die Beeinflussung von obj-c++ zu verhindern? Wenn die @Schnittstelle Foo in der Datei .mm steht, wie kann ich dann eine Instanz davon erstellen?

1 Stimmen

Jedes Modul, das Foo.h importiert, muss als Objective-C++ kompiliert werden, aber der Client-Code muss nur IFoo's Header, um IFoo zu verwenden und kann daher nur Objective-C sein. Dies ist ein klassisches Muster für die Verwaltung von Abhängigkeiten.

0 Stimmen

Wie würde man in Ihrem Beispiel ein neues Foo instanziieren, ohne den Header zu importieren? Das erste, was mir in den Sinn kommt, ist eine Objective C++-Fabrikklasse, die nicht importiert und C++-Zeug in ihren Header aufnimmt. Das scheint alles sehr kompliziert zu sein. Es gibt auch diesen Ansatz: stackoverflow.com/questions/2262011/ aber ich habe es nicht zum Laufen gebracht (siehe stackoverflow.com/questions/2463970/ )

3voto

Ben S Punkte 66945

Auch ich bin kürzlich auf dieses Problem gestoßen. In meinem Fall war ein Protokoll ein Overkill. Ich brauchte nur einen Zeiger auf ein Datenzugriffsobjekt, das zufällig ein C++-Objekt war.

Ich deklarierte die Klasse mit einer void * Instanzvariable und casten sie, wenn ich sie in den Instanzmethoden verwende.

Dies ist ein wenig hack-y, aber konzeptionell, es ist sehr ähnlich zu dem, was die Objective-C id Typ ist.

1voto

Peter N Lewis Punkte 17536

Gibt es einen bestimmten Grund, warum Sie nicht einfach Objective C++ für alles verwenden können? Schalten Sie den Compiler einfach auf Compile Sources As um: Objective C++ (oder benennen Sie alle Ihre Quelldateien von .cpp oder .m in .mm um). Dann können Sie C++ und Objective C frei mischen.

C++ beginnt sich auf die gesamte Anwendung

Welches Problem ist damit verbunden? Wenn Ihr Objective C-Code nur C/Objective C-Code im Allgemeinen macht, dann wird er mit ziemlicher Sicherheit überhaupt nicht davon betroffen sein, als C++ kompiliert zu werden. Es gibt keine nennenswerten Größen- oder Geschwindigkeitsprobleme.

Die einzigen zwei Nachteile, die ich gefunden habe, sind: Sie können (noch) nicht Clang statische Analysator verwenden, um C++ zu analysieren; einige (relativ seltsam) C-Code funktioniert nicht in C++, die gelegentlich ein Problem bei der Verwendung von Drittanbieter-C-Code ist.

0 Stimmen

Das einzige Problem ist, dass ich mich mit C++ nicht auskenne und ich befürchtete, dass es einige subtile Unterschiede geben würde, die zu Fehlern führen könnten. Mir gefällt die Idee, dass C++ nur in der Klasse gekapselt wird, die es wirklich braucht. Aber danke, das ist sicherlich eine Lösung.

0voto

Darren Ford Punkte 822

Sie könnten feststellen, dass Sie Probleme haben, dies zu tun - von dem, was ich von ObjectiveC++ erinnern, können Sie feststellen, dass der Konstruktor und der Destruktor für Ihre eingeschlossenen C++-Objekt wird nicht aufgerufen werden.

0voto

uliwitness Punkte 8124

TUN SIE DIES NICHT

Wenn Sie eine Instanzvariable ausdefieren, erhalten Sie zwei getrennte Instanzvariablen-Layouts für diese Klasse. Sie werden überall zufällige Speicherknüller erhalten, weil der für das Objekt zugewiesene Speicher in der Hälfte der Fälle zu klein ist. Anstatt die Instanzvariable ifdef out zu deklarieren, deklarieren Sie ihren Typ vorwärts wie

struct CPPClass;

und haben einen Zeiger darauf in der Ivar, dann in Ihrem init / dealloc Methoden rufen new/delete auf, um das Objekt zu erstellen. Wenn Sie mehrere Objekte haben, können Sie eine Struktur erstellen, die alle C++-Ivars direkt enthält, und dann nur diese Struktur neu erstellen/löschen.

In diesem Thread finden Sie weitere Details und Links zu Informationen, einschließlich eines Podcasts, in dem ausführlich über ObjC++ gesprochen wird: Kann ich beim Kompilieren und Verknüpfen C++-Hauptfunktionen und -Klassen von Objective-C- und/oder C-Routinen trennen?

0 Stimmen

In C müsste ein struct-Typ mit dem Namen struct CPPClass , nicht CPPClass also müssten sie auch das ändern.

0 Stimmen

In der Ivar-Erklärung, ja, gut erkannt. Für C++ sind struct Foo und Foo allerdings dasselbe, also wird es nur im Header benötigt. Normalerweise schreibe ich einfach struct vor den Klassennamen, wo immer ich es verwende, man braucht keine separate struct-Deklaration, wie ich sie hier erwähnt habe, für sich selbst.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X