C++引用式Getter-Setter的值校验实现及类属性特性模仿探讨
问题:C++引用式Getter-Setter的封装问题与属性特性模拟
我在StackOverflow上看到一段C++引用式Getter-Setter代码:
class Foo { X x_; public: X & x() { return x_; } const X & x() const { return x_; } };
起初觉得这种写法很不错,但后来意识到它破坏了封装性,与直接将成员设为public并无区别。
我现在有两个问题:
- 如果需要像设置纬度时那样对值进行校验(如下列
setLat方法所示),能否通过返回引用的lat()方法实现类似的校验逻辑?
void MyClass::setLat(double lat) { if (lat < -90 || lat > 90) { throw std::range_error("The latitude must be between -90 and 90."); } _lat = lat; }
- 在新版C++中是否可以模仿C#和Python的类属性特性,以及这种做法是否合适?
回答
1. 直接返回成员引用无法实现赋值校验
直接返回_lat的引用(比如double& lat())的话,外部代码可以直接通过引用修改_lat,完全绕开你的校验逻辑,和把_lat设为public没有区别。
要实现返回“可赋值但带校验”的效果,你可以用代理类:创建一个轻量的代理对象,重载赋值运算符,在赋值时执行校验逻辑,然后让lat()方法返回这个代理对象而非直接返回成员引用。示例代码如下:
class MyClass { private: double _lat; // 代理类:负责纬度的赋值校验 class LatProxy { private: MyClass& parent; public: LatProxy(MyClass& p) : parent(p) {} // 重载赋值运算符,执行校验 LatProxy& operator=(double lat) { if (lat < -90 || lat > 90) { throw std::range_error("The latitude must be between -90 and 90."); } parent._lat = lat; return *this; } // 支持隐式转换为double,方便读取值 operator double() const { return parent._lat; } }; public: // 返回代理对象,而非直接返回成员引用 LatProxy lat() { return LatProxy(*this); } // 只读版本,返回const引用 const double& lat() const { return _lat; } };
使用时和直接用引用的写法几乎一致,但赋值会自动触发校验:
MyClass obj; obj.lat() = 45.0; // 正常赋值 obj.lat() = 100.0; // 抛出range_error double currentLat = obj.lat(); // 正常读取值
2. 新版C++可以模拟类属性特性,做法可行
C++没有像C#/Python那样原生的get/set属性语法,但通过代理类、运算符重载等方式,可以实现几乎一致的使用体验,这种做法是完全合适的,尤其适合需要对属性读写做自定义逻辑(比如校验、日志、通知)的场景。
除了上面的代理类写法,C++20及以后还可以结合[[nodiscard]]等特性优化细节,但核心思路都是用中间层封装读写逻辑。需要注意的是,代理类会带来极其微小的性能开销(几乎可以忽略,尤其是针对基本类型),但对于大多数业务场景来说完全在可接受范围内。
内容的提问来源于stack exchange,提问作者eje211
相关产品推荐
相关产品推荐

