Możesz użyć specjalizacji szablonu, aby oddzielić części, które mają tylko cel, na obiekcie nie będącym stałym.
Poniżej znajduje się implementacja klasy zabawki. Ma jeden składnik tablicy c v z 10 intami, i dla naszych celów jest ważny tylko wtedy, gdy każdy z nich jest równy zeru.
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;
}
}
};
Zobacz, że ja już element funkcyjny, który rozwiązuje nieprawidłową pozycję, i piękny konstruktor, który inicjuje go jako niepoprawnego obiektu (nie rób tego: D)
Ponieważ mamy zamiar użyj szablonów, musimy przenieść implementację cyklu sprawdzania/poprawiania poza klasę. Aby odpowiednie funkcje mogły uzyskać dostęp do metody v
i fix()
, zrobimy z nich znajomych. Nasz kod teraz wygląda:
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 implementacja jest prosta:
// 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;
}
Teraz tutaj jest skomplikowana część. Chcemy, aby nasza funkcja fix()
zmieniała zachowanie w oparciu o constness. W tym celu musimy specjalizować szablon. Dla obiektu niestanowiącego stałego, naprawi on pozycję. Dla jednego const, to zrobi nic:
// 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) {}
Wreszcie piszemy nasze is_valid()
i make_valid()
metody, a tutaj mamy pełne wdrożenie:
#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
}
Mam nadzieję, że nie przeszkadza main()
funkcja tam. Będzie testować naszą małą zabawkę, a wyjście 001
, zgodnie z oczekiwaniami. Teraz wszelkie czynności związane z tym kodem nie będą musiały zajmować się powielaniem kodu, czego prawdopodobnie chciałeś uniknąć. Mam nadzieję, że to było pomocne.
Oczywiście, jeśli chcesz ukryć te szczegóły implementacji od końcowego użytkownika, powinieneś przenieść je do odpowiedniej szczegółowej przestrzeni nazw. Zostawię to tobie :)
Dzielenie czeków i "ustalanie" na dwie różne funkcje? Kontrola to 'const', a poprawka nie jest? – crashmstr
Zgadzam się z @crashmstr, mając jedną funkcję, która ma dwie różne rzeczy, to zły zapach. –
W rzeczywistości już nazwa 'CheckValidity' sugeruje, że ta funkcja sprawdza tylko i może być stała. Jeśli chcesz 'MakeValid' to jest coś innego (i nie powinno być const ...) – user463035818