C + + 11有 C # 风格的属性吗?

在 C # 中,使用 getter 和 setter 的字段有一个很好的语法 Sugar。此外,我喜欢自动实现的属性,它允许我编写

public Foo foo { get; private set; }

在 C + + 中我必须写

private:
Foo foo;
public:
Foo getFoo() { return foo; }

在 C + + 11中是否有这样的概念,允许我在这上面使用一些语法糖?

83532 次浏览

You can emulate getter and setter to some extent by having a member of dedicated type and overriding operator(type) and operator= for it. Whether it's a good idea is another question and I'm going to +1 Kerrek SB's answer to express my opinion thereon :)

Does you class really need to enforce some invariant or is it just a logical grouping of member elements? If it is the latter you should consider making the thing a struct and accessing the members directly.

Maybe have a look at the property class I have assembled during the last hours: https://codereview.stackexchange.com/questions/7786/c11-feedback-on-my-approach-to-c-like-class-properties

It allows you to have properties behaving like this:

CTestClass myClass = CTestClass();


myClass.AspectRatio = 1.4;
myClass.Left = 20;
myClass.Right = 80;
myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);

No, C++ has no concept of properties. Though it can be awkward to define and call getThis() or setThat(value), you are making a statement to the consumer of those methods that some functionality may occur. Accessing fields in C++, on the other hand, tells the consumer that no additional or unexpected functionality will occur. Properties would make this less obvious as property access at first glance appears to react like a field, but in fact reacts like a method.

As an aside, I was working in a .NET application (a very well known CMS) attempting to create a customer membership system. Due to the way they had used properties for their user objects, actions were firing off that I hadn't anticipated, causing my implementations to execute in bizarre ways including infinite recursion. This was because their user objects made calls to the data access layer or some global caching system when attempting to access simple things like StreetAddress. Their entire system was founded on what I would call an abuse of properties. Had they have used methods instead of properties, I think I would have figured out what was going wrong much more quickly. Had they have used fields (or at least made their properties behave more like fields), I think the system would have been easier to extend and maintain.

[Edit] Changed my thoughts. I'd had a bad day and went a bit on a rant. This cleanup should be more professional.

In C++ you can write your own features. Here is an example implementation of properties using unnamed classes. Wikipedia article

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;
};

You can write your own getters & setters in place and if you want holder class member access you can extend this example code.

Based on https://stackoverflow.com/a/23109533/404734 here is a version with a public getter and a private setter:

struct Foo
{
class
{
int value;
int& operator= (const int& i) { return value = i; }
friend struct Foo;
public:
operator int() const { return value; }
} alpha;
};

This is not exactly a property, but it does what you want the simple way:

class Foo {
int x;
public:
const int& X;
Foo() : X(x) {
...
}
};

Here the big X behaves like public int X { get; private set; } in C# syntax. If you want full-blown properties, I made a first shot to implement them here.

C++ doesn't have this built in, you can define a template to mimic properties functionality:

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;
};

To define a property:

Property<float> x;

To implement a custom getter/setter just inherit:

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;

To define a read-only property:

template <typename T>
class ReadOnlyProperty {
public:
virtual ~ReadOnlyProperty() {}
virtual operator T const & () const { return value; }
protected:
T value;
};

And to use it in class Owner:

class Owner {
public:
class : public ReadOnlyProperty<float> { friend class Owner; } x;
Owner() { x.value = 8; }
};

You could define some of the above in macros to make it more concise.

As many other have already said, there's no built-in support in the language. However, if you are targeting the Microsoft C++ compiler you can take advantage of the Microsoft-specific extension for properties which is documented here.

This is the example from the linked page:

// 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;
}

You probably know that but I would simply do the following:

class Person {
public:
std::string name() {
return _name;
}
void name(std::string value) {
_name = value;
}
private:
std::string _name;
};

This approach is simple, uses no clever tricks and it gets the job done!

The issue though is that some people don't like to prefix their private fields with an underscore and so they can't really use this approach, but fortunately for these who do, it's really straightforward. :)

The get and set prefixes doesn't add clarity to your API but making them more verbose and the reason I don't think they add useful information is because when someone needs to use an API if the API makes sense she will probably realize what it does without the prefixes.

One more thing, it's easy to grasp that these are properties because name isn't a verb.

Worst case scenario, if the APIs are consistent and the person didn't realize that name() is an accessor and name(value) is a mutator then she will only have to look it up once in the documentation to understand the pattern.

As much as I love C# I don't think C++ needs properties at all!

With C++11 you can define a Property class template and use it like this:

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; }
};

And here ist the Property class template.

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;
};

Nope.. But you should consider if it`s just get : set function and no additional task preformed inside get:set methods just make it public.

There is nothing in the C++ language that will work across all platforms and compilers.

But if you're willing to break cross-platform compatibility and commit to a specific compiler you may be able to use such syntax, for example in Microsoft Visual C++ you can do

// 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;
}

I collected the ideas from multiple C++ sources and put it into a nice, still quite simple example for getters/setters in C++:

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;
}

Output:

new canvas 256 256
resize to 128 256
resize to 128 64

You can test it online here: http://codepad.org/zosxqjTX

There are a set of macros written Here. THis has convinient property declarations for value types, reference types, read only types, strong and weak types.

class MyClass {


// Use assign for value types.
NTPropertyAssign(int, StudentId)


public:
...


}

Here's another implementation of properties in c++ (std=c++17)

propertycpp@github

It provides a example which shows how to create properties, either publicly available as readonly where build-time exception is handled when attempting to write on property; example that shows "default" property with read&write access, example of dynamic getter (alters actual value), and dynamic readonly.

Initializing a property is far from nice...