2010-09-29 18 views
11

In C++ und Java können Datenstrukturen private, public und protected Regionen haben. Ich möchte dieses Konzept in ein C-Sprachenprogramm portieren, das ich schreibe.Partitionierung in private und öffentliche Bereiche?

Gibt es irgendwelche Idiome zum Implementieren von privaten oder geschützten Funktionszeigern und Datenfeldern in einem C struct? Ich weiß, dass C struct s sind öffentlich, ich bin auf der Suche nach einem Idiom um einige Implementierungsdetails zu verbergen und zwingen Benutzer, die öffentliche Schnittstelle zu verwenden.

Hinweis: Die Sprache, die vom Geschäft ausgewählt wurde, so dass ich objektorientierte Konzepte in C steckte Umsetzung

Dank.

+0

Informationsverbergung und -verkapselung ist älter als OOD. –

Antwort

20

Wie Sie wissen, können Sie dies nicht tun. Es gibt jedoch Idiome, die einen ähnlichen Effekt ermöglichen.

C ermöglicht es Ihnen, etwas Ähnliches zu tun, was als "pimpl" Idiom im objektorientierten Design bekannt ist. Ihre Struktur kann einen undurchsichtigen Zeiger auf eine andere vorwärts deklarierte Struktur haben, die als private Daten der Struktur fungiert.Funktionen, die auf der Struktur ausgeführt werden und anstelle von Elementfunktionen verwendet werden, können die vollständige Definition für das private Member haben und diese verwenden, während andere Teile des Codes dies nicht können. Zum Beispiel:

In einem Header, foo.h:

struct FooPrivate; 

    struct Foo { 
    /* public: */ 
     int x; 
     double y; 
    /* private: */ 
     struct FooPrivate* p; 
    }; 

    extern struct Foo* Foo_Create(); /* "constructor" */ 

    extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */ 

In der Implementierung foo.c:

struct FooPrivate { 
    int z; 
    }; 

    struct Foo* Foo_Create() 
    { 
    struct Foo* foo = malloc(sizeof(Foo)); 

    foo->p = malloc(sizeof(FooPrivate)); 

    foo->x = 0; 
    foo->y = 0; 
    foo->p->z = 0; 

    return foo; 
    } 

    void Foo_DoWhatever(struct Foo* foo) 
    { 
     foo->p->z = 4; /* Can access "private" parts of foo */ 
    } 

in einem Programm:

#include "foo.h" 

    int main() 
    { 
     struct Foo* foo = Foo_Create(); 

     foo->x = 100; /* Can access "public" parts of foo */ 
     foo->p->z = 20; /* Error! FooPrivate is not fully declared here! */ 

     Foo_DoWhatever(foo); /* Can call "member" function */ 

     return 0; 
    } 

Notiere die müssen eine "Konstruktor" -Funktion verwenden, um Speicher für die privaten Daten zuzuordnen. Offensichtlich müssten Sie dies mit einer speziellen "Destruktor" -Funktion paaren, um die privaten Daten richtig zu verteilen.

Oder alternativ, wenn Sie Ihre Struktur möchten überhaupt keine öffentlichen Bereichen haben, können Sie die gesamte Struktur undurchsichtig machen könnte, und haben nur den Header so etwas wie

struct Foo; 

    extern struct Foo* Foo_Create(); /* "constructor" */ 

    extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */ 

Mit der aktuellen Definition sein von struct Foo in foo.c, und Getter- und Setter-Funktionen für alle Eigenschaften verfügbar, auf die Sie direkten Zugriff haben möchten.

+0

+1 Danke, dass du mich an das Pimple-Idiom erinnert hast. Ich habe nie darüber nachgedacht, es auf die Sprache C anzuwenden. –

+2

In C99 möchten Sie vielleicht den Zeiger auf 'struct FooPrivate' mit einem flexiblen Array-Member' struct FooPrivate p [] 'ersetzen. Dies wird verhindern, dass der Benutzer der Schnittstelle versehentlich eine flache Kopie von "struct Foo" erstellt, da dies "struct Foo" zu einem unvollständigen Typ macht. – caf

+0

@caf Können Sie erweitern, auf welche Änderungen die Antwort erforderlich wäre, stattdessen flexible Array-Mitglieder zu verwenden? – CMCDragonkai

0

Es tut mir leid für dich, weil das Mischen verschiedener OO-Konzepte in C oft wie ein quadratischer Pflock in einem runden Loch ist. Nichtsdestoweniger hat GObject Unterstützung für öffentliche und private Mitglieder, aber es ist eine meiner am wenigsten bevorzugten Architekturen auf der Erde. Wenn Sie sich nicht mit dem geringen Leistungseinbruch beschäftigen, können Sie möglicherweise eine einfachere Lösung finden - eine sekundäre Struktur, die mit privaten Mitgliedern gefüllt ist, und einen anonymen Zeiger auf diese Struktur von der primären (öffentlichen) Struktur haben.

+1

Ich möchte mich von Bibliothekspaketen fernhalten, da dies Lizenzprobleme beinhaltet und die Entwicklung schnell abgeschlossen werden muss. –

+0

(a) GObject ist eine Open-Source-Bibliothek, obwohl zugegebenermaßen sogar Lizenzprobleme auftreten können; (b) die sekundäre private Struktur benötigt keine Bibliothek. – Reinderien

