2010-02-16 15 views
18

Ich habe versucht, die Gültigkeit von private Zugriffsspezifizierer in C++. Hier geht:C++ Ist privat wirklich privat?

Schnittstelle:

// class_A.h 

class A 
{ 
public: 
    void printX(); 
private: 
    void actualPrintX(); 
    int x; 
}; 

Umsetzung:

// class_A.cpp 
void A::printX() 
{ 
    actualPrintX(); 
} 

void A::actualPrintX() 
{ 
    std::cout << x: 
} 

ich dies in einem statischen Bibliothek gebaut (.a/LIB). Wir haben jetzt ein class_A.h und classA.a (oder classA.lib) -Paar. Ich habe class_A.h bearbeitet und die private: daraus entfernt.

nun in einem anderen classTester.cpp:

#include "class_A.h" // the newly edited header 

int main() 
{ 
    A a; 

    a.x = 12;   // both G++ and VC++ allowed this! 
    a.printX();   // allowed, as expected 
    a.actualPrintX(); // allowed by G++, VC++ gave a unresolved linker error 

    return 0; 
} 

Ich weiß, dass aus einer Bibliothek Header sind alle Wetten nach Manipulation (ich meine, Systemintegrität, etc.) Wenn auch das Verfahren Hacky ist, das ist wirklich dürfen? Gibt es eine Möglichkeit, dies zu blockieren? Oder mache ich hier etwas falsch?

+0

Ich weiß, dass die Cheshire Cat (Pimpl - private impl. Http://en.wikipedia.org/wiki/Cheshire_Cat_Idiom_%28programming_technique%29) Design hier möglich ist und auch, dass Zugriffsbezeichner Compile-Time-Wachen vom Compiler sind . – legends2k

+0

Warum in aller Welt können Sie es nicht einfach neu aufbauen? –

+0

@Dominic: Meine Absicht war zu sehen, was passiert, wenn ich versuche, auf private Teile einer Klasse zuzugreifen. – legends2k

Antwort

30

private ist nicht ein Sicherheitsmechanismus. Es ist eine Möglichkeit, Absichten zu kommunizieren und Informationen zu verbergen, über die andere Teile Ihres Programms nicht informiert werden müssen, wodurch die Gesamtkomplexität reduziert wird.

Da zwei verschiedene Header-Dateien nicht standardkonform sind, betreten Sie technisch undefined das Behavior Territory, aber praktisch, wie Sie herausgefunden haben, ist es den meisten Compilern egal.

7

Nein. Die private Zugriffskontrolle ist da, um Sie davon abzuhalten, dumme Dinge zu tun, nicht als Sicherheitsmechanismus, um andere daran zu hindern, auf Ihre Daten oder Funktionen zuzugreifen. Es gibt viele, viele Möglichkeiten, um es zu umgehen.

2

Es gibt keine Implementierung von A :: actualPrintX. Das ist dein Linkerfehler.

+0

Danke für die Benachrichtigung, es ist ein Tippfehler, ich habe es jetzt behoben :) – legends2k

+0

Jungs, bitte nicht stimmen Sie ihn ab. Zuvor in der Frage habe ich 'actualPrintX()' anstelle von 'A :: actualPrintX()'; Er hat mich gerade über diesen Tippfehler informiert. – legends2k

9

Sie sind über das hinausgegangen, was in C++ erlaubt ist, also was Sie tun, ist nicht erlaubt - aber natürlich kann es auf einigen Compilern in einigen Situationen funktionieren.

Insbesondere verletzen Sie die One Definition Rule.

Diese article von Herb Sutter erklärt es sehr schön - es bietet auch eine legale und portable Möglichkeit, das Access Specifier System zu umgehen.

+0

Aber ich habe nichts mehr als einmal definiert; Einverstanden, ich habe die Erklärung manipuliert, aber wie kommt es zu einem Fall von ODR-Verletzung? – legends2k

+2

