提问人:eje211 提问时间:10/31/2023 最后编辑:eje211 更新时间:10/31/2023 访问量:81
检查 C++ 引用 setter 值
Check C++ reference setter value
问:
我有一个关于 C++ 中备受关注的 C++ 引用 getter-setter 的问题。这是基本代码:
class Foo
{
X x_;
public:
X & x() { return x_; }
const X & x() const { return x_; }
}
我的问题是:如果我在设置值时需要检查该值怎么办?让我们想象一下,我正在设置坐标的纬度。如果我正在编写一个方法,它将如下所示:setLat
void MyClass::setLat(double lat)
{
if (lat < -90 || lat > 90)
{
throw std::range_error("The latitude must be between -90 and 90.");
}
_lat = lat;
}
我可以用以下方法做类似的事情吗:
double & MyClass::lat()
{
return _lat;
}
谢谢
当我看到这个问题顶部的代码是另一个 StackOverflow 问题的答案时,我想,“很酷。但后来,我最终意识到这里的人在说什么:它破坏了封装。此成员也可能是公开的。
由于我不经常使用 C++,所以我不太确定,想。
然而,@ted-lyngmo给出的答案确实很有道理。我认为最终只有一个 regluar getter 和一个 setter 可能是最好的。但我认为有可能在最新版本的 C++ 中模仿 C# 和 Python 的属性能力。看起来是这样。现在:这样做是个好主意吗?这个问题似乎仍然悬而未决。
答:
2赞
463035818_is_not_an_ai
10/31/2023
#1
返回非常量引用会中断封装。一旦调用者获得引用,他们就可以做任何他们喜欢的事情。您想要的行为可以通过返回代理来实现:
#include<iostream>
struct foo {
struct proxy {
proxy(int& _value) : _value(_value) {}
proxy& operator=(int value) {
if (value != 42) throw "bad value";
_value = value;
return *this;
}
operator int () { return _value; }
private:
int& _value;
};
proxy get() { return {x}; }
int get() const { return x; }
private:
int x = 0;
};
int main(){
foo f;
f.get() = 42;
std::cout << f.get();
}
但是,提供实际的 setter 要简单得多。foo::set(int)
评论
0赞
Red.Wave
10/31/2023
我会将访问器函数限定为 rvalue(),这样,如果用户代码按值存储它们,则会生成诊断。proxy
&&
1赞
HolyBlackCat
10/31/2023
我可能更喜欢带有参数的 setter。
4赞
Ted Lyngmo
10/31/2023
#2
一种选择是创建用户定义的纬度类型,并将所有验证放入其中:
class Latitude {
public:
Latitude() = default;
explicit Latitude(double lat) : m_latitude(lat) {
if (m_latitude < -90 || m_latitude > 90)
throw std::range_error("The latitude must be between -90 and 90.");
}
explicit operator double() const { return m_latitude; }
private:
double m_latitude = 0.;
};
这样一来,就可以使包含纬度的类保持整洁:
class MyClass {
public:
void setLat(Latitude lat) {
_lat = lat;
}
private:
Latitude _lat;
};
或
class MyClass {
public:
Latitude& latitude() {
return _lat;
}
private:
Latitude _lat;
};
评论
0赞
eje211
10/31/2023
这对我来说现在完全是理论上的,但也许可以用它来制作一个模板,并将检查代码作为 lambda 传递,以便有一个完全可重用的类。我并不是说这是一个好主意,但它可能是可能的。我主要是想弄清楚 C++ 是如何工作的。这是一个很好的例子。谢谢!
0赞
Ted Lyngmo
10/31/2023
@eje211很高兴它有帮助!别客气!是的,当您想使用不同的基础类型执行类似操作时,使用类模板是很好的。可以创建一个类模板,而不是普通的 s 或 s,该模板可以是具有特定有效间隔等的类似类型的基类。int
double
interval
Latitude
0赞
eje211
11/4/2023
我理解模板化。我用过 Java、C# 和 Scala。这个概念与这些语言中的泛型相同。模板原则上是相同的,但是,就像所有 C++ 一样,它们只是感觉非常不同。(而且我知道它们在引擎盖下根本不像在 JVM 上那样工作。泛型在运行时在 JVM 上实现,在编译时在 C# 和 C++ 的模板中实现。例如,根据我的经验,值类别(rvalue,lvalue,prvalue,xvalue,glvalue)至少在这种程度上只存在于C++中。
评论
X x() const { return x_; }
void set_x(X value) { x_ = std::move(value); }
Foo