2013-03-07 10 views
5

Wiem, że wdrażanie obiektów podobnych do nieruchomości było wielokrotnie dyskutowane, ale to, czego potrzebuję, różni się nieco od różnych proponowanych rozwiązań.Zmienna właściwości w języku C++

Co chcę jest obiektem, który ma te same semantykę zmiennej, ale gdy jej wartość jest zmieniana Chcę wykonać pewne działania. Oznacza to posiadanie niejawnych metod pobierania/ustawiania, zaimplementowanych przeciążanie operatora = i niejawnego operatora konwersji.

Należy pamiętać, że kiedy mam na myśli "właściwość podobną", nie mam na myśli semantycznego object.property, ale tylko sposób na uzyskanie niejawnych metod pobierania/ustawiania (jak w property-like-variable = value). Powodem jest to, że mam jakiś istniejący kod, który używa zmiennej, chcę wykonać jakąś akcję za każdym razem, gdy zmienia się wartość zmiennej bez zmiany istniejącego kodu (tylko deklaracja zmiennej powinna się zmienić).

I uzyskali ten wynik z minimalnym kodu:

#include <stdio.h> 

class Int 
{ 
    int _value; 

public: 
    // default constructor 
    Int() { printf("Int: initial value undefined\n"); } 

    // assignment constructor 
    Int(int value) : _value(value) { printf("Int: initial value is %d\n", _value); } 

    // property get 
    operator const int() const { return _value; } 

    // property set 
    int operator=(int value) 
    { 
     _value = value; 
     printf("new value is %d\n", value); 
     return value; 
    } 

    // TODO: overload other operators if needed 
}; 

To jest przykład użycia:

class C 
{ 
    Int _i; 

public: 
    C() : _i(2) { } 

    void foo() 
    { 
     _i = 3; 
     printf("foo(): _i = %d\n", _i); 
    } 
}; 

int main() 
{ 
    int i; 
    Int I; 

    i = 1; 
    I = i; 
    I = I + 1; 

    C c; 
    c.foo(); 
} 

Teraz kilka pytań:

  1. ma tę potrzebę już omówione? Jest wzór projektu?
  2. Robię coś złego tutaj? (zarówno z punktu widzenia implementacji, jak i projektowania).
  3. W tym przypadku istnieje pewna wada wydajności lub kompilator jest w stanie zoptymalizować go bez obciążania?
  4. Istnieje lepszy sposób na wywoływanie tego konstruktu? Ponieważ nie wydaje się to ściśle "własnością" (wydaje się bardziej podobne do "boksowania" zmienną w klasie).
  5. Czy można to uogólnić szablonami? Nie widzę prostego sposobu, aby to zrobić, ponieważ potrzeba zrobienia tego jest wykonywanie niestandardowych rzeczy podczas pobierania/ustawiania wartości.
+1

Nazwałbym to po prostu „klasy otoki” otoczyć 'int' w nieco innym API, ale może jest na to lepsze słowo. – hyde

+0

Niedawno użyłem podobnego wzorca ... Jednak okazało się, że jest to problematyczne w połączeniu z 'auto' C++ 11, ponieważ operator konwersji nie zostanie w tym przypadku wywołany, a skopiowanie klasy opakowania może być niepożądane. – Julian

Odpowiedz

3

Ad pytanie 2: Widzę dwa ogólne problemy z kodem:

  1. pierwszy const kwalifikator w swojej deklaracji operator const int() jest bez znaczenia (gcc ostrzega o tym)

  2. się zwracany typ operatora przypisania powinien być Int& nie int inaczej będzie miał inną semantykę niż oryginalna int i następujący kod C++ nie skompiluje:

    Int i; 
    (i=2)=3; 
    

Ad pytanie 3: jeśli zestaw/get funkcje zrobić nic, ale przypisać/zwrotu wartości powinny być zoptymalizowane całkowicie. Jeśli jednak zadzwonisz pod numer printf(), możesz poważnie popaść w problemy z wydajnością;).

Ad pytanie 5: Uogólnienie z szablonami wydaje cieśninie naprzód, w tym dostosowanie go do różnych typów za pośrednictwem szablonu sepcialization:

template<typename T> class observe 
{ 
    T _value; 

public: 
    // default constructor 
    observe() { cout << "Int: initial value undefined" << endl; } 

    // assignment constructor 
    observe(const T& v) : _value(v) { cout << "initial value is " << v << endl; } 

    // property get 
    operator T() const { return _value; } 

    // property set 
    observe<T>& operator=(const T& value) 
    { 
     _value = value; 
     cout << "new value is " << _value << endl; 
     return *this; 
    } 
}; 

// special behavior for the assignment of doubles 
template<> observe<double>& observe<double>::operator =(const double& value) 
{ 
    _value = value; 
    cout << "new double value is " << _value << endl; 
    return *this; 
} 

observe<double> d; 
d = 1.0; 
+0

Dzięki za przydatne notatki. O słownym kwalifikatorze const, przyznaję, że semantyka tych kwalifikatorów jest zawsze nieco niejasna dla mojego umysłu ... dlaczego jest bez znaczenia? Nie mam gotowej konfiguracji GCC, a mój kompilator (MS VC++ 2005) nie zgłasza żadnych ostrzeżeń. Jakie ostrzeżenie jest zgłaszane przez GCC? Dzięki!!! – Wizard79

+1

'warning: kwalifikatory typów są ignorowane przy zwracaniu funkcji typu [-Wignored-qualifiers]' Interesujące jest jednak to, że nie generuje tego ostrzeżenia, gdy dodaję dodatkową const do mojej sugerowanej wersji "szablonowej". – axxel

0

Myślę, że aby było bardziej jak własność, kod "set" musiałby znajdować się wewnątrz class C. Jak o tym:

class C 
{ 
public: 
    class i_property_class { 

...methods from your current Int class 

    } i; 

...rest of class C 
} 

Jeśli to działa, to może warto spróbować klasę bazową dla i_property_class i przedłużyć go, zapewniając metody trzeba, i nadrzędne, ile potrzeba (uwaga, dziedzicząc operator= może być tricky) do uzyskać niestandardowe zachowanie dla każdej oddzielnej właściwości.

A następnie, jeśli chodzi o twoje pytanie 5, sprawdzenie, czy ta klasa podstawowa jest szablonem, również powinno działać.

+0

'klasa C' tutaj jest przeznaczona tylko jako przykład użycia. W moim realnym scenariuszu moja zmienna właściwości jest zbudowana wokół wyliczenia i jest używana w całej aplikacji. O klasie bazowej szablonu, jak obsługiwałbym "metodę zestawu" (która miałaby niestandardowe zachowanie dla każdej implementacji) bez użycia funkcji wirtualnej? – Wizard79

+0

Nie jestem pewien, czy możesz w znaczący sposób dziedziczyć 'operator =' (masz na myśli, że przez "ustawioną metodę", prawda?), To prawdopodobnie łatwiej, jeśli tylko przesłonisz go w klasach pochodnych. W każdym razie nie ma potrzeby uzyskiwania dostępu do właściwości za pomocą wskaźnika klasy podstawowej (lub przynajmniej jest to prawdopodobnie lepsze, jeśli zdecydujesz, że nie jest to dozwolone), więc nie ma potrzeby stosowania metod wirtualnych. – hyde

Powiązane problemy