Sie haben zweimal "Klasse A" definiert - einmal in der Übersetzungseinheit "classTester.cpp" und einmal in der Übersetzungseinheit "class_A.cpp". Die ODR-Verletzung besagt, dass diese beiden Definitionen nicht identisch sind. –

+0

Aargh, ich sehe es jetzt! Danke für Sutters Artikel zum selben Thema. – legends2k

5

Ich versuchte es. Dies ist ein nm eines Programms, das ich schrieb, mit einer Klasse Test mit einer privaten Methode und einer öffentlichen.

0000000100000dcc T __ZN4Test3barEv 
0000000100000daa T __ZN4Test3fooEv 

Wie Sie sehen können, ist die Signatur genau gleich. Der Linker hat absolut nichts zu unterscheiden zwischen einer privaten und einer öffentlichen Methode.

+0

Ja, wie ich bereits erwähnt habe, gab es in G ++ absolut keinen Fehler beim Zugriff auf 'actualPrintX()'; aber die Symbole in VC++ scheinen einen Unterschied zu haben und daher hat der Linker einen Fehler erzeugt. Vielen Dank! – legends2k

5

Ich stimme den meisten anderen Antworten zu.

Ich möchte jedoch darauf hinweisen, dass es für einen Compiler vollkommen akzeptabel ist, die Mitglieder unterschiedlich zu organisieren, wenn Sie das private entfernen. Wenn es funktioniert, ist das Glück. Du kannst dich nicht darauf verlassen. Wenn beide Seiten nicht dieselbe Deklaration verwenden, verwenden sie nicht dieselbe Klasse.

2

Da hat niemand eine Möglichkeit erwähnt, dies zu blockieren ...Eine Möglichkeit, den Zugriff auf private Mitglieder zu blockieren, besteht darin, sie als separaten internen Typ zu deklarieren, der außerhalb der Datei nicht sichtbar ist. Wenn Sie explizit auf diesen internen Zugriff zugreifen möchten, müssen Sie natürlich die interne Deklaration bereitstellen. Dies wird üblicherweise auch mit einem internen Header durchgeführt, der diesen Typ enthält.

Hinweis: In diesem Beispiel müssen Sie das Zuordnen/Freigeben dieses internen Objekts verfolgen. Es gibt andere Möglichkeiten, dies zu tun, die dies nicht erfordern.

// class_A.h 
class A { 
public: 
    void printX(); 
private: 
    void *Private; 
}; 

// class_A.cpp 
class A_private { 
    void actualPrintX(); 
    int x; 
}; 

void A::printX() { 
    reinterpret_cast<A_private *>(Private)->actualPrintX(); 
} 

void A_private::actualPrintX() { 
    std::cout << x: 
} 
+0

Ebenso, neben der Frage, in den Kommentaren habe ich auch über die Cheshire Cat Methode erwähnt, die ähnlich ist, nur ohne die reinterpret_cast. – legends2k

+0

Verzeih mir, das muss ich verpasst haben; Gut zu wissen, dass es tatsächlich einen Namen hat. – Ioan

1

In den meisten Fällen müssen Sie nicht einmal die Header-Datei bearbeiten, um private Mitglieder öffentlich zu machen. Sie können dies mit dem Präprozessor tun. Etwas wie folgt aus:

//"classWithPrivateMembers.hpp" 
class C 
{ 
private: //this is crucial for this method to work 
    static int m; 
}; 

int C::m = 12; 

und dann, dies funktionieren wird:

#define private public 
#include "classWithPrivateMembers.hpp" 
#undef private 

int main() 
{ 
    C::m = 34; // it works! 
} 
+0

Sie können '#define class struct 'hinzufügen, um nicht qualifizierte private vars zu erfassen. – mskfisher

1

Beachten Sie auch, dass, wenn Sie ein Mitglied der Zugriffsvariablen ändern, wird der Compiler es bei einer innerhalb der Klasse versetzt verschiedenen platzieren können Objekt. Der Standard erlaubt Compilern eine ziemlich große Freiheit bei der Neuanordnung von Mitgliedern (zumindest innerhalb der gleichen Zugangsebene, glaube ich).