9

Das Konzept manchmal in C verwendet wird

// lib.h 
typedef struct { 
    int publicInt; 
    //... 
    char * publicStr; 
} Public; 

Public * getPublic(); 
int function(Public * public); 

// lib.c 

typedef struct { 
    Public public; 
    int privateInt; 
    // ... 
    char * privateStr 
} Private; 

static Private * getPrivate(); 

Public * getPublic() { return (Public*) getPrivate(); } 
int function(Public * public) { 
    Private * private = (Private *) public; 
    // ... 
} 

Dies verwendet den Standard Trick, der ein Zeiger auf eine Struktur mit einem Zeiger auf das erste Element in einer Struktur vertauscht werden.

Wenn Sie alle Ihre Felder wollen privat sein, ist es noch einfacher:

// lib2.h 
typedef struct AllPrivate * Handle; 
Handle getHandle(); 
int function2(Handle handle); 

// lib2.c 
struct AllPrivate { /* ... */ } 

Dateien, die #include lib2.h nicht beschweren, da wir nur struct AllPrivate * verwenden, und alle Zeiger sind die gleichen Größe, so dass der Compiler die Innereien von struct AllPrivate nicht kennen muss.

einen geschützten Bereich tun, dann würden Sie nur

// include/public.h 
struct Public { /* ... */ } 
struct Public * getPublic(); 
int somePublicFunction(struct Public *); 

// dev/include/protected.h 
struct Protected { struct Public public; /* ... */ } 
struct Protected * getProtected(); 
int someProtectedFunction(struct Protected *); 

// dev/src/private.c 
struct Private { struct Protected protected; /* ... * /} 
struct Public * getPublic() { return (struct Public *) getPrivate(); } 
struct Public * getProtected() { return (struct Protected *) getPrivate(); } 
int somePublicFunction(struct Public * public) { 
    struct Private private = (struct Private *) public; 
    // ... 
} 
int someProtectedFunction(struct Protected * protected) { 
    struct Private private = (struct Private *) protected; 
    // ... 
} 

definieren müssen Dann ist es nur eine Frage, sicherzustellen, dass dev/include nicht herumgereicht wird.

2

Für Datenfelder - einfach nicht verwenden. Sie können ein paar Tricks machen, indem Sie ihnen verrückte Namen geben, um ihre Verwendung zu verhindern, aber das wird die Leute nicht aufhalten. Die einzige Möglichkeit, dies zu tun, besteht darin, eine weitere private Struktur zu erstellen, auf die ein void-Zeiger durch Ihre Bibliotheksfunktionen zugreift.

Für private Funktionen - Datei static Funktionen verwenden. Setzen Sie all Ihre Bibliotheksfunktionen in eine C-Datei und deklarieren Sie diejenigen, die Sie als privat haben wollen, als static und fügen Sie sie nicht in irgendwelche Header-Dateien ein.

1

Konventionell hat ein privates Mitglied einen zusätzlichen Unterstrich in seinem Namen oder so etwas wie _pri angehängt. Oder möglicherweise ein Kommentar. Diese Technik führt keine durch den Compiler erzwungene Überprüfung durch, um sicherzustellen, dass niemand unangemessen auf diese Felder zugreift, sondern dient als Warnung für alle, die eine struct Deklaration lesen, dass die Inhalte Implementierungsdetails sind und sie nicht sehen oder anstupsen sollten.

Eine andere übliche Technik besteht darin, Ihre Struktur als unvollständigen Typ zu offenbaren. Zum Beispiel in der Header-Datei, die Sie haben könnten:

struct my_struct; 

void some_function(struct my_struct *); 

Und in der Umsetzung, oder einige interne Header, die nicht zugänglich für die Verbraucher der Bibliothek ist, haben Sie:

struct my_struct 
{ 
    /* Members of that struct */ 
}; 

können Sie mache auch ähnliche Tricks mit Void-Zeigern, die im "privaten" Teil des Codes an die richtige Stelle geworfen werden. Dieser Ansatz verliert an Flexibilität (Sie können z. B. keine stack-allokierte Instanz eines nicht definierten Typs haben), aber das kann akzeptabel sein.

Wenn Sie eine Mischung aus privaten und öffentlichen Mitgliedern haben möchten, können Sie dasselbe wie oben ausführen, aber den privaten Strukturzeiger als Mitglied des öffentlichen Elements speichern und in öffentlichen Benutzern der Bibliothek unvollständig lassen .

Obwohl dies eine gewisse Indirektion mit sich bringt, die die Leistung beeinträchtigen kann. Es gibt einige (in der Regel nicht tragbar, aber funktioniert auf vernünftigen Compiler) Typ-punning Tricks Sie können auch verwenden:

struct public_struct 
{ 
    int public_member; 
    int public_member2; 
    /* etc.. */ 
}; 

struct private_struct 
{ 
    struct public_struct base_members; 

    int private_member1; 
    int private_member2; 
}; 

void some_function(struct public_struct *obj) 
{ 
    /* Hack alert! */ 
    struct private_struct *private = (struct private_struct*)obj; 
} 

Dies geht auch davon aus, dass Sie nicht diese Objekte auf dem Stapel oder in statischen Speicher speichern kann, oder die Größe zur Kompilierzeit erhalten.

Verwandte Themen