C++ 实现 get set

原文:https://www.codeproject.com/Articles/118921/C-Properties

Introduction

This article discusses how to implement C#-like properties in Visual C++ 6 or above. This article assumes that the reader has enough knowledge in the C++ language to understand the code (though the code snippet will be thoroughly explained). It is recommended that the reader understands macros, classes, functions, and typedefs; however, this code is available for anyone to use.

Background

If you are a new C++ or C# programmer, then properties may be a new concept. Properties are simply a convenient way of defining a getter and a setter for a given member variable. A getter is a member function that returns the literal value of the member variable that the property represents. The setter sets the literal value of the member variable that the property represents, but does not return a value. Properties in C# typically look like the following:

private int _x;
public int x
{
	get
	{
		return _x;
	}
	set
	{
		_x = value;
	}
}  

Of course, get designates the getter and set designates the setter. Within the setter, a variable labeled valueexists that represents the rvalue of an assignment statement that involved your property. For example, in the following code snippet, the value variable would contain the value 50.

someObject.x = 50;  

When the property is assigned a value, as in the above example, the setter member function is called and passed the rvalue as an argument to a parameter labeled value. Of course, the simplicity of properties allow us to not worry about the definition of these member functions and hides the actual calling of the function from us. We simply declare the getters and setters using the get and set keywords in C#.

Properties in C++

Unfortunately, C++ does not natively support properties. Properties in C++ involve writing out each function for each setter and getter. For example, the following code implements the same property as above, but in C++.

private: 
 	int x; 
public:  
 	int getX()
 	{
  		return x;
  	}
   	void setX(int value) 
   	{
   		x = value;
        }    

In order to use the setter and getter, we simply call the corresponding member function.

someObject.setX(50);  
int x = someObject.getX();  

The above code is perfectly fine and many people prefer these setters and getters as opposed to the C#-styled ones; however, there also exist many people who want C#-styled properties in C++. Because of this, Visual C++ 6.0 and above decided to implement a property declaration attribute.

__declspec( property (put=setFunction, get=getFunction) ) data-type property-name; 

The above code allows us to tell Visual C++ that we want setFunction to be called every time property-name is assigned to. Conversely, we want getFunction to be called every time we need to get the value of property-name. Of course, we still have to define those member functions, but this brings us much closer to implementing C#-style properties.

(A small note to non-Visual C++ users: implementing these properties is possible; however, it requires a much higher level or work and this article does not address it.)

The Implementation

Of course our C++ implementation will not be the exact syntax as C# properties for obvious reasons, but nonetheless it is a close representation. Below is the only code you will need in order to implement properties in Visual C++.

