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