C++ 将成员变量作为类成员引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12387239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:14:24  来源:igfitidea点击:

Reference member variables as class members

c++reference

提问by Angus Comber

In my place of work I see this style used extensively:-

在我的工作场所,我看到这种风格被广泛使用:-

#include <iostream>

using namespace std;

class A
{
public:
   A(int& thing) : m_thing(thing) {}
   void printit() { cout << m_thing << endl; }

protected:
   const int& m_thing; //usually would be more complex object
};


int main(int argc, char* argv[])
{
   int myint = 5;
   A myA(myint);
   myA.printit();
   return 0;
}

Is there a name to describe this idiom? I am assuming it is to prevent the possibly large overhead of copying a big complex object?

有没有名字可以描述这个成语?我假设它是为了防止复制大型复杂对象可能产生的大量开销?

Is this generally good practice? Are there any pitfalls to this approach?

这通常是好的做法吗?这种方法有什么缺陷吗?

回答by David Rodríguez - dribeas

Is there a name to describe this idiom?

有没有名字可以描述这个成语?

In UML it is called aggregation. It differs from composition in that the member object is not ownedby the referring class. In C++ you can implement aggregation in two different ways, through references or pointers.

在 UML 中,它被称为聚合。它与组合的不同之处在于成员对象不引用类所有。在 C++ 中,您可以通过引用或指针以两种不同的方式实现聚合。

I am assuming it is to prevent the possibly large overhead of copying a big complex object?

我假设它是为了防止复制大型复杂对象可能产生的大量开销?

No, that would be a really bad reason to use this. The main reason for aggregation is that the contained object is not owned by the containing object and thus their lifetimes are not bound. In particular the referenced object lifetime must outlive the referring one. It might have been created much earlier and might live beyond the end of the lifetime of the container. Besides that, the state of the referenced object is not controlled by the class, but can change externally. If the reference is not const, then the class can change the state of an object that lives outside of it.

不,这将是使用它的一个非常糟糕的理由。聚合的主要原因是包含对象不属于包含对象,因此它们的生命周期不受限制。特别是被引用的对象生命周期必须比引用的生命周期更长。它可能已经创建得更早,并且可能会在容器的生命周期结束后继续存在。除此之外,被引用对象的状态不受类控制,但可以从外部改变。如果引用不是const,则该类可以更改位于它之外的对象的状态。

Is this generally good practice? Are there any pitfalls to this approach?

这通常是好的做法吗?这种方法有什么缺陷吗?

It is a design tool. In some cases it will be a good idea, in some it won't. The most common pitfall is that the lifetime of the object holding the reference must never exceed the lifetime of the referenced object. If the enclosing object uses the reference afterthe referenced object was destroyed, you will have undefined behavior. In general it is better to prefer composition to aggregation, but if you need it, it is as good a tool as any other.

它是一种设计工具。在某些情况下,这将是一个好主意,在某些情况下则不是。最常见的陷阱是持有引用的对象的生命周期绝不能超过被引用对象的生命周期。如果封闭对象被引用的对象被销毁使用引用,您将有未定义的行为。一般来说,最好选择组合而不是聚合,但如果您需要它,它与其他任何工具一样好。

回答by manlio

It's called dependency injection via constructor injection: class Agets the dependency as an argument to its constructor and saves the reference to dependent class as a private variable.

它被称为通过构造函数注入A的依赖注入:类获取依赖作为其构造函数的参数,并将对依赖类的引用保存为私有变量。

There's an interesting introduction on wikipedia.

维基百科上有一个有趣的介绍。

For const-correctnessI'd write:

对于常量正确性,我会写:

using T = int;

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  // ...

private:
   const T &m_thing;
};

but a problem with this class is that it accepts references to temporary objects:

但是这个类的一个问题是它接受对临时对象的引用:

T t;
A a1{t};    // this is ok, but...

A a2{T()};  // ... this is BAD.

It's better to add (requires C++11 at least):

最好添加(至少需要 C++11):

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  A(const T &&) = delete;  // prevents rvalue binding
  // ...

private:
  const T &m_thing;
};


Anyway if you change the constructor:

无论如何,如果您更改构造函数:

class A
{
public:
  A(const T *thing) : m_thing(*thing) { assert(thing); }
  // ...

private:
   const T &m_thing;
};

it's pretty much guaranteed that you won't have a pointer to a temporary.

class A
{
public:
  A(const T *thing) : m_thing(*thing) { assert(thing); }
  // ...

private:
   const T &m_thing;
};

几乎可以保证您不会有指向临时.

