介绍
尽管我们都知道C#属性是为每个单独的成员字段编写getter / setter函数的良好替代,但实际上我们没有在C ++中使用属性的标准方法。这是C ++替代属性的快速实现。该设计旨在提供与使用C#属性时相似的感觉,尽管在实现中存在一些令人讨厌的方面。
使用代码
使用代码时,只需在代码顶部包含“ prop.h ”即可。(' prop.cpp '是一个示例源代码。)设计主要遵循C#属性的原理,但是您需要遵循一些小的语法细节。
宣言
首先,如果您的属性不需要实现任何特殊的get / set函数,那么请不要。
class Test {
public:
prop<size_t> p; //< This is a 'size_t' property.
}
您可以像普通成员字段一样使用声明的属性。例如:
int foo() {
Test test;
test.p = 3;
std::cout << test.p << std::endl;
}
其次,如果你的财产需要实现自己的getter和setter,那就给它提供这样的定义。请注意,setter函数通过reserved关键字接收调用者给定的值value
。
class Test {
size_t p_org; //< Let's assume the real value is to be stored here.
public:
prop<size_t> p {
__get__() { return p_org; }
__set__(size_t) { p_org = value; }
};
};
最后,您还可以通过仅提供getter函数并将原始类型指定为 声明来声明只读属性 。const
class Test {
size_t p_org; //< Let's assume the real value is to be stored here.
public:
prop<const size_t> p_readonly {
__get__() { return p_org; }
};
};
如果尝试使用只读属性设置任何值,则会出现以下编译时错误。
int main() {
Test test;
test.p_readonly = 0xdeadbeef; //< compile error: no operator= defined.
return 0;
}
...
test.cpp:3:19: error: no viable overloaded '='
test.p_readonly = 0xdeadbeef;
~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~
以下是一些重点;
- 你需要保持getter和setter的顺序; 你需要先声明getter。
-
需要使用基本类型声明任何属性,例如整数或浮点。
(它假设原始类型具有++, - 和各种复合赋值运算符的正确定义。) -
必须使用
const
类型声明只读属性,并且必须为其提供getter定义。
(否则,无论如何,你都无法获得想要获得的价值!)
初始化
您可以使用构造函数中的属性名称直接初始化默认属性。
class Test {
public:
prop<size_t> p;
Test() : p(0) {}
};
但是,如果属性具有自己的getter / setter,则可以初始化原始数据成员而不是属性本身。
class Test {
public:
size_t p_org;
prop<size_t> p {
__get__() { return p_org; }
__set__(size_t) { p_org = value; }
};
Test() : p_org(0) {}
};
物业读/写
读取和写入属性就像您使用常规公共成员字段一样。它支持可用于常规变量的各种操作。请参阅附件中的“ prop.cpp ”以查看示例读/写用法。
这个怎么运作
下面是标题' prop.h ' 的简化源代码。

#pragma once
#include <cassert>
#include <functional>
template <typename T>
class prop {
public:
using Getter = std::function<T()>;
using Setter = std::function<void(T)>;
private:
T __val__;
Getter __getter__;
Setter __setter__;
void initialize() {
__getter__ = [&]() { return __val__; };
__setter__ = [&](T value) { __val__ = value; };
}
/* ... */
public:
prop() { initialize(); }
prop(T o) : __val__(o) { initialize(); }
prop(Getter getter, Setter setter = readonly_setter()) :
__getter__(getter), __setter__(setter) {}
/* ... */
virtual prop<T>& operator=(T o)
{ __setter__(o); return *this; }
virtual operator T() const
{ return __getter__(); }
/* ... */
};
template <typename T>
class prop<const T> {
public:
using Getter = std::function<T()>;
private:
Getter __getter__;
prop(const prop<T>&) = delete;
prop(prop<T>&&) = delete;
public:
prop(Getter getter) : __getter__(getter) {}
virtual operator T() const
{ return __getter__(); }
};
#define __get__() [&]()
#define __set__(T) , [&](T& value)
基本上,您将声明基类的实例' prop
', 并可选择将其提供给您自己的getter / setter。的prop
类有三个成员,一(__val__
)为默认的getter / setter数据变量和其它两个拉姆达变量(__getter__
,__setter__
),用于可变操纵。
您用来指定自己的getter / setter(比如说__get__()
和__set__()
)的实际上是用宏编写的辅助函数。具体来说,__get__()
宏是getter函数的lambda声明, __set__()
宏也是。
这里一个令人讨厌的技巧是,
在宏定义的前面加上一个逗号(),这就是遵循统一初始化的语法。我只是在那里放了一个逗号,因为它可以防止程序员在getter定义之后需要添加额外逗号的混乱,也因为它可能允许属性声明看起来不那么尴尬,甚至一点点。 __set__()
最后,只读属性的基类是部分专用的prop
类(prop<const T>
)。更具体地说,只读prop
类专门用于任何常量类型,并且从不定义值修改操作符。由于必须提供适当的getter定义,因此在这种情况下没有定义默认构造函数。
历史
- 2019.05.28:刚发布
-
2019.05.28:基本操作(++, - ,+ =, - =,* =,/ =)和只读属性(不允许
__set__()
定义) - 2019.05.29:修复了次要编译错误
- 2019.05.30:修复了次要编译/运行时错误
- 2019.06.06:编译时只读属性功能。