Obwohl es Ihre Frage nicht direkt beantwortet, stellen Sie möglicherweise fest, dass generierter Code zum Verwalten von Eigenschaften in C++ tatsächlich nicht erforderlich ist. Der folgende Template-Code ermöglicht es Ihnen, und Eigenschaften bequem Gebrauch zu erklären:
// Declare your class containing a few properties
class my_class {
public:
property<int> x;
property<string> y;
...
};
...
my_class obj;
cout << obj.x(); // Get
obj.y("Hello, world!"); // Set
Hier ist der Code:
// Utility template to choose the 2nd type if the 1st is void
template <typename T, typename U>
struct replace_void {
typedef T type;
};
template <typename T>
struct replace_void<void, T> {
typedef T type;
};
// Getter/setter template
template <typename T, typename D = void>
class property {
typedef typename replace_void<D, property>::type derived_type;
derived_type& derived() { return static_cast<derived_type&>(*this); }
public:
property() {} // May be safer to omit the default ctor
explicit property(T const& v) : _v(v) {}
property(property const& p) : _v(p._v) {}
property& operator=(property const& p) { _v = p._v; return *this; }
T operator()() const { return _v; } // Getter
void operator()(T const& v) { derived().check(v); _v = v; } // Setter
protected:
// Default no-op check (derive to override)
void check(T const& v) const { (void)v; //avoid unused variable warning}
private:
T _v;
};
check()
ist eine Funktion, der Wert überprüft, ob zugeordnet ist gültig ist. Sie können es in einer Unterklasse außer Kraft setzen:
class nonnegative_int : public property<int, nonnegative_int> {
public:
// Have to redeclare all relevant ctors unfortunately :(
nonnegative_int(int v) : property<int, nonnegative_int>(v) {}
void check(int const& v) const {
if (v < 0) {
throw "Yikes! A negative integer!";
}
}
};
Da haben Sie es - alle Vorteile von extern erzeugten Getter/Setter-Funktionen, mit keinem der mess! :)
Sie könnten wählen, check()
eine bool
zurückgeben, die Gültigkeit anzeigt, anstatt eine Ausnahme zu werfen. Und man könnte ein ähnliches Verfahren, access()
, für den Fang von Lese Verweisen auf das Objekt im Prinzip hinzuzufügen.
EDIT: Wie Herr Fooz in den Kommentaren anmerkt, kann die Klasse Autor später die Implementierung ändern, ohne die logische Struktur der Klasse modifiziert (zB durch das property<int> x
Mitglied mit einem Paar x()
Methoden zu ersetzen), obwohl binären Die Kompatibilität ist verloren, daher müssen Benutzer ihren Client-Code immer dann neu kompilieren, wenn eine solche Änderung vorgenommen wird. Diese Fähigkeit, zukünftige Änderungen schmerzlos zu integrieren, ist eigentlich der Hauptgrund, warum Menschen Getter/Setter-Funktionen anstelle von öffentlichen Mitgliedern verwenden.
Leistung Anmerkung: Weil wir die CRTP verwenden „Kompilierung-Polymorphismus“ zu erreichen, gibt es keinen Overhead-virtual-Aufruf für die eigene check()
in einer Unterklasse bereitstellt, und Sie müssen es nicht virtual
zu erklären.
Funktioniert das mit privaten Datenmitgliedern? – nmuntz
Wow! Ich dachte eigentlich daran, Vorlagen zu verwenden, um Eigenschaften zu emulieren und kam zurück, um meine Antwort zu bearbeiten ... nur um diese Lösung bereits hier zu finden, und sie ist viel vollständiger als der einfache Hack, den ich posten würde! Danke für das Teilen. –
Es ist wahrscheinlich erwähnenswert, dass der Klassenautor die Implementierung ändern kann, ohne die logische Struktur der Klasse zu ändern (zB indem man direkt ein Paar x() -Methoden erstellt oder die D-Implementierung ändert), aber die Binärkompatibilität ist verloren kompilieren Sie ihren Clientcode, sobald eine solche Änderung vorgenommen wird. –