在C#中,使用getter和setter的字段有一个不错的语法糖。此外,我喜欢自动执行的属性,这些属性使我可以编写
public Foo foo { get; private set; }
在C ++中,我必须编写
private:
Foo foo;
public:
Foo getFoo() { return foo; }
C ++ 11中是否有这样的概念,可以让我对此有所了解?
在C#中,使用getter和setter的字段有一个不错的语法糖。此外,我喜欢自动执行的属性,这些属性使我可以编写
public Foo foo { get; private set; }
在C ++中,我必须编写
private:
Foo foo;
public:
Foo getFoo() { return foo; }
C ++ 11中是否有这样的概念,可以让我对此有所了解?
Answers:
在C ++中,您可以编写自己的功能。这是使用未命名类的属性的示例实现。维基百科文章
struct Foo
{
class {
int value;
public:
int & operator = (const int &i) { return value = i; }
operator int () const { return value; }
} alpha;
class {
float value;
public:
float & operator = (const float &f) { return value = f; }
operator float () const { return value; }
} bravo;
};
您可以在适当的位置编写自己的getter和setters,如果希望拥有者类成员访问,可以扩展此示例代码。
_alpha
私有变量和alpha
参考。
sizeof(Foo)
。
C ++没有内置此功能,您可以定义一个模板来模拟属性功能:
template <typename T>
class Property {
public:
virtual ~Property() {} //C++11: use override and =default;
virtual T& operator= (const T& f) { return value = f; }
virtual const T& operator() () const { return value; }
virtual explicit operator const T& () const { return value; }
virtual T* operator->() { return &value; }
protected:
T value;
};
要定义属性:
Property<float> x;
要实现自定义getter / setter,只需继承:
class : public Property<float> {
virtual float & operator = (const float &f) { /*custom code*/ return value = f; }
virtual operator float const & () const { /*custom code*/ return value; }
} y;
要定义一个只读属性:
template <typename T>
class ReadOnlyProperty {
public:
virtual ~ReadOnlyProperty() {}
virtual operator T const & () const { return value; }
protected:
T value;
};
并在课堂Owner
上使用它:
class Owner {
public:
class : public ReadOnlyProperty<float> { friend class Owner; } x;
Owner() { x.value = 8; }
};
您可以在宏中定义一些上述内容,以使其更加简洁。
virtual
是可能大多数用例是不必要的,因为房产是不太可能使用多态。
C ++语言没有任何东西可以在所有平台和编译器上正常工作。
但是,如果您愿意破坏跨平台的兼容性并承诺使用特定的编译器,则可以使用这种语法,例如在Microsoft Visual C ++中,您可以
// declspec_property.cpp
struct S {
int i;
void putprop(int j) {
i = j;
}
int getprop() {
return i;
}
__declspec(property(get = getprop, put = putprop)) int the_prop;
};
int main() {
S s;
s.the_prop = 5;
return s.the_prop;
}
通过具有专用类型的成员并对其进行重写operator(type)
,operator=
可以在某种程度上模拟getter和setter 。这是否是一个好主意是另一个问题,我将在+1
Kerrek SB的回答中表达我的意见:)
friend
到字段所有者。
也许看看我在过去几个小时内组装的属性类:https : //codereview.stackexchange.com/questions/7786/c11-feedback-on-my-approach-to-c-like-class-properties
它允许您具有如下行为的属性:
CTestClass myClass = CTestClass();
myClass.AspectRatio = 1.4;
myClass.Left = 20;
myClass.Right = 80;
myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);
使用C ++ 11,您可以定义一个Property类模板并像这样使用它:
class Test{
public:
Property<int, Test> Number{this,&Test::setNumber,&Test::getNumber};
private:
int itsNumber;
void setNumber(int theNumber)
{ itsNumber = theNumber; }
int getNumber() const
{ return itsNumber; }
};
这里是Property类模板。
template<typename T, typename C>
class Property{
public:
using SetterType = void (C::*)(T);
using GetterType = T (C::*)() const;
Property(C* theObject, SetterType theSetter, GetterType theGetter)
:itsObject(theObject),
itsSetter(theSetter),
itsGetter(theGetter)
{ }
operator T() const
{ return (itsObject->*itsGetter)(); }
C& operator = (T theValue) {
(itsObject->*itsSetter)(theValue);
return *itsObject;
}
private:
C* const itsObject;
SetterType const itsSetter;
GetterType const itsGetter;
};
C::*
意思 我以前从未见过类似的东西吗?
C
。这类似于普通函数指针,但是要调用成员函数,您需要提供一个在其上调用函数的对象。这是通过itsObject->*itsSetter(theValue)
上面示例中的行实现的。有关此功能的详细说明,请参见此处。
正如许多其他人已经说过的那样,该语言没有内置支持。但是,如果您以Microsoft C ++编译器为目标,则可以利用此处介绍的Microsoft特定属性扩展。
这是链接页面中的示例:
// declspec_property.cpp
struct S {
int i;
void putprop(int j) {
i = j;
}
int getprop() {
return i;
}
__declspec(property(get = getprop, put = putprop)) int the_prop;
};
int main() {
S s;
s.the_prop = 5;
return s.the_prop;
}
不,C ++没有属性的概念。尽管定义和调用getThis()或setThat(value)可能很尴尬,但是您正在向这些方法的使用者声明可能会发生某些功能。另一方面,访问C ++中的字段会告诉使用者不会发生任何其他功能。属性使这一点变得不那么明显,因为乍一看属性访问似乎像一个字段一样反应,但实际上像一个方法一样反应。
顺便说一句,我正在一个.NET应用程序(一个非常著名的CMS)中尝试创建一个客户会员系统。由于他们为用户对象使用属性的方式,动作无法正常执行,这导致我的实现以奇怪的方式执行,包括无限递归。这是因为他们的用户对象在尝试访问诸如StreetAddress之类的简单内容时调用了数据访问层或某些全局缓存系统。他们的整个系统建立在所谓的滥用财产的基础上。如果他们使用方法而不是属性,我想我会更快地找出问题所在。如果他们使用了字段(或者至少使它们的属性表现得更像字段),我认为该系统将更易于扩展和维护。
[编辑]改变了我的想法。我度过了糟糕的一天,然后咆哮了一下。此清理工作应该更专业。
基于https://stackoverflow.com/a/23109533/404734,以下是带有公共获取器和私有设置器的版本:
struct Foo
{
class
{
int value;
int& operator= (const int& i) { return value = i; }
friend struct Foo;
public:
operator int() const { return value; }
} alpha;
};
这不完全是一个属性,但是它可以按照您想要的简单方式进行操作:
class Foo {
int x;
public:
const int& X;
Foo() : X(x) {
...
}
};
在这里,大X的行为类似于public int X { get; private set; }
C#语法。如果您想要完善的属性,我首先在这里实现它们。
X
,对新对象仍将指向旧对象的成员,因为它就像指针成员一样被复制。这本身是不好的,但是当删除旧对象时,您会在上面发生内存损坏。为了使这项工作有效,您还必须实现自己的副本构造函数,赋值运算符和move构造函数。
您可能知道这一点,但我只需执行以下操作:
class Person {
public:
std::string name() {
return _name;
}
void name(std::string value) {
_name = value;
}
private:
std::string _name;
};
这种方法很简单,不需要任何巧妙的技巧,就可以完成工作!
但是,问题是有些人不喜欢在下划线前给私有字段加上前缀,因此他们不能真正使用此方法,但是幸运的是,对于那些这样做的人,这确实很简单。:)
get和set前缀并没有使您的API更加清晰,但使它们更加冗长,而我认为它们没有添加有用的信息的原因是,当某人需要使用API时,如果该API有意义,那么她可能会意识到它的含义。没有前缀。
还有一件事,因为name
不是动词,所以很容易理解它们是属性。
最坏的情况是,如果API是一致的,并且用户没有意识到这name()
是访问者,并且name(value)
而是一个变种器,那么她只需在文档中查找一次即可了解该模式。
尽管我喜欢C#,但我认为C ++根本不需要属性!
foo(bar)
(而不是较慢的变量foo = bar
),那么您的mutators就很有意义,但是您的访问者与属性完全无关...
foo(bar)
代替foo=bar
它。void foo(Bar bar)
_foo
我从多个C ++来源收集了这些想法,并将其放入C ++中的getter / setter方法的漂亮但仍然非常简单的示例中:
class Canvas { public:
void resize() {
cout << "resize to " << width << " " << height << endl;
}
Canvas(int w, int h) : width(*this), height(*this) {
cout << "new canvas " << w << " " << h << endl;
width.value = w;
height.value = h;
}
class Width { public:
Canvas& canvas;
int value;
Width(Canvas& canvas): canvas(canvas) {}
int & operator = (const int &i) {
value = i;
canvas.resize();
return value;
}
operator int () const {
return value;
}
} width;
class Height { public:
Canvas& canvas;
int value;
Height(Canvas& canvas): canvas(canvas) {}
int & operator = (const int &i) {
value = i;
canvas.resize();
return value;
}
operator int () const {
return value;
}
} height;
};
int main() {
Canvas canvas(256, 256);
canvas.width = 128;
canvas.height = 64;
}
输出:
new canvas 256 256
resize to 128 256
resize to 128 64
您可以在此处在线测试:http : //codepad.org/zosxqjTX
有一组书面宏这里。这具有用于值类型,引用类型,只读类型,强类型和弱类型的方便的属性声明。
class MyClass {
// Use assign for value types.
NTPropertyAssign(int, StudentId)
public:
...
}