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

Posted

技术标签:

【中文标题】在 C++ 中创建只读(公共)类成员【英文标题】:Creating read-only (public) class members in C++ 【发布时间】:2011-12-10 06:28:56 【问题描述】:

我来自 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++ 中,variable() 和 setVariable() 调用是脏的。根据您的数据类型和内部类数据,它们实际上比原始公共变量更安全。 【参考方案1】:

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

并且请不要求助于宏来使其看起来像你有一些只读属性。这只会让人们在 5 年后阅读您的代码时感到生气。

【讨论】:

将您的答案标记为正确。经过这么长时间,我意识到这不是语言的一部分,没有人应该试图强迫它成为。 :) 但老实说,我真的很怀念在 C++ 中创建只读属性的可能性。有时人们只是忘记了不改变某些属性是有理由的。【参考方案2】:

如果我正确理解您的问题,您想用 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

【讨论】:

s/默认可分配/复制可分配/ 不幸的是,这不是我想要完成的。我试图本质上拥有 getter 和 setter,但通过赋值而不是括号 () 来调用它们。这是我能描述它的最好方式。 @JamesMcNellis,我想可以是default copy assignable。 没有“默认可分配”或“可默认复制分配”之类的东西。这称为复制可分配性。 @JamesMcNellis:对我来说很有意义:“default assignable”=“编译器自动生成的赋值运算符不会导致编译错误”。【参考方案3】:

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

原则上,您可以这样做:

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

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

【讨论】:

AbstractGetter添加一个私有setter怎么样,让它把Foo作为第二个模板参数和Foo成为朋友,然后让它直接存储T 我同意。即使我无法理解这些东西...... :) @JamesMcNellis,好吧,我承认这会更好,但它不会让您将任何智能放入 getter :) 我找到了这个答案:***.com/questions/760777/…。用户“j_random_hacker”似乎回答了几乎相同的问题,我只是没有完全理解它。大声笑不知道这是否有助于澄清任何事情。 我并不是说我会推荐这样做:我讨厌偷偷摸摸的隐式转换。【参考方案4】:

实际上可以使用模板基元。

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

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

【讨论】:

解决方案比问题更糟糕。一百万年后,我永远不会诉诸这样的伎俩。但它很聪明,我对此表示赞赏。【参考方案5】:

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

通过这种方式,它可以用作没有括号的setter和getter。

声明MyClass x("hello");

然后x.myString = "new value"; // this will call setter implementation in MyClass

类似的作业

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

会起作用的。

虽然这可以做到,但在c++中可不是什么好事。它为每个不好的属性使用两个额外的指针内存。还需要编写更多的运算符重载来处理 STL 和外部代码。可能有更好的方法。

【讨论】:

以上是关于在 C++ 中创建只读(公共)类成员的主要内容,如果未能解决你的问题,请参考以下文章

试图将类成员公开为只读或公共常量

使用 COM 在 C++ 中实例化 C# 类成员

让公共成员变量访问 C++ 中同一类的私有成员

派生类 C++ 中基类受保护成员的访问声明为公共的

是否可以在 JavaScript Object Literal Notation 中创建只读成员?

C++ 使用变量访问类的公共成员