简体   繁体   English

定义非const'get'成员函数的原因?

[英]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: 有几种可能的原因:

1. Easy to instrument 1.易于安装

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.

2. Cultural convention / "the boss said so" 2.文化习俗/“老板这么说”

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. 然后,即使特定类只允许非常量访问是有意义的,也不会发生。

3. Maybe val just isn't there? 3.也许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()方法可以返回某种代理对象,该代理对象在赋值,变异等情况下执行一些计算(例如,在数据库中存储或检索数据)。

4. Allows changing class internals later without changing user code 4.允许以后更改类内部而不更改用户代码

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM