在C ++中创建只读(公共)类成员

时间:2011-12-10 06:28:56

标签: c++ getter-setter reflection

我来自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()方法。我希望通过一些操作符重载技巧我可以在这里完全相同的事情。注意我是菜鸟,所以请你这样说。 :)

更新 我想解释这个的最简单的方法是,我试图基本上有getter和setter但是通过赋值而不是用括号()来调用它们。

5 个答案:

答案 0 :(得分:8)

抱歉,使用C ++,语法糖不存在,这意味着您必须自己实现get / set方法。 因此,在您的情况下,您将不得不实现一个getVariable()方法,而不是它的等效Set方法,以使其只读。

请不要诉诸宏来使就像你有一些只读属性。 5年后,当他们阅读你的代码时,这只会让人失望。

答案 1 :(得分:3)

如果我理解你的问题,你想用C ++术语创建const公共成员变量。

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 :(得分:2)

获得你想要的那种行为并非不可能。但它也不一定是个好主意。

原则上,您可以这样做:

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&)。而且它也是一个相当丑陋的黑客 - 如果你开始需要表示比引用更复杂的东西(也许你的getter需要有一些逻辑?)语法或内存开销会变得更糟。

因此,使用getter函数是正常的C ++风格,而不是像这样的黑客。

答案 3 :(得分:2)

实际上可以使用模板化的灵长类动物。

如果您想要一个只读变量但不希望客户端必须更改它们访问它的方式,请尝试这个模板化的类:

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! 请记住,您还需要添加按位和一元运算符!这只是一个让你入门的例子

答案 4 :(得分:0)

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和外部代码。也许有更好的方法。