Also, since the constructor takes a pointer, it's clearer to users of Athat they need to pay attention to the lifetime of the object they pass.

此外,由于构造函数接受一个指针,因此用户更清楚A他们需要注意他们传递的对象的生命周期。



Somewhat related topics are:

一些相关的主题是:

回答by Alok Save

Is there a name to describe this idiom?

有没有名字可以描述这个成语?

There is no name for this usage, it is simply known as "Reference as class member".

这种用法没有名称,简称为“作为类成员引用”

I am assuming it is to prevent the possibly large overhead of copying a big complex object?

我假设它是为了防止复制大型复杂对象可能产生的大量开销?

Yes and also scenarios where you want to associate the lifetime of one object with another object.

是的,还有您希望将一个对象的生命周期与另一个对象相关联的场景。

Is this generally good practice? Are there any pitfalls to this approach?

这通常是好的做法吗?这种方法有什么缺陷吗?

Depends on your usage. Using any language feature is like "choosing horses for courses". It is important to note that every (almost all) language feature exists because it is useful in some scenario.
There are a few important points to note when using references as class members:

取决于你的使用情况。使用任何语言功能就像“为课程选马”。需要注意的是,每个(几乎所有)语言特性都存在,因为它在某些情况下很有用。
使用引用作为类成员时,有几点需要注意:

  • You need to ensure that the referred object is guaranteed to exist till your class object exists.
  • You need to initialize the member in the constructor member initializer list. You cannot have a lazy initialization, which could be possible in case of pointer member.
  • The compiler will not generate the copy assignment operator=()and you will have to provide one yourself. It is cumbersome to determine what action your =operator shall take in such a case. So basically your class becomes non-assignable.
  • References cannot be NULLor made to refer any other object. If you need reseating, then it is not possible with a reference as in case of a pointer.
  • 您需要确保引用的对象保证存在,直到您的类对象存在。
  • 您需要在构造函数成员初始值设定项列表中初始化该成员。你不能有一个延迟初始化,这在指针成员的情况下是可能的。
  • 编译器不会生成副本分配operator=(),您必须自己提供。确定您的=操作员在这种情况下应采取什么行动是很麻烦的。所以基本上你的班级变成了不可分配的
  • 不能引用NULL或引用任何其他对象。如果您需要重新安装,则不可能像指针一样使用引用。

For most practical purposes (unless you are really concerned of high memory usage due to member size) just having a member instance, instead of pointer or reference member should suffice. This saves you a whole lot of worrying about other problems which reference/pointer members bring along though at expense of extra memory usage.

对于大多数实际目的(除非您真的担心由于成员大小而导致的高内存使用),只需拥有一个成员实例,而不是指针或引用成员就足够了。这让您不必担心引用/指针成员带来的其他问题,但会增加额外的内存使用量。

If you must use a pointer, make sure you use a smart pointer instead of a raw pointer. That would make your life much easier with pointers.

如果必须使用指针,请确保使用智能指针而不是原始指针。有了指针,这将使您的生活变得更加轻松。

回答by Indy9000

C++ provides a good mechanism to manage the life time of an object though class/struct constructs. This is one of the best features of C++ over other languages.

C++ 提供了一种很好的机制来通过类/结构构造来管理对象的生命周期。这是 C++ 优于其他语言的最佳特性之一。

When you have member variables exposed through ref or pointer it violates the encapsulation in principle. This idiom enables the consumer of the class to change the state of an object of A without it(A) having any knowledge or control of it. It also enables the consumer to hold on to a ref/pointer to A's internal state, beyond the life time of the object of A. This is bad design. Instead the class could be refactored to hold a ref/pointer to the shared object (not own it) and these could be set using the constructor (Mandate the life time rules). The shared object's class may be designed to support multithreading/concurrency as the case may apply.

当您通过 ref 或指针公开成员变量时,它原则上违反了封装。这个习语使类的使用者能够在它(A)不知道或控制它的情况下改变 A 的对象的状态。它还使消费者能够在 A 的对象的生命周期之外保留指向 A 内部状态的引用/指针。这是糟糕的设计。相反,可以重构该类以保存指向共享对象(不拥有它)的引用/指针,并且这些可以使用构造函数(强制生命时间规则)进行设置。共享对象的类可以设计为支持多线程/并发,视情​​况而定。

回答by Puppy

Member references are usually considered bad. They make life hard compared to member pointers. But it's not particularly unsual, nor is it some special named idiom or thing. It's just aliasing.

成员引用通常被认为是不好的。与成员指针相比,它们让生活变得艰难。但它并不是特别不寻常,也不是什么特别命名的成语或事物。这只是别名。