W C # jest ładny cukier składniowy dla pól z getter i setter. Ponadto podoba mi się auto-implementowane właściwości, które pozwalają mi pisać
public Foo foo { get; private set; }
W C ++ muszę pisać
private:
Foo foo;
public:
Foo getFoo() { return foo; }
Czy jest jakaś taka koncepcja w C ++ 11, która pozwala mi mieć trochę cukru składniowego w tym?
Odpowiedzi:
W C ++ możesz pisać własne funkcje. Oto przykładowa implementacja właściwości przy użyciu nienazwanych klas. Artykuł w Wikipedii
struct Foo { class { int value; public: int & operator = (const int &i) { return value = i; } operator int () const { return value; } } alpha; class { float value; public: float & operator = (const float &f) { return value = f; } operator float () const { return value; } } bravo; };
Możesz napisać własne metody pobierające i ustawiające, a jeśli chcesz mieć dostęp do członka klasy posiadacza, możesz rozszerzyć ten przykładowy kod.
źródło
_alpha
dla zmiennej prywatnej ialpha
dla odniesienia.sizeof(Foo)
.C ++ nie ma tego wbudowanego, możesz zdefiniować szablon, aby naśladować funkcjonalność właściwości:
template <typename T> class Property { public: virtual ~Property() {} //C++11: use override and =default; virtual T& operator= (const T& f) { return value = f; } virtual const T& operator() () const { return value; } virtual explicit operator const T& () const { return value; } virtual T* operator->() { return &value; } protected: T value; };
Aby zdefiniować właściwość :
Property<float> x;
Aby zaimplementować niestandardowy getter / setter, po prostu dziedzicz:
class : public Property<float> { virtual float & operator = (const float &f) { /*custom code*/ return value = f; } virtual operator float const & () const { /*custom code*/ return value; } } y;
Aby zdefiniować właściwość tylko do odczytu :
template <typename T> class ReadOnlyProperty { public: virtual ~ReadOnlyProperty() {} virtual operator T const & () const { return value; } protected: T value; };
I do wykorzystania w klasie
Owner
:class Owner { public: class : public ReadOnlyProperty<float> { friend class Owner; } x; Owner() { x.value = 8; } };
Możesz zdefiniować niektóre z powyższych w makrach, aby były bardziej zwięzłe.
źródło
virtual
jest prawdopodobnie niepotrzebne w większości przypadków użycia, ponieważ właściwość prawdopodobnie nie będzie używana polimorficznie.W języku C ++ nie ma nic, co działałoby na wszystkich platformach i kompilatorach.
Ale jeśli chcesz złamać kompatybilność między platformami i zdecydować się na konkretny kompilator, możesz użyć takiej składni, na przykład w Microsoft Visual C ++, możesz to zrobić
// declspec_property.cpp struct S { int i; void putprop(int j) { i = j; } int getprop() { return i; } __declspec(property(get = getprop, put = putprop)) int the_prop; }; int main() { S s; s.the_prop = 5; return s.the_prop; }
źródło
Możesz do pewnego stopnia emulować getter i setter, mając członka dedykowanego typu i nadpisując
operator(type)
ioperator=
dla niego. Czy to dobry pomysł, to kolejne pytanie i idę do odpowiedzi+1
Kerreka SB, aby wyrazić swoją opinię na ten temat :)źródło
friend
do właściciela pola.Może spójrz na klasę właściwości, którą utworzyłem w ciągu ostatnich godzin: /codereview/7786/c11-feedback-on-my-approach-to-c-like-class-properties
Pozwala mieć właściwości zachowujące się w ten sposób:
CTestClass myClass = CTestClass(); myClass.AspectRatio = 1.4; myClass.Left = 20; myClass.Right = 80; myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);
źródło
W C ++ 11 możesz zdefiniować szablon klasy właściwości i używać go w następujący sposób:
class Test{ public: Property<int, Test> Number{this,&Test::setNumber,&Test::getNumber}; private: int itsNumber; void setNumber(int theNumber) { itsNumber = theNumber; } int getNumber() const { return itsNumber; } };
A oto szablon klasy Property.
template<typename T, typename C> class Property{ public: using SetterType = void (C::*)(T); using GetterType = T (C::*)() const; Property(C* theObject, SetterType theSetter, GetterType theGetter) :itsObject(theObject), itsSetter(theSetter), itsGetter(theGetter) { } operator T() const { return (itsObject->*itsGetter)(); } C& operator = (T theValue) { (itsObject->*itsSetter)(theValue); return *itsObject; } private: C* const itsObject; SetterType const itsSetter; GetterType const itsGetter; };
źródło
C::*
znaczy Nigdy wcześniej nie widziałem czegoś takiego?C
. Jest to podobne do zwykłego wskaźnika funkcji, ale aby wywołać funkcję składową, musisz podać obiekt, na którym ta funkcja jest wywoływana. Osiąga się to za pomocą liniiitsObject->*itsSetter(theValue)
w powyższym przykładzie. Zobacz tutaj, aby uzyskać bardziej szczegółowy opis tej funkcji.Jak już powiedziało wielu innych, nie ma wbudowanej obsługi języka. Jeśli jednak celujesz w kompilator Microsoft C ++, możesz skorzystać z rozszerzenia specyficznego dla firmy Microsoft dla właściwości, które jest udokumentowane tutaj.
Oto przykład z połączonej strony:
// declspec_property.cpp struct S { int i; void putprop(int j) { i = j; } int getprop() { return i; } __declspec(property(get = getprop, put = putprop)) int the_prop; }; int main() { S s; s.the_prop = 5; return s.the_prop; }
źródło
Nie, C ++ nie ma pojęcia właściwości. Chociaż zdefiniowanie i wywołanie metody getThis () lub setThat (wartość) może być niewygodne, użytkownik informuje użytkownika o tych metodach, że mogą wystąpić pewne funkcje. Z drugiej strony dostęp do pól w C ++ informuje konsumenta, że nie wystąpią żadne dodatkowe lub nieoczekiwane funkcje. Właściwości sprawiłyby, że byłoby to mniej oczywiste, ponieważ dostęp do właściwości na pierwszy rzut oka wydaje się reagować jak pole, ale w rzeczywistości reaguje jak metoda.
Nawiasem mówiąc, pracowałem w aplikacji .NET (bardzo dobrze znanym CMS) próbując stworzyć system członkostwa klientów. Ze względu na sposób, w jaki używali właściwości dla swoich obiektów użytkowników, uruchamiane były akcje, których nie przewidziałem, powodując, że moje implementacje wykonywały się w dziwaczny sposób, w tym w nieskończoną rekurencję. Było tak, ponieważ ich obiekty użytkowników wywoływały warstwę dostępu do danych lub jakiś globalny system buforowania, próbując uzyskać dostęp do prostych rzeczy, takich jak StreetAddress. Cały ich system opierał się na czymś, co nazwałbym nadużyciem własności. Gdyby zamiast właściwości zastosowali metody, myślę, że o wiele szybciej zorientowałbym się, co jest nie tak. Gdyby użyli pól (lub przynajmniej sprawili, że ich właściwości zachowywały się bardziej jak pola), myślę, że system byłby łatwiejszy w rozbudowie i utrzymaniu.
[Edytuj] Zmieniłem moje myśli. Miałem zły dzień i trochę poszedłem na tyradę. To czyszczenie powinno być bardziej profesjonalne.
źródło
Na podstawie https://stackoverflow.com/a/23109533/404734 tutaj jest wersja z publicznym pobierającym i prywatnym ustawiaczem:
struct Foo { class { int value; int& operator= (const int& i) { return value = i; } friend struct Foo; public: operator int() const { return value; } } alpha; };
źródło
To nie jest dokładnie właściwość, ale robi to, co chcesz w prosty sposób:
class Foo { int x; public: const int& X; Foo() : X(x) { ... } };
Tutaj duży X zachowuje się jak
public int X { get; private set; }
w składni C #. Jeśli chcesz pełnowartościowych właściwości, zrobiłem pierwsze zdjęcie, aby je tutaj wdrożyć .źródło
X
do nowego obiektu będzie nadal wskazywało na element składowy starego obiektu, ponieważ jest on po prostu kopiowany jak element wskaźnika. Jest to złe samo w sobie, ale kiedy stary obiekt zostanie usunięty, pojawi się uszkodzenie pamięci. Aby to zadziałało, musiałbyś również zaimplementować własny konstruktor kopiujący, operator przypisania i konstruktor przenoszenia.Prawdopodobnie wiesz o tym, ale zrobiłbym po prostu, co następuje:
class Person { public: std::string name() { return _name; } void name(std::string value) { _name = value; } private: std::string _name; };
To podejście jest proste, nie wykorzystuje sprytnych sztuczek i wykonuje swoją pracę!
Problem polega jednak na tym, że niektórzy ludzie nie lubią poprzedzać swoich prywatnych pól podkreśleniem, więc tak naprawdę nie mogą używać tego podejścia, ale na szczęście dla tych, którzy to robią, jest to naprawdę proste. :)
Prefiksy get and set nie dodają jasności do twojego API, ale sprawiają, że są bardziej szczegółowe, a powodem, dla którego nie sądzę, aby dodawały użyteczne informacje, jest to, że gdy ktoś musi użyć API, jeśli API ma sens, prawdopodobnie zdaje sobie sprawę, co to jest działa bez przedrostków.
I jeszcze jedno, łatwo zrozumieć, że są to właściwości, ponieważ
name
nie jest to czasownik.W najgorszym przypadku, jeśli interfejsy API są spójne, a osoba nie zdawała sobie sprawy, że
name()
jest akcesorium iname(value)
jest mutatorem, będzie musiała tylko raz to sprawdzić w dokumentacji, aby zrozumieć wzorzec.Chociaż kocham C #, nie sądzę, żeby C ++ w ogóle potrzebował właściwości!
źródło
foo(bar)
(zamiast wolniejszychfoo = bar
), ale twoje akcesory nie mają w ogóle nic wspólnego z właściwościami ...foo(bar)
a zamiast tegofoo=bar
można to osiągnąć za pomocąvoid foo(Bar bar)
metody mutatora na_foo
zmiennej składowej .Nie. Ale powinieneś rozważyć, czy to jest po prostu funkcja get: set i żadne dodatkowe zadanie nie jest wykonywane wewnątrz metody get: set, po prostu upublicznij ją.
źródło
Zebrałem pomysły z wielu źródeł C ++ i umieściłem je w ładnym, wciąż dość prostym przykładzie pobierania / ustawiania w C ++:
class Canvas { public: void resize() { cout << "resize to " << width << " " << height << endl; } Canvas(int w, int h) : width(*this), height(*this) { cout << "new canvas " << w << " " << h << endl; width.value = w; height.value = h; } class Width { public: Canvas& canvas; int value; Width(Canvas& canvas): canvas(canvas) {} int & operator = (const int &i) { value = i; canvas.resize(); return value; } operator int () const { return value; } } width; class Height { public: Canvas& canvas; int value; Height(Canvas& canvas): canvas(canvas) {} int & operator = (const int &i) { value = i; canvas.resize(); return value; } operator int () const { return value; } } height; }; int main() { Canvas canvas(256, 256); canvas.width = 128; canvas.height = 64; }
Wynik:
new canvas 256 256 resize to 128 256 resize to 128 64
Możesz to przetestować online tutaj: http://codepad.org/zosxqjTX
źródło
Czy twoja klasa naprawdę musi wymusić jakąś niezmienną, czy jest to tylko logiczne zgrupowanie elementów składowych? Jeśli to drugie, powinieneś rozważyć utworzenie struktury i bezpośredni dostęp do członków.
źródło
Jest to zestaw makr napisanych tutaj . Ma wygodne deklaracje właściwości dla typów wartości, typów odwołań, typów tylko do odczytu, typów silnych i słabych.
class MyClass { // Use assign for value types. NTPropertyAssign(int, StudentId) public: ... }
źródło