2010-06-15 25 views
5

Ich versuche zu verhindern, dass eine Klasse in der Lage sein wird, seinen "This" -Zeiger in einen Zeiger einer seiner Schnittstellen zu konvertieren. Ich tue dies, indem ich private Vererbung über eine mittlere Proxy-Klasse verwende. Das Problem ist, dass die private Vererbung alle öffentlichen statischen Member und Typen der Basisklasse für alle Klassen unter der erbenden Klasse in der Hierarchie unzugänglich macht.C++ private Vererbung und statische Mitglieder/Typen

class Base 
{ 
public: 
    enum Enum 
    { 
     value 
    }; 
}; 

class Middle : private Base 
{ 
}; 

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     Base::Enum e = Base::value; // doesn't compile BAD!  
     Base* base = this; // doesn't compile GOOD! 
    } 
}; 

Ich habe versucht, dies sowohl in VS2008 (die erforderliche Version) und VS2010, weder Arbeit.

Kann jemand an einen Workaround denken? Oder ein anderer Ansatz, um die Konvertierung zu stoppen?

Auch ich bin Kuriositäten des Verhaltens, ist es nur ein Nebeneffekt der Compiler-Implementierung, oder ist es von Entwurf? Wenn von Entwurf, warum? Ich dachte immer an private Vererbung, dass niemand weiß, dass Middle von Base erbt. Das gezeigte Verhalten impliziert jedoch, dass private Vererbung viel mehr bedeutet als das, tatsächlich hat Child weniger Zugriff auf Base als jeder Namespace, der nicht in der Klassenhierarchie ist!

+0

+1: Eine sehr interessante Frage, in der Tat. –

Antwort

6

Sie sollten in der Lage sein Base::Enum zugreifen, indem er voll qualifizierenden es:

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     ::Base::Enum e = ::Base::value; 
    } 
}; 

Dies ist das Verhalten der angegebenen Sprache (C++ 03 §11.2/3):

Hinweis: Ein Mitglied einer privaten Basisklasse ist möglicherweise nicht als ererbter Mitgliedsname verfügbar, aber direkt zugänglich.

Dies wird durch ein erweitertes Beispiel gefolgt, die Ihrem Beispiel-Code effektiv ähnlich ist.

Es scheint jedoch, dass weder Visual C++ 2008 noch Visual C++ 2010 dies korrekt implementiert, so dass Sie zwar den Typ ::Base::Enum verwenden können, aber Sie immer noch nicht auf ::Base::value zugreifen können. (Tatsächlich scheint Visual C++ eine Menge falsch verstanden zu haben, da es Ihnen inkorrekt erlaubt, das nicht vollständig qualifizierte Base::Enum zu verwenden).

„get around“ das Problem, die Sie hinzufügen können Erklärungen an die Middle-Klasse:

class Middle : private Base 
{ 
protected: 

    using Base::Enum; 
    using Base::value; 
}; 

Dies wird nicht Base::Enum oder Base::value in Ihrer Child Klasse verwenden lassen, aber es ermöglicht es Ihnen, Verwenden Sie eine Enum und value oder Middle::Enum und Middle::value.

+1

Ich habe einen Fehlerbericht zu Microsoft Connect bezüglich der Visual C++ - Fehler gesendet: https://connect.microsoft.com/VisualStudio/feedback/details/567693/ –

+0

Vielen Dank für Ihre ausführliche Antwort. Ärgerlich ist es durch einen Fehler verursacht. Leider ist das Hinzufügen zu der Middle-Klasse in meinem realen Fall keine Option, da Middle eine Template-Klasse ist, die die Basisklasse als Type-Argument verwendet. Meine einzige Option ist, die Enumeration außerhalb der Schnittstelle zu bewegen. – WearyMonkey

+0

Ich werde feststellen, dass die "Notiz" aus dem Standard, die ich zitiert, wirklich nichts spezifiziert, es erklärt nur dieses Verhalten. Meine Vermutung ist, dass die tatsächliche Spezifikation dieses Verhaltens durch die verschiedenen Abschnitte verteilt wird, die beschreiben, wie die Namensauflösung stattfindet. –

1

Ich habe nur eine Frage: Warum Sie private Vererbung überhaupt?

Vererbung ist ein ganz gebrochen Konzept, meiner Meinung nach, weil es die One Responsibility Prinzip verletzt:

  • Sie die Schnittstelle erben
  • Sie die Implementierung erben

Leider Vererbung erforderlich ist für Polymorphismus in objektorientiertem Code, damit Sie in diesem Fall nicht davor zurückschrecken können.

Aber hier möchten Sie explizit NICHT Polymorphie verwenden, so frage ich mich, warum Vererbung überhaupt verwenden, da es seine einzige interessante Verwendung (Imo) ist.

Stattdessen in C++ können Sie verwenden:

  • Zusammensetzung, für die Wiederverwendung von Code
  • Freie Funktionen (im eigenen Namensraum definiert)
  • using, typedef etc ... bringen Objekte aus
  • außerhalb der Klasse

Ihr Beispiel scheint hier eingeschränkt, aber ich wickeln auch meine Aufzählungen in struct Namespace Verschmutzung durch eine verhindern tausend Symbole (und weil struct als Vorlagenparameter verwendet werden können, wo Namespaces nicht können).

struct MyEnum 
{ 
    enum type 
    { 
    value 
    }; 
}; 

class Child 
{ 
public: 
    typedef MyEnum::type Enum; 

    Child(Enum e = MyEnum::value); 

private: 
}; 

ich mit der Qualifikation den Namen etwas falsch nicht sehen, sondern ich glaube, es macht es einfacher, wieder zu lesen, da Sie wissen, welche ENUM wir sprechen ...

Wirklich, private Erbe wird am besten vermieden (und normalerweise durch Komposition ersetzt). Der einzige gültige Fall (imo) ist für die leere Basisoptimierung ... und ehrlich gesagt ist es nicht oft notwendig (wie bei Optimierungen üblich).

+0

Die andere Verwendung für private Vererbung ist, wenn Sie Zugriff auf geschützte Mitglieder einer Klasse benötigen. Das ist keine ideale Situation, aber es kommt selten vor. Ich bin mir sicher, dass es am besten vermieden wird. –

+0

Normalerweise entkopple ich an dieser Stelle normalerweise: Ich erstelle eine Mittelklasse, die öffentlich erbt und Komposition verwendet. –

+0

Ich stimme sicherlich zu, dass die Zusammensetzung der Vererbung möglichst vorzuziehen ist. Jedoch ist die Basisklasse in meinem realen Weltproblem die Kernklasse in einer alten großen Codebasis. Wenn man einen korrekten Refactor-Job macht, würde man den größten Teil des Codes umschreiben. – WearyMonkey

Verwandte Themen