#define PROPERTY(t,n)  __declspec( property 
	( put = property__set_##n, get = property__get_##n ) ) t n;\
	typedef t property__tmp_type_##n
#define READONLY_PROPERTY(t,n) __declspec( property (get = property__get_##n) ) t n;\
	typedef t property__tmp_type_##n
#define WRITEONLY_PROPERTY(t,n) __declspec( property (put = property__set_##n) ) t n;\
	typedef t property__tmp_type_##n
#define GET(n) property__tmp_type_##n property__get_##n() 
#define SET(n) void property__set_##n(const property__tmp_type_##n& value)   

At first sight, this code may look frightening, but fear not. The above code is extremely easy to use. To declare a property using these macros, we simply use one of the PROPERTY macros. Below is a sample implementation of the sample property used earlier.

private: 
	int _x; 
public:  
 	PROPERTY(int, x);
  	GET(x) 
 	{ 
 		return _x; 
	}
 	SET(x)
 	{
 		_x = value;
 	}   

And just as in C#, we can access our property like the following:

someObject.x = 50;
int x = someObject.x; 

When we assign a value to the x property, the rvalue (which is 50 in this example) is assigned to the variablevalue, which is accessible in the setter. The code within the setter is then executed. When we get the value of the property, the code within our getter is executed and a value is returned (in this case, the value of _x).

Read-Only, Write-Only, and Access Modifiers

The above code natively supports read-only, write-only, and access-modified properties. Each of the different types of properties have their own uses. For example, a read-only property should only be used if neither the parent class nor any object accessing the property does not need to write to the property. Conversely, write-only should only be used if neither the parent class nor any object accessing the property does not need to read from the property. In order to use read-only and write-only properties, we simply use the corresponding macro and only define the corresponding setter or getter. Below is an example of each.

 // read-only   
READONLY_PROPERTY(int, length);  
GET(length) 
{ 
	return strlen(someString); 
}  
// write-only (rare)
WRITEONLY_PROPERTY(int, size);  
SET(size) 
{ 
	_size = value;
}   

Typically, neither read-only nor write-only properties are used. It is much more common to change the access-modifier of a property. Changing the access modifier of a property simply means to make it publicprivate,protected, etc. It is common for read-only properties to be implemented by simply making the setter private. However, the getter and setter do not need to be declared with the same access-modifier. For example, the gettercan be public while the setter can be private. Likewise, the getter can be private and the setter can bepublic.

Full Example

screen2.png

When learning any new concept, it helps to see a fully working example.

// properties.h

#ifndef _PROPERTIES_H
#define _PROPERTIES_H

#define PROPERTY(t,n)  __declspec( property 
	( put = property__set_##n, get = property__get_##n ) ) t n;\
	typedef t property__tmp_type_##n
#define READONLY_PROPERTY(t,n) __declspec( property (get = property__get_##n) ) t n;\
	typedef t property__tmp_type_##n
#define WRITEONLY_PROPERTY(t,n) __declspec( property (put = property__set_##n) ) t n;\
	typedef t property__tmp_type_##n

#define GET(n) property__tmp_type_##n property__get_##n()
#define SET(n) void property__set_##n(const property__tmp_type_##n& value)

#endif /* _PROPERTIES_H */ 
// main.cpp
#include <iostream>

#include <math.h>

#include "properties.h"

class Vector2
{
public:
	float x;
	float y;

	READONLY_PROPERTY(float, Length);
	GET(Length) 
	{ 
		return sqrt((x*x + y*y));
	}
};

int main()
{
	Vector2 vec;
	vec.x = 1;
	vec.y = 1;
	std::cout << "Length of vector(" << vec.x << ", " << vec.y << ") = ";
	std::cout << vec.Length << "\n"; // <--- property, not a function call

	return 0;
}  

The above code works with any Visual C++ version above 6.0. It does not need any additional dependencies -- as the section title states, it is a full example.

How It Works

Now let us move on to the nitty-gritty. The way these properties actually work lie in the magic of C++ macros. Let us take our original C++ property example and see what our macros would actually generate.

private: 
	int _x; 
public:  
 	PROPERTY(int, x);
  	GET(x) 
 	{ 
 		return _x; 
	}
 	SET(x)
 	{
 		_x = value;
	}  

The above code is not what the compiler actually sees after preprocessing is complete. We must replace ourPROPERTYGET, and SET with the expanded form of the macro. The expanded version would look like the following:

private: 
	int _x; 
public:  
	__declspec(property(put = property__set_x, get = property__get_x)) int x;
	typedef int property__tmp_type_x;

	property__tmp_type_x property__get_x()
	{
		return _x;
	}

	void property__set_x(const property__tmp_type_x& value)
	{
		_x = value;
	}   

As you can see, our GET and SET macros expand to member functions, just like they do in C#. However, this is hidden from us when we use our macros so it appears more C#-like.

Conclusion

Many people seem to argue about whether it is better to use properties or simply use getter and setter member functions in C++. The simple fact is that both are using member functions to perform the getting and setting, and as such, neither have any runtime performance gain over the other -- they are exactly the same.

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中,可以使用类和成员函数来实现get、set、subscribe接口的设计。 下面是一个示例类的设计: ```c++ class Device { public: Device(int id); double getTemperature(); void setTemperature(double temperature); void subscribeTemperature(std::function<void(double)> callback); private: int id_; double temperature_; std::vector<std::function<void(double)>> callbacks_; }; Device::Device(int id) : id_(id), temperature_(0.0) {} double Device::getTemperature() { return temperature_; } void Device::setTemperature(double temperature) { temperature_ = temperature; for (auto &callback : callbacks_) { callback(temperature_); } } void Device::subscribeTemperature(std::function<void(double)> callback) { callbacks_.push_back(callback); } ``` 该类实现了一个Device设备的温度读取、修改和订阅功能。其中,getTemperature()函数用于获取当前温度值,setTemperature()函数用于修改温度值,并通知所有订阅者,subscribeTemperature()函数用于订阅温度值的变化,并注册回调函数。使用该类时,可以按照以下方式进行调用: ```c++ Device device(1); device.subscribeTemperature([](double temperature) { std::cout << "Temperature changed: " << temperature << std::endl; }); device.setTemperature(25.4); double temperature = device.getTemperature(); ``` 上述代码中,首先创建了一个Device对象,并注册了一个订阅温度变化的回调函数。然后,调用setTemperature()函数修改温度值,并会触发回调函数。最后,调用getTemperature()函数获取当前温度值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值