Есть ли в C ++ 11 свойства стиля C #? - PullRequest
86 голосов
/ 03 декабря 2011

В C # есть хороший синтаксический сахар для полей с getter и setter. Кроме того, мне нравятся автоматически реализованные свойства, которые позволяют мне писать

public Foo foo { get; private set; }

В C ++ я должен написать

private:
    Foo foo;
public:
    Foo getFoo() { return foo; }

Есть ли в C ++ 11 такая концепция, позволяющая мне использовать синтаксический сахар?

Ответы [ 14 ]

76 голосов
/ 16 апреля 2014

В C ++ вы можете написать свои собственные функции.Вот пример реализации свойств с использованием безымянных классов. Статья Wiki

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;
};

Вы можете написать свои собственные методы получения и установки на месте, и если вы хотите получить доступ к члену класса держателя, вы можете расширить этот пример кода.

41 голосов
/ 18 февраля 2016

C ++ не имеет этого встроенного, вы можете определить шаблон для имитации функциональных свойств:

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;
};

К определить свойство :

Property<float> x;

Для реализации пользовательского геттера / сеттера просто наследуйте:

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;

Чтобы определить свойство только для чтения :

template <typename T>
class ReadOnlyProperty {
public:
    virtual ~ReadOnlyProperty() {}
    virtual operator T const & () const { return value; }
protected:
    T value;
};

И использовать его в классе Owner:

class Owner {
public:
    class : public ReadOnlyProperty<float> { friend class Owner; } x;
    Owner() { x.value = 8; }
};

Вы можете определить некоторые из вышеперечисленных в макросах , чтобы сделать их более краткими.

20 голосов
/ 12 марта 2018

В языке C ++ нет ничего, что могло бы работать на всех платформах и компиляторах.

Но если вы хотите нарушить кросс-платформенную совместимость и зафиксировать конкретный компилятор, вы можете использовать такой синтаксис, например в Microsoft Visual C ++ , вы можете сделать

// 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;  
}
18 голосов
/ 03 декабря 2011

Вы можете эмулировать геттер и сеттер в некоторой степени, имея член выделенного типа и переопределяя для него operator(type) и operator=.Является ли это хорошей идеей - это другой вопрос, и я собираюсь +1 ответить Kerrek SB, чтобы высказать свое мнение по этому вопросу:)

17 голосов
/ 14 января 2012

Может быть, посмотрите на класс свойств, который я собрал за последние часы: https://codereview.stackexchange.com/questions/7786/c11-feedback-on-my-approach-to-c-like-class-properties

Это позволяет вам иметь свойства, ведущие себя так:

CTestClass myClass = CTestClass();

myClass.AspectRatio = 1.4;
myClass.Left = 20;
myClass.Right = 80;
myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);
16 голосов
/ 06 ноября 2016

С C ++ 11 вы можете определить шаблон класса Property и использовать его следующим образом:

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; }
};

А вот и шаблон класса 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;
};
14 голосов
/ 09 марта 2016

Как уже говорили многие другие, в языке нет встроенной поддержки. Однако, если вы нацелены на компилятор Microsoft C ++, вы можете воспользоваться специфичным для Microsoft расширением для свойств, которые описаны здесь .

Это пример со связанной страницы:

// 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;
}
12 голосов
/ 26 декабря 2013

Нет, C ++ не имеет понятия свойств.Хотя может быть неудобно определять и вызывать getThis () или setThat (value), вы делаете для потребителя заявление о том, что некоторые функции могут возникнуть.С другой стороны, доступ к полям в C ++ говорит потребителю, что никакой дополнительной или неожиданной функциональности не произойдет.Свойства сделали бы это менее очевидным, так как доступ к свойствам на первый взгляд, кажется, реагирует как поле, но на самом деле реагирует как метод.

Кроме того, я работал в приложении .NET (очень известномCMS) пытается создать систему членства клиента.Из-за того, как они использовали свойства для своих пользовательских объектов, запускались действия, которых я не ожидал, в результате чего мои реализации выполнялись причудливыми способами, включая бесконечную рекурсию.Это произошло потому, что их пользовательские объекты обращались к уровню доступа к данным или к какой-либо глобальной системе кэширования при попытке доступа к простым вещам, таким как StreetAddress.Вся их система была основана на том, что я бы назвал злоупотреблением имуществом.Если бы они использовали методы вместо свойств, я думаю, я бы понял, что происходит гораздо быстрее.Если бы они использовали поля (или хотя бы сделали их свойства более похожими на поля), я думаю, что систему было бы проще расширять и поддерживать.

[Редактировать] Изменил мои мысли.У меня был плохой день и я немного разглагольствовал.Эта уборка должна быть более профессиональной.

11 голосов
/ 08 октября 2014

На основе https://stackoverflow.com/a/23109533/404734 приведена версия с общедоступным и частным установщиком:

struct Foo
{
    class
    {
            int value;
            int& operator= (const int& i) { return value = i; }
            friend struct Foo;
        public:
            operator int() const { return value; }
    } alpha;
};
5 голосов
/ 19 марта 2016

Вы, наверное, знаете это, но я бы просто сделал следующее:

class Person {
public:
    std::string name() {
        return _name;
    }
    void name(std::string value) {
        _name = value;
    }
private:
    std::string _name;
};

Этот подход прост, не использует хитрых уловок и выполняет свою работу!

Проблема, однако, в том, что некоторые люди не любят ставить префикс своих личных полей с подчеркиванием, и поэтому они не могут действительно использовать этот подход, но, к счастью для тех, кто делает, это действительно просто. :)

Префиксы get и set не добавляют ясности вашему API, но делают их более многословными, и я не думаю, что они добавляют полезную информацию, потому что, когда кто-то должен использовать API, если API имеет смысл, он, вероятно, будет понять, что он делает без префиксов.

Еще одна вещь, легко понять, что это свойства, потому что name не глагол.

Сценарий наихудшего случая, если API согласованы, и человек не осознал, что name() является аксессором, а name(value) мутатором, тогда ему нужно будет только один раз найти его в документации, чтобы понять шаблон .

Столько, сколько я люблю C #, я не думаю, что C ++ вообще нуждается в свойствах!

...