Имеет ли С++ 11 свойства стиля С#?

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

public Foo foo { get; private set; }

В С++ мне приходится писать

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

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

Ответ 1

Нет, это не так.

Тем не менее, философия С++ заключается в содействии инкапсуляции. Всякий раз, когда вы обнаружите, что ваш интерфейс просто проходит через элементы-члены ( "getters and setters" ), подумайте еще раз. Ваш класс должен иметь полноценный интерфейс для того, что делает класс, а не просто служить свободным контейнером для вещей. Объекты-члены должны быть деталями реализации, а не быть частью открытого интерфейса. Если вам нужен свободный контейнер, просто используйте std::tuple или что-то в этом роде.

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 6

Как уже говорили многие другие, в языке нет встроенной поддержки. Однако, если вы нацелены на компилятор 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;
}

Ответ 7

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

Ответ 8

В языке 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;  
}

Ответ 9

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

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

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

Ответ 10

На основе fooobar.com/questions/109912/... здесь представлена ​​версия с публичным получателем и частным установщиком:

struct Foo
{
    class
    {
            int value;
            int& operator= (const int& i) { return value = i; }
            friend struct Foo;
        public:
            operator int() const { return value; }
    } alpha;
};

Ответ 11

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

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) является мутатором, тогда ей придется искать его только один раз в документации, чтобы понять шаблон.

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

Ответ 12

Это не совсем свойство, но оно делает то, что вы хотите простым способом:

class Foo {
  int x;
public:
  const int& X;
  Foo() : X(x) {
    ...
  }
};

Здесь большой X ведет себя как public int X { get; private set; } в синтаксисе С#. Если вы хотите полномасштабные свойства, я сделал первый снимок, чтобы реализовать их здесь.

Ответ 13

Нет... Но вы должны подумать, если это просто функция get: set и никакие дополнительные задачи не выполняются внутри get: set, методы просто делают ее общедоступной.

Ответ 14

Требуется ли вам класс для принудительного применения некоторого инварианта или это просто логическая группировка элементов-членов? Если это последний, вы должны подумать о том, чтобы создать структуру и получить доступ к членам напрямую.

Ответ 15

Я собрал идеи из нескольких источников C++ и поместил их в хороший, все еще довольно простой пример для методов получения/установки в 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;
}

Выход:

new canvas 256 256
resize to 128 256
resize to 128 64

Вы можете проверить это онлайн здесь: http://codepad.org/zosxqjTX