Создание доступных для чтения (открытых) членов класса в C ++ - PullRequest
4 голосов
/ 10 декабря 2011

Я пришел из таких языков, как Actionscript 3, где у нас есть особый способ определения переменной-члена как экземпляра, так и метод установки / извлечения значения защищенного или закрытого члена. Позвольте мне привести пример:

Внутри класса мы можем сказать что-то вроде этого:

private var _myString:String;

public get myString():String 
{
    return _myString;
}

public set myString(newValue:String):void
{
    //Do some super secret member protection n' stuff
    _myString = newValue;
}

И тогда за пределами этого объекта я могу сделать следующее:

trace(myClass.myString); //Output whatever _myString is. (note the lack of (). It's being accessed like property not a method...

И еще, я мог бы сделать что-то вроде удаления метода public set myString, поэтому, если кто-то попытается сделать это с моим классом:

myClass.myString = "Something"; //Try to assign - again note the lack of ()

Будет выдано сообщение об ошибке, сообщающее пользователю, что свойство доступно только для чтения.

Теперь, когда я использую C ++, и он бесконечно более потрясающий, чем Actionscript 3, мне интересно, как я могу имитировать этот тип поведения. Я не хочу использовать кучу грязных getVariable() и setVariable() методов. Я надеялся, что с помощью некоторой хитрости перегрузки операторов я смогу сделать то же самое здесь. Заметьте, я новичок, поэтому, пожалуйста, обращайтесь ко мне как таковой. :)

Обновление Я думаю, что самый простой способ объяснить это, я пытаюсь по существу иметь геттеры и сеттеры, но вызывать их с помощью присваивания, а не с помощью скобок ().

Ответы [ 5 ]

8 голосов
/ 10 декабря 2011

Извините, в C ++, синтаксического сахара нет , что означает, что вы должны сами реализовать методы get / set. Таким образом, в вашем случае вам придется реализовать метод getVariable () без его эквивалентного метода Set, чтобы сделать его доступным только для чтения.

И, пожалуйста, не прибегайте к макросам, чтобы сделать его Смотри как будто у тебя есть свойства только для чтения. Это просто разозлит людей, когда они прочитают твой код через 5 лет.

3 голосов
/ 10 декабря 2011

Если я правильно понимаю ваш вопрос, вы хотите создать const публичную переменную-член в терминах C ++.

class myClass
{
//...
public:
  const std::string myString;

  myClass(std::string s) : myString(s) {}
};

Итак, myClass::myString инициализируется, когда вы объявляете объект class ион остается неизменным (только для чтения) в течение всего срока его службы.

Как побочный эффект этого подхода, теперь myClass объекты не могут быть по умолчанию назначаемыми.то есть

myClass o1("s"), o2("t");
o1 = o2; // error
2 голосов
/ 01 апреля 2014

На самом деле это возможно с использованием шаблонных приматов.

Если вы хотите переменную только для чтения, но не хотите, чтобы клиенту приходилось менять способ доступа к ней, попробуйте этот шаблонный класс:

template<typename MemberOfWhichClass, typename primative>                                       
class ReadOnly {
    friend MemberOfWhichClass;
public:
    inline operator primative() const                 { return x; }

    template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
    template<typename number> inline number operator+ (const number& y) const { return x + y; } 
    template<typename number> inline number operator- (const number& y) const { return x - y; } 
    template<typename number> inline number operator* (const number& y) const { return x * y; }  
    template<typename number> inline number operator/ (const number& y) const { return x / y; } 
    template<typename number> inline number operator<<(const number& y) const { return x <<y; }
    template<typename number> inline number operator>>(const number& y) const { return x >> y; }
    template<typename number> inline number operator^ (const number& y) const { return x ^ y; }
    template<typename number> inline number operator| (const number& y) const { return x | y; }
    template<typename number> inline number operator& (const number& y) const { return x & y; }
    template<typename number> inline number operator&&(const number& y) const { return x &&y; }
    template<typename number> inline number operator||(const number& y) const { return x ||y; }
    template<typename number> inline number operator~() const                 { return ~x; }

protected:
    template<typename number> inline number operator= (const number& y) { return x = y; }       
    template<typename number> inline number operator+=(const number& y) { return x += y; }      
    template<typename number> inline number operator-=(const number& y) { return x -= y; }      
    template<typename number> inline number operator*=(const number& y) { return x *= y; }      
    template<typename number> inline number operator/=(const number& y) { return x /= y; }      
    template<typename number> inline number operator&=(const number& y) { return x &= y; }
    template<typename number> inline number operator|=(const number& y) { return x |= y; }
    primative x;                                                                                
};      

Пример использования:

class Foo {
public:
    ReadOnly<Foo, int> x;
};

Теперь вы можете получить доступ к Foo.x, но не можете изменить Foo.x! Помните, что вам нужно добавить побитовые и унарные операторы! Это всего лишь пример, с которого можно начать

2 голосов
/ 10 декабря 2011

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

В принципе, вы можете сделать что-то вроде этого:

template<typename T>
class AbstractGetter {
  T &refT;
public:
  operator const T() const { return refT; }
  AbstractGetter(T &refT_) : refT(refT_) { }
};

class Foo {
  int foo_private;
public:
  AbstractGetter<int> foo;

  Foo() : foo(foo_private) { }
};

ОДНАКО: AbstractGetter здесь стоит вам sizeof(int&) в дополнительной памяти.И это тоже довольно уродливый хак - если вы начинаете нуждаться в представлении чего-либо более сложного, чем просто ссылка (может быть, вашему геттеру нужна какая-то логика?), То синтаксис или накладные расходы памяти становятся намного хуже.

Кактаким образом, это обычный стиль C ++ для использования функций-получателей, а не хаков, подобных этому.

0 голосов
/ 18 ноября 2018

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

Вот шаблон оболочки.

// element wrapper for creating a getter and setter.
template <typename Base, typename T>
class getterSetter{
  T value;
  // below members required for setting up setter logic in owner class.
  Base *parent;            //owner object pointer
  bool (Base::*setter)(T&); //setter callback return true to set, false to not set.

public:
  getterSetter(T& v, Base *p, bool (Base::*set)(T&))
    : value(v),
      parent(p),
      setter(set)
  {} // read-write constructor.                                                                                                             
  getterSetter(T& v): value(v),parent(NULL), setter(NULL) {} // read only constructor.                                                      

  // setter implemented via operator overloading of = operator.
  const getterSetter& operator=(const T& v) {
    if (this->parent && this->setter && (parent->*(this->setter))(v)) {
      value = v;
    }
    else {
      // throw an exception here.                                                                                                           
    }
    return *this;
  }

  // cast sometimes helps for a getter.
  operator T() const{
    return value;
  }
};

Реализация будет как

class MyClass {
public:
  getterSetter<MyClass, std::string> myString;
  MyClass(std::string v): myString(v, this, &test::setX){}
  // MyClass(std::string v): myString(v) {} ;//for readonly myString.
  MyClass(MyClass&) = delete;

  bool setX(std::string& v) {
    // your setter logic here.                                                                                                                     
    return true;
  }
};

Таким образом, это может быть использовано как установщик и получатель без парантеза.

объявить MyClass x("hello");

, затем x.myString = "new value"; // this will call setter implementation in MyClass

назначение как

std::string newString;
newString = x.myString;

будет работать.

Хотя это можно сделать, это не очень хорошая вещь в c ++. он использует два дополнительных указателя памяти для каждого свойства, что плохо. Также необходимо написать больше перегрузок операторов для работы с STL и внешним кодом. Там может быть лучший способ.

...