2016-05-01 11 views
1

Ich kann nicht auf Feld in der untergeordneten Klasse überschrieben von übergeordneten C++ - Konstruktor zugreifen, und ich kann keine Vorlagen verwenden, weil Upstream-Projekt sie nicht verwendet.Zugriff überschriebenes Feld im Konstruktor ohne Vorlagen

Dies funktioniert Python-Prototyp, den ich versuche, in C++ neu zu implementieren. Der Code enthält zwei Treiberklassen - ein untergeordnetes Element und ein übergeordnetes Element. Das übergeordnete Element gibt während der Initialisierung name der Klasse aus.

class Driver(object): 
    name = "Unknown" 
    def __init__(self): 
    print(self.name) 

class SpecificDriver(Driver): 
    name = "Specific" 
    def __init__(self): 
    super(SpecificDriver, self).__init__() 

Driver() 
SpecificDriver() 

Dieser druckt zwei Saiten zu trösten

Unknown 
Specific 

Sieht aus wie in C++ kann nicht außer Kraft gesetzt name zugreifen, da die name an dieser Stelle nicht existiert - Call child method from parent constructor. Also gibt es vielleicht eine andere Möglichkeit, Treiber name bei der Initialisierung gedruckt zu bekommen?

UPDATE (2018): Der ursprüngliche Titel für diese Frage lautete "Überschriebenes untergeordnetes Feld während der Initialisierung in C++ ohne Vorlagen überschreiben" und wurde als zu breit geschlossen.

+0

Sie könnten statische Polymorphie aka CRTP verwenden. –

+0

@ πάνταῥεῖ erfordert das die Verwendung von Vorlagen?In der Codebase, die ich patche, sehe ich sie nicht, und es sieht überhaupt nicht einfach aus. –

+0

Ja, das würde "Driver" zu einer Templates-Basisklasse machen, die die Ableitungsklasse als Typparameter verwendet. Daher können Sie 'static_cast (this)' überall dort verwenden, wo Sie auf die 'SpecificDriver'-Mitglieder zugreifen möchten. Ziemlich einfach IMHO. –

Antwort

2

Obwohl Sie dies ohne Vorlagen tun möchten, ist dies die einzige Möglichkeit, dies von einem Basisklassenkonstruktor aus zu tun.

Hier ist ein Beispiel, wie es getan werden sollte:

struct IDriver { 
    // Public virtual API: 
    virtual void func1() = 0; 
    // ... 
    virtual ~IDriver() {} 
}; 

template<typename Derived> 
class Driver : public IDriver { 
public: 
    Driver() { 
     std::cout << "Driver" << std::endl; 
     std::cout << static_cast<Derived*>(this)->name() << std::endl; 
    } 
}; 

class SpecificDriver : public Driver<SpecificDriver> { 
public: 
    // Public virtual API: 
    virtual void func1(); 
    std::string name() const { return "SpecificDriver"; } 
    // or use typeid(SpecificDriver).name() if you prefer 
}; 

int main() { 
    SpecificDriver sd; 
} 

Live Demo


Wie für Ihren Kommentar:

Ist es möglich, zusätzliche init() Funktion zu verwenden, wie in @tobspr Methode, aber Namen statt Feldaufruf ein Feld?

Nun, da der Klassenname ohnehin eine statische Eigenschaft dieser Klassen ist, können Sie ein static const Feld verwenden, wie folgt:

:

template<typename Derived> 
class Driver : public IDriver { 
public: 
    Driver() { 
     std::cout << name << std::endl; 
     std::cout << Derived::name << std::endl; 
    } 

private: 
    static const std::string name; 
}; 

template<typename Derived> 
const std::string Driver<Derived>::name = "Driver"; 

class SpecificDriver : public Driver<SpecificDriver> { 
public: 
    static const std::string name; 
}; 

const std::string SpecificDriver::name = "SpecificDriver"; 

int main() { 
    SpecificDriver sd; 
} 

typeid() Verwendung Live Demo

oder sogar vereinfachte

#include <iostream> 
#include <string> 
#include <typeinfo> 

template<typename Derived> 
class Driver { 
public: 
    Driver() { 
     std::cout << typeid(*this).name() << std::endl; 
     std::cout << typeid(Derived).name() << std::endl; 
    } 
}; 

class SpecificDriver : public Driver<SpecificDriver> { 
}; 

int main() { 
    SpecificDriver sd; 
} 

Live Demo

+0

Ist es möglich, zusätzliche 'init()' -Funktion wie in @tobspr -Methode zu verwenden, aber 'name' ein Feld anstelle von Funktionsaufruf? –

1

Angenommen, Sie haben zwei Klassen Base und Derived, der Base-Konstruktor weiß nichts über die Abgeleitete Klasse, die es unmöglich macht, zwischen beiden Typen zu unterscheiden.

Da Sie auch nicht (nicht) nennen virtuelle Methoden im Konstruktor, ein gemeinsames Muster ein Init-Methode zu machen ist:

struct Base { 
    virtual std::string get_name() { return "Base"; } 
    void init() { std::cout << get_name(); } 
}; 

struct Derived : public Base { 
    virtual std::string get_name() { return "Derived"; } 
}; 

// Later on .. 
Base b; 
b.init(); // Should print "Base" 

Derived d; 
d.init(); // Should print "Derived" 

Wie Sie sehen können, ist dies auf jeden Fall nicht die einfachste Lösung. Die Verwendung von Vorlagen wäre in diesem Fall sicherlich besser.

+0

Ich habe versucht, neue 'init' Methode in der Klasseninitialisierung zu verwenden und es funktioniert nicht, wahrscheinlich weil es nicht auch erstellt - http://codepad.org/OTXOdVNC Vielleicht gibt es eine Möglichkeit, auf statische Eigenschaften der abgeleiteten Klasse zuzugreifen ? –

+0

Warum ist es nicht möglich, "Base" und "Derived" als Klasseneigenschaften und nicht als Methoden zu definieren? –