Sie können eine Vorlagenspezialisierung verwenden, um die Teile zu trennen, die nur einen Zweck für ein nicht-konstantes Objekt haben.
Folgendes ist eine Implementierung für eine Spielzeugklasse. Es hat ein einzelnes C-Array-Member v mit 10 Ints, und für unsere Zwecke ist es nur gültig, wenn jeder einzelne von ihnen gleich Null ist.
class ten_zeroes {
int v[10];
void fix(int pos) {v[pos] = 0;}
public:
ten_zeroes() { // construct as invalid object
for (int i=0;i<10;i++) {
v[i] = i;
}
}
};
Sehen Sie, dass ich bereits ein Funktionselement hergestellt, das eine ungültige Position fixiert, und einen schönen Konstruktor, der es als ein ungültiges Objekt initialisiert (tu das nicht: D)
Da wir gehen Verwenden Sie Templates, wir müssen die Implementierung des Check/Fix-Zyklus außerhalb der Klasse verschieben. Damit die relevanten Funktionen auf v
und die fix()
Methode zugreifen können, machen wir ihnen Freunde. Unser Code sieht jetzt aus wie:
class ten_zeroes {
int v[10];
void fix(int pos) {v[pos] = 0;}
public:
ten_zeroes() { // construct as invalid object
for (int i=0;i<10;i++) {
v[i] = i;
}
}
template<typename T>
friend void fix(T& obj, int pos);
template<typename T>
friend bool check(T& obj);
};
check()
‚s Implementierung ist einfach:
// Check and maybe fix object
template<typename T>
bool check(T& obj){
bool result = true;
for(int i=0;i<10;i++) {
if (obj.v[i]) {
result = false;
fix(obj, i);
}
}
return result;
}
Hier ist der schwierige Teil. Wir möchten, dass unsere fix()
-Funktion das Verhalten basierend auf der Konstanz ändert. Dafür müssen wir die Vorlage spezialisieren. Bei einem nichtkonstanten Objekt wird die Position fixiert. Für eine const ein, wird es nichts tun:
// For a regular object, fix the position
template<typename T>
void fix(T& obj, int pos) { obj.fix(pos);}
// For a const object, do nothing
template<typename T>
void fix(const T& obj, int pos) {}
Schließlich schreiben wir unsere is_valid()
und make_valid()
Methoden, und hier haben wir die vollständige Umsetzung:
#include <iostream>
class ten_zeroes {
int v[10];
void fix(int pos) {v[pos] = 0;}
public:
ten_zeroes() { // construct as invalid object
for (int i=0;i<10;i++) {
v[i] = i;
}
}
bool is_valid() const {return check(*this);} // since this is const, it will run check with a const ten_zeroes object
void make_valid() { check(*this);} // since this is non-const , it run check with a non-const ten_zeroes object
template<typename T>
friend void fix(T& obj, int pos);
template<typename T>
friend bool check(T& obj);
};
// For a regular object, fix the position
template<typename T>
void fix(T& obj, int pos) { obj.fix(pos);}
// For a const object, do nothing
template<typename T>
void fix(const T& obj, int pos) {}
// Check and maybe fix object
template<typename T>
bool check(T& obj){
bool result = true;
for(int i=0;i<10;i++) {
if (obj.v[i]) {
result = false;
fix(obj, i);
}
}
return result;
}
int main(){
ten_zeroes a;
std::cout << a.is_valid() << a.is_valid(); // twice to make sure the first one didn't make any changes
a.make_valid(); // fix the object
std::cout << a.is_valid() << std::endl; // check again
}
Ich hoffe nicht, den Geist main()
Funktion dort. Es wird unser kleines Spielzeug testen und 001
ausgeben, wie erwartet. Jede Wartung dieses Codes wird sich nicht mehr mit der Duplizierung von Code befassen müssen, was Sie wahrscheinlich vermeiden wollten. Ich hoffe, das war hilfreich.
Wenn Sie diese Implementierungsdetails vor dem endgültigen Benutzer verbergen möchten, sollten Sie sie natürlich in einen geeigneten Detail-Namespace verschieben. Ich überlasse das Ihnen :)
Split-Checks und "Fixieren" in zwei verschiedene Funktionen? Checks ist 'const' und fix nicht? – crashmstr
Ich stimme @crashmstr, mit einer Funktion, die zwei verschiedene Dinge ist ein schlechter Design-Geruch. –
Eigentlich sagt schon der Name 'CheckValidity', dass diese Funktion nur eine Prüfung durchführt und const sein kann. Wenn Sie 'MakeValid' wollen, ist es etwas anderes (und es sollte nicht const sein) – user463035818