引用成员variables作为类成员
在我工作的地方,我看到这种风格被广泛使用:
#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; }
有没有一个名字来形容这个成语? 我假设它是为了防止复制大型复杂对象的可能的大开销?
这是一个很好的做法吗? 这种方法有什么缺陷吗?
有没有一个名字来形容这个成语?
在UML中称为聚合。 它不同于构图,因为成员对象不属于引用类。 在C ++中,可以通过引用或指针以两种不同的方式实现聚合。
我假设它是为了防止复制大型复杂对象的可能的大开销?
不,这将是一个非常糟糕的理由使用这个。 聚合的主要原因是被包含的对象不属于包含对象,因此它们的生命周期是不受限制的。 特别是引用的对象的生命周期必须超过引用的。 它可能早就build立了,可能会在集装箱的寿命结束之后生存。 除此之外,引用对象的状态不受类的控制,但可以在外部进行更改。 如果引用不是const
,那么这个类可以改变一个对象的状态。
这是一个很好的做法吗? 这种方法有什么缺陷吗?
这是一个devise工具。 在某些情况下,这将是一个好主意,有些则不会。 最常见的错误是持有引用的对象的生命周期不得超过引用对象的生命周期。 如果封闭对象在被引用的对象被销毁之后使用了引用,你将会有未定义的行为。 总的来说,select合成更好,但是如果你需要的话,它和其他工具一样好。
它通过构造器注入被称为dependency injection :类A
获取依赖作为其构造函数的参数,并将对依赖类的引用保存为私有variables。
有一个有趣的维基百科介绍。
对于const的正确性,我会写:
using T = int; class A { public: A(const T &thing) : m_thing(thing) {} // ... private: const T &m_thing; };
但是这个类的一个问题是它接受对临时对象的引用:
T t; A a1{t}; // this is ok, but... A a2{T()}; // ... this is BAD.
最好添加(至less需要C ++ 11):
class A { public: A(const T &thing) : m_thing(thing) {} A(const T &&) = delete; // prevents rvalue binding // ... private: const T &m_thing; };
无论如何,如果你改变构造函数:
class A { public: A(const T *thing) : m_thing(*thing) { assert(thing); } // ... private: const T &m_thing; };
这是非常有保证的, 你将不会有一个临时指针 。
另外,由于构造函数需要一个指针,因此A
用户需要注意他们传递的对象的生命周期。
有些相关的主题是:
- 我应该更喜欢成员数据中的指针或引用吗?
- 使用引用作为类成员的依赖关系
- GotW#88
- 禁止通过构造函数将r值绑定到成员const引用
有没有一个名字来形容这个成语?
这个用法没有名字,简称为“引用为类成员” 。
我假设它是为了防止复制大型复杂对象的可能的大开销?
是,也是您想要将一个对象的生命周期与另一个对象相关联的场景。
这是一个很好的做法吗? 这种方法有什么缺陷吗?
取决于您的使用情况。 使用任何语言function就像“select课程马” 。 需要注意的是,每个( 几乎所有 )语言function都存在,因为它在某些情况下很有用。
使用引用作为类成员时,需要注意以下几点:
- 您需要确保引用的对象保证存在,直到您的类对象存在。
- 您需要在构造函数成员初始值设定项列表中初始化成员。 你不能有一个懒惰的初始化 ,这在指针成员的情况下是可能的。
- 编译器不会生成复制赋值
operator=()
,您将不得不自己提供一个。 在这种情况下确定你的运营商将采取什么行动是很麻烦的。 所以基本上你的课变得不可分配 。 - 引用不能为
NULL
或引用任何其他对象。 如果您需要重新设置,那么在指针的情况下不可能使用引用。
对于大多数实际的目的(除非你真的担心由于成员的大小而导致的高内存使用),只要有一个成员实例,而不是指针或引用成员就足够了。 这样可以节省大量的引用/指针成员带来的其他问题,而不必担心额外的内存使用。
如果您必须使用指针,请确保使用智能指针而不是原始指针。 这将使你的生活更容易与指针。
会员参考通常被认为是不好的。 与成员指针相比,他们生活艰难。 但这不是特别的不恰当,也不是一些特殊的成语或事物。 这只是别名。
C ++提供了一个很好的机制来通过类/结构体来pipe理一个对象的生存期。 这是C ++比其他语言最好的特性之一。
当你通过ref或pointer暴露成员variables时,它原则上违反了封装。 这个习惯用法使得这个阶级的消费者能够改变A的一个对象的状态而没有它(A)对它有任何的知识或控制。 它也使得消费者能够在A的对象的生命周期之外保持对A的内部状态的引用/指针。这是不好的devise。 相反,类可以被重构为持有ref /指向共享对象(不拥有它),这些可以使用构造函数(命令寿命规则)设置。 共享对象的类可能被devise为支持multithreading/并发(视情况而定)。