[英]Reasons for defining non-const 'get' member functions?
I'm working on learning C++ with Stroustrup's (Programming Principles & Practice Using C++) book. 我正在与Stroustrup的《使用C ++编程原理和实践》一书一起学习C ++。 In an exercise we define a simple struct:
在一个练习中,我们定义一个简单的结构:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
We're then asked to define a const and a non-const member function to get val
. 然后要求我们定义一个const和一个非const成员函数来获取
val
。
I was wondering: Is there any case where it makes sense to have non-const get
function that returns val
? 我想知道:在任何情况下,让非常量
get
函数返回val
有意义吗?
It seems much cleaner to me that we can't change the value in such situations indirectly. 在我看来,在这种情况下我们不能间接改变价值的做法更为清洁。 What might be use cases where you need a const and a non-const
get
function to return a member variable? 需要用const和非const
get
函数返回成员变量的用例可能是什么?
Non-const getters? 非常量获取器?
Getters and setters are merely convention. Getter和Setter只是约定。 Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
除了提供一个getter和setter之外,有时使用的惯用法是按照
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy: 这样,根据实例的恒定性,您可以获得引用或副本:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. 总体而言,这是否是一种好风格,尚有待商opinion。 There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
在某些情况下,它会引起混乱,而在其他情况下,此行为正是您所期望的(请参阅下文)。
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means). 无论如何,更重要的是根据类的用途和使用方式来设计类的接口,而不是盲目地遵循有关setter和getter的约定(例如,应给该方法起一个有意义的名称)它表达了它的作用,而不仅仅是“假装被封装,现在让我通过吸气剂访问所有内部组件”,这就是在各处使用吸气剂的实际含义。
Concrete example 具体例子
Consider that element access in containers is usually implemented like this. 考虑到容器中的元素访问通常是这样实现的。 As a toy example:
作为玩具示例:
struct my_array {
int operator[](unsigned i) const { return data[i]; }
int& operator[](unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data
could be public). 容器的工作不是向用户隐藏元素(即使
data
可能是公开的)。 You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const
and a non-const overload makes perfectly sense in this case. 您不希望根据要读取或写入元素的方式来使用不同的方法来访问元素,因此在这种情况下提供
const
和非const重载是很有意义的。
non-const reference from get vs encapsulation get与封装的非常量引用
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. 也许不是那么明显,但是是否提供getter和setter支持封装还是相反,这引起了一些争议。 While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions.
虽然一般而言,此问题在很大程度上是基于意见的,但对于返回非const引用的getter来说,与其说意见无关,不如说是。 They do break encapuslation.
他们确实破坏了包围。 Consider
考虑
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. 顾名思义,该类已损坏。 Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the
set
suggests). 客户可以简单地获取一个引用,而该类没有机会计算修改值的次数(如
set
建议的)。 Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. 一旦返回非常量引用,则有关封装的信息与公开成员几乎没有什么区别。 Hence, this is used only for cases where such behaviour is natural (eg container).
因此,这仅用于这种行为是自然的情况(例如容器)。
PS 聚苯乙烯
Note that your example returns a const T&
rather than a value. 请注意,您的示例返回
const T&
而不是值。 This is reasonable for template code, where you dont know how expensive a copy is, while for an int
you wont gain much by returning a const int&
instead of an int
. 这是合理的模板代码,你不知道副本有多贵,而对于
int
您不会通过返回获益良多const int&
,而不是一个int
。 For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&
. 为了清楚起见,我使用了非模板示例,尽管对于模板代码,您可能宁愿返回
const T&
。
First let me rephrase your question: 首先让我重新表述您的问题:
Why have a non-const getter for a member, rather than just making the member public?
为什么要为成员使用非常量获取方法,而不仅仅是使成员公开?
Several possible reasons reasons: 有几种可能的原因:
Who said the non-const getter needs to be just: 谁说非常量获取器必须是:
T& get() { return val; }
? ? it could well be something like:
可能是这样的:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
Some organizations enforce coding standards. 一些组织执行编码标准。 These coding standards are sometimes authored by people who are possibly overly-defensive.
这些编码标准有时是由可能过分防御的人编写的。 So, you might see something like:
因此,您可能会看到类似以下内容的内容:
Unless your class is a "plain old data" type , no data members may be public.
除非您的课程是“普通数据”类型 ,否则任何数据成员都不得公开。 You may use getter methods for such non-public members as necessary.
您可以根据需要为此类非公开成员使用getter方法。
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen. 然后,即使特定类只允许非常量访问是有意义的,也不会发生。
val
just isn't there? val
不在吗? You've given an example in which val
actually exists in an instance of the class. 您已经给出了一个示例,其中
val
实际上存在于类的实例中。 But actually - it doesn't have to! 但实际上-不必! The
get()
method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (eg storing or retrieving data in a database). get()
方法可以返回某种代理对象,该代理对象在赋值,变异等情况下执行一些计算(例如,在数据库中存储或检索数据)。
Now, reading items 1. or 3, above, you might ask "but my struct S
does have val
!" 现在,阅读上面的项目1.或3,您可能会问“但是我的
struct S
确实有val
!”。 or "by my get()
doesn't do anything interesting!" 或“通过我的
get()
不会做任何有趣的事!” - well, true, they don't; -好吧,是的,他们没有; but you might want to change this behavior in the future.
但是您将来可能希望更改此行为。 Without a
get()
, all of your class' users will need to change their code. 没有
get()
,您所有类的用户都将需要更改其代码。 With a get()
, you only need to make changes to the implementation of struct S
. 使用
get()
,只需更改struct S
的实现即可。
Now, I don't advocate for this kind of a design approach approach, but some programmers do. 现在,我不提倡这种设计方法,但是有些程序员支持。
get()
is callable by non const objects which are allowed to mutate, you can do: get()
被允许进行突变的非const对象调用,您可以执行以下操作:
S r(0);
r.get() = 1;
but if you make r
const as const S r(0)
, the line r.get() = 1
no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const
to at least to able to retrieve the value for const objects, doing so allows you do: 但是,如果将
r
const设为const S r(0)
,则行r.get() = 1
将不再编译,甚至无法检索该值,这就是为什么您至少需要const版本const T& get() const
为了能够检索const对象的值,这样做允许您执行以下操作:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, ie if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object. 的成员函数的const版本尽量与呼叫在制造的物体,也就是说,如果对象是是常量和成员函数不可改变返回一个参考,它可以通过返回反映呼叫者的常量性的常量性特性一致const引用,因此保留了对象的不变性。
It depends on the purpose of S
. 这取决于
S
的目的。 If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly. 如果是某种薄包装纸,则允许用户直接访问基础值可能是合适的。
One of the real-life examples is std::reference_wrapper
. 真实示例之一是
std::reference_wrapper
。
No. If a getter simply returns a non-const reference to a member, like this: 否。如果获取程序只是返回对成员的非常量引用,如下所示:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member
should be public instead, and the accessor is not needed. 然后,
m_member
应该改为public,并且不需要访问器。 There is absolutely no point making this member private, and then create an accessor, which gives all access to it. 绝对没有必要将此成员设为私有,然后创建一个访问器,该访问器提供对其的所有访问权限。
If you call getMember()
, you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member
, the enclosing class will know nothing about it. 如果调用
getMember()
,则可以将结果引用存储到指针/引用中,然后,可以使用m_member
进行任何m_member
,封闭的类对此一无所知。 It's the same, as if m_member
had been public. 就像
m_member
已经公开一样。
Note, that if getMember()
does some additional task (for example, it doesn't just simply return m_member
, but lazily constructs it), then getMember()
could be useful: 请注意,如果
getMember()
执行一些其他任务(例如,它不仅返回m_member
,而是懒洋洋地构造了它),那么getMember()
可能会有用:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.