回答
您可以使用类似于Jon所建议的解决方案,但使用运算符重载保留普通的C++语义。我稍微修改Jon的代码如下(解释遵循的代码):
#include <iostream>
template<typename T>
class Accessor {
public:
explicit Accessor(const T& data) : value(data) {}
Accessor& operator=(const T& data) { value = data; return *this; }
Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; }
operator T() const { return value; }
operator T&() { return value; }
private:
Accessor(const Accessor&);
T value;
};
struct Point {
Point(int a = 0, int b = 0) : x(a), y(b) {}
Accessor<int> x;
Accessor<int> y;
};
int main() {
Point p;
p.x = 10;
p.y = 20;
p.x++;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
我们超载operator=
保留了通常的赋值语法,而不是一个函数调用的语法。我们使用演员操作符作为“getter”。我们需要operator=
的第二个版本来允许在main()
中分配第二种类型。
现在,您可以添加到Accessor的构造函数指针或更好的函数 - 以任何方式调用getter/setters似乎是正确的。下面的示例假设setter函数返回布尔传达协议设置新值,吸气剂可以只修改它的出路:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data)
{
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator()(const T& data)
{
return round(data, 2);
}
private:
double cint(double x) {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { value = getter(value); return value;}
operator T&() { value = getter(value); return value; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
然而,随着最后一行表明,它有一个bug。返回T &的演员操作员允许用户绕过设置者,因为它允许他们访问私有值。解决这个错误的一种方法是实现你希望Accessor提供的所有操作符。例如,在下面的代码我使用了+ =运营商,因为我脱离的铸造操作返回参考我不得不实施operator+=
:
#include <iostream>
#include <functional>
#include <cmath>
template<typename T>
class MySetter {
public:
bool operator()(const T& data) const {
return (data <= 20 ? true : false);
}
};
template<typename T>
class MyGetter {
public:
T operator() (const T& data) const {
return round(data, 2);
}
private:
double cint(double x) const {
double dummy;
if (modf(x,&dummy) >= 0.5) {
return (x >= 0 ? ceil(x) : floor(x));
} else {
return (x < 0 ? ceil(x) : floor(x));
}
}
double round(double r, int places) const {
double off = pow(10.0L, places);
return cint(r*off)/off;
}
};
template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
private:
public:
explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}
Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
operator T() const { return getter(value);}
Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; }
private:
Accessor(const Accessor&);
T value;
G getter;
S setter;
};
struct Point {
Point(double a = 0, double b = 0) : x(a), y(b) {}
Accessor<double> x;
Accessor<double> y;
};
int main() {
Point p;
p.x = 10.712;
p.y = 20.3456;
p.x+=1;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 15.6426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 25.85426;
std::cout << p.x << "," << p.y << std::endl;
p.x = p.y = 19.8425;
p.y+=1;
std::cout << p.x << "," << p.y << std::endl;
return 0;
}
你必须实现所有你要的运营商使用。
+ 1击败了我,但我不喜欢这种提供大量运算符重载的想法。另一种处理外部失效值的方法是提供一个不同的“被写入”回调函数,当下一次读取值时会调用它。 – 2010-04-02 00:08:25
考虑到你只在模板类中定义了所有这些运算符,我不认为这太可怕了。在我看来,STL或boost代码与此相比(以及一般来说太:))是可怕的。 但也有其他选择。必须考虑自己的情况并据此作出选择。 :) – conio 2010-04-04 00:52:50
你不知道。 C++不支持像C#那样的属性。如果你想让代码在set/get上运行,它必须是一个方法。
属性真的可以在C#中运行方法。编译器将它从你身上隐藏起来。 – 2010-04-01 19:46:31
对于这样的行为,我使用模板化的元访问器。这是一个高度简化的一个POD类型:
template<class T>
struct accessor {
explicit accessor(const T& data) : value(data) {}
T operator()() const { return value; }
T& operator()() { return value; }
void operator()(const T& data) { value = data; }
private:
accessor(const accessor&);
accessor& operator=(const accessor&);
T value;
};
典型用法是这样的:
struct point {
point(int a = 0, int b = 0) : x(a), y(b) {}
accessor<int> x;
accessor<int> y;
};
point p;
p.x(10);
p.y(20);
p.x()++;
std::cout << p.x();
编译器,如果你设置的东西,并有权利优化开启通常内联这些调用。与使用实际的getter和setter不同,它不再是性能瓶颈,无论发生什么优化。将它扩展为自动支持非POD或枚举类型或允许在读取或写入数据时允许注册回调是微不足道的。
编辑:如果您不想使用括号,则可以始终定义operator=()
和隐式转换运算符。这里有一个版本可以做到这一点,同时也添加了基本的“东西发生”回调支持:
更多编辑:好的,完全错过了某人已经做了我的代码的修订版本。叹。
当你可以定义operator =时,为什么使用笨重的函子风格? – 2010-04-01 23:24:16
@Ben:为完整性添加差异。它忽略了C#中的那些垃圾。 – 2010-04-01 23:59:45
属性是不是在C++的支持,但你可以实现它们:
1)通过使用模板
2)通过使语言的扩展和编写自定义代码预处理器
两种方法都不会是一帆风顺的,但这是可以完成的。
C++支持重载operator =,所以不需要扩展。 – 2010-04-01 23:21:33
@ Ben Voigt这取决于你想要实现什么,以及你想要的属性数量。使用大量的代码,引入一个或两个关键字并编写代码预处理器将是更好的主意 - 它将使代码更具可读性。 – SigTerm 2010-04-02 15:22:20
这是我一段时间后做的一个PoC实现,除了需要在构造函数中设置一些东西以使其良好且流畅地工作以外,其效果很好。
http://www.codef00.com/code/Property.h
这里的用法示例:
#include <iostream>
#include "Property.h"
class TestClass {
public:
// make sure to initialize the properties with pointers to the object
// which owns the property
TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) {
}
private:
int getProp1() const {
return m_Prop1;
}
void setProp1(int value) {
m_Prop1 = value;
}
int getProp2() const {
return 1234;
}
void setProp3(double value) {
m_Prop3 = value;
}
int m_Prop1;
double m_Prop3;
public:
PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1;
PropertyRO<int, TestClass, &TestClass::getProp2> prop2;
PropertyWO<double, TestClass, &TestClass::setProp3> prop3;
};
和这个类的一些使用...
int main() {
unsigned int a;
TestClass t;
t.prop1 = 10;
a = t.prop1;
t.prop3 = 5;
a = t.prop2;
std::cout << a << std::endl;
return 0;
}
有两个烦恼这种方法:
- 你需要克属性a 指向其拥有的类。
- 的语法来声明一个属性是 有点冗长,但我敢打赌,我可以清理 说了一下一些宏
有点尴尬,是的,但使用'this'注册访问器的想法很好,因为它很容易让属性根据实例和类型信息玩弄技巧。 – 2010-04-02 00:20:10
您可以提供具有类似名称的数据成员get和set方法:
class Example
{
private:
unsigned int x_;
double d_;
std::string s_s;
public:
unsigned int x(void) const
{ return x_;}
void x(unsigned int new_value)
{ x_ = new_value;}
double d(void) const
{ return d_;}
void d(double new_value)
{ d_ = new_value;}
const std::string& s(void) const
{ return s_;}
void s(const std::string& new_value)
{ s_ = new_value;}
};
虽然这种接近,因为它需要使用“()”的每个成员,它不符合性能,微软的语言提供具体的功能。
最接近的属性匹配是声明数据成员为公共。
错误的是,通过重载operator =,你可以得到确切的语法(对于用户)和为你提供属性的任何语言的能力。 – 2010-04-01 23:23:02
如果您不关心您的C++代码不能用Microsoft Visual C++编译器以外的任何其他编译器编译,那么您可以使用某些编译器的非标准扩展。
例如,下面的代码将创建一个名为MyProperty
的C#类属性。
struct MyType
{
// This function pair may be private (for clean encapsulation)
int get_number() const { return m_number; }
void set_number(int number) { m_number = number; }
__declspec(property(get=get_number, put=set_number)) int MyProperty;
private:
int m_number:
}
int main()
{
MyType m;
m.MyProperty = 100;
return m.MyProperty;
}
有关此特定于Microsoft的语言扩展的更多信息,请访问here。
- 1. 设置公共类属性
- 2. JavaScript中的原型/类/公共属性
- 3. 公共变量 - 需要的属性C#
- 4. 公共存取VS类的公共属性
- 5. 创建内部类的公共属性?
- 6. 公共属性和私人成员C#
- 7. 公共属性不可子类
- 8. TypeScript Setter的公共属性
- 9. 我可以循环访问C++类的(公共)属性吗?
- 10. 这个属性如何在ruby类中拥有多个属性?
- 11. 类图属性可见性。公共属性可以有getter和setter操作?
- 12. 错误 - 类型'System.Web.UI.ScriptManager'没有名为'CompositeScript'的公共属性
- 13. MSI公共属性overriden
- 14. 公共属性定义
- 15. BLToolkit:公共只读属性
- 16. 在oneOf之前有可能拥有共同的属性吗?
- 17. 内部类改性拥有类的属性
- 18. ASP.NET C# - 如何设置UserControl中的CheckBoxList的公共属性?
- 19. vb.net中的另一个类的公共属性
- 20. 有没有办法在公共性状中拥有私人功能?
- 21. 服务类有一个公共属性,不解决
- 22. ColdFusion 9 CFScript私有属性和公共属性
- 23. IronPython如何访问C#中定义的公共静态属性?
- 24. 在.NET核心中获取类的公共属性
- 25. 是否有可能拥有共享/静态依赖属性?
- 26. 未被其他类识别的C++类属性(包含头部,公共)
- 27. 如何使公共方法仅在类本身和拥有C#中的对象的类中可见?
- 28. 在Visual Studio 2015中,asp.net mvc 6项目资源文件不会生成具有公共属性的公共类
- 29. 公共属性和不变性
- 30. C++类(公共,私有和受保护)
看看这里的类似问题:http://stackoverflow.com/questions/2017623/forward-unbreakable-accessor-class-templates-c。我们利用C++特性甩掉了一些实现属性的方法。 – 2010-04-01 11:30:07
平台特定的或平台无关的C++?在Windows中,VC++编译器具有支持此功能的非标准扩展。请参阅下面的答案。 – 2010-04-01 20:12:21