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

Creating read-only (public) class members in C++

本文关键字:公共 成员 只读 C++ 创建      更新时间:2023-10-16

我来自 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,但通过赋值而不是括号 (( 调用它们。

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

请不要求助于宏来使其看起来像您有一些只读属性。这只会激怒人们5年后阅读您的代码。

如果我

正确理解您的问题,您希望以C++术语创建const公共成员变量。

class myClass
{
//...
public:
  const std::string myString;
  myClass(std::string s) : myString(s) {}
};

所以现在,当您声明class对象时,myClass::myString 被初始化,并且它在其整个生命周期内保持不可变(只读(。

作为这种方法的副作用,现在myClass对象不能默认分配。

myClass o1("s"), o2("t");
o1 = o2; // error

获得您想要的行为并非不可能。但这也不一定是一个好主意。

原则上,您可以执行以下操作:

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++风格,而不是像这样的黑客。

实际上,使用模板化基词是可能的。

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

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

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