在 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&)
的额外内存。而且这也是一个相当丑陋的 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++ 中创建只读(公共)类成员的主要内容,如果未能解决你的问题,请参考以下文章