C++:作为引用或指针的基类型成员变量

C++ : Base type member variables as References, or Pointer

本文关键字:基类 类型 成员 变量 指针 引用 C++      更新时间:2023-10-16

我想知道在C++中解决设计难题的最佳方法是什么。。。

我有一个类,它包含另一个类的Base类型的成员变量,并且创建的实际对象被创建为BaseDerived

类不需要修改这些变量,它只是在使用它们。其他人正在创建这些变量。这些Derived类还需要转到我的类中的容器(std::vector、QList等)类,因此它们应该执行正确的复制构造和赋值。

所以,我想知道什么可能是最好的:

  1. 将成员变量创建为Base*,并让我们管理它们和它们使用的内存。这导致了经典的内存泄漏问题。。。有些人只是在不再使用对象时忘记删除它
  2. 将成员变量创建为Base&,让我们祈祷它们在某个地方超出范围时不会消失

拥有引用成员变量总是一个糟糕的选择,因为编译器生成的赋值和移动赋值做了错误的事情,或者不是人们所期望的。

坚持使用成员变量的指针或智能指针。

@hansmaad是正确的,如果您在控制对象的生命周期方面有问题,您应该与创建或管理对象的人共享其所有权。
您有两个选项:
1) boost::shared_ptrstd::tr1::shared_ptr
您可以很容易地将此类用于任何类型的CCD_ 9,而无需更改CCD_,但是,如果您在多线程环境中工作,则很难实现shared_ptr的线程安全,并且不要忘记,如果您使用其中一个类将对象创建为共享对象,则不应直接管理对象的生存时间,并且从原始指针创建新的共享对象是不合法的,并且您应该始终复制构造共享对象。例如:

boost::shared_ptr<Base> sharedObject( new Drived() );
boost::shared_ptr<Base> validCopy( sharedObject );    // Ok share ownership
Base* p = sharedObject.get();
boost::shared_ptr<Base> invalidCopy( p );    // Error, can't create new shared_ptr from raw pointer

2) boost::intrusive_ptr
你可以很容易地使它成为线程安全的,你可以将它作为原始指针或智能指针传递,因为它可以从原始指针构建,因为引用计数是在类中实现的,但你应该改变类的定义,并添加引用计数机制

我会使用指针,对于向量(即vector<Base *>,而不是vector<Base>)和容器类,原因如下:

  • 如果将派生对象存储在向量中,则该向量可能会被重新调整大小,从而导致所有对象"移动"到内存中的新位置。这将使所有未完成的指针和引用无效
  • 如果容器包含引用,则无法像包含指针那样轻松地复制它,因为引用只能在定义时绑定(因此在构造函数中通过MyClass::MyClass(int &a) : memberA(a) {},如果内存可用)
  • 指针可以通过其他方式更改,如根据需要设置方法,并且可以在缺少信息的情况下设置为null

就所有权而言,jrok是第一个说:shared_ptr<>是你的朋友。不要重新发明轮子,只需使用标准库来为您简化事情。在这种情况下,您唯一需要担心的是循环指针(即,对象指向自身,因此总是有一个有效的指针)。

对于引用成员变量,首先要考虑的是您的类(而不是Derived,该类将具有指向Base的指针或引用的数据成员)是否需要值语义(这是另一种说法,即"正确地复制和分配")。

如果是这样,那么引用成员变量或多或少是不可能直接出现的,因为它们无法重新定位。在一些奇怪的情况下,你无论如何都可以使用它们,但你也可以假设你不会,并使用指针。

引用数据成员偶尔对具有"实体语义"的类型有用(也就是说,它们根本不赋值,可能复制,也可能不复制),但它们仍然不会给你带来很大好处。它们还可能诱使您编写一个接受const Base&参数的构造函数,并将其存储在引用数据成员[*]中。

谁拥有对象(并负责释放它)完全独立于您使用的是指针还是引用。可能有一个通用的约定,不使用对您拥有的东西的引用(应该有一个约定,不为您拥有的事物使用原始指针,您应该选择或编写一个合适的智能指针。智能指针类可以保存原始指针)。但这只是惯例。你不应该假设你管理内存,当且仅当你有一个指针。

摘要:使用指针,然后单独决定如何管理内存。

[*]这是一个错误,因为最终有人会意外地在初始值设定项中使用临时对象,然后类的实例及其引用数据成员的寿命将超过临时对象。因此,存储引用以供返回后使用的东西不应该使用const &参数,即使它们不修改对象。他们可以服用const *。在C++11中,我想如果还有一个右值引用重载,以防止const&重载被选为临时重载,它们可能是可以的,但我还没有尝试过。

您应该考虑所有权。谁拥有这些物品?如果这个问题没有明确的答案,您应该使用std::shared_ptr<Base>(共享所有权)。如果有一个类拥有这些对象,而所有其他类都只使用它们,那么可以使用std::unique_ptr<Base>,一个像boost::ptr_vector这样的指针容器,或者如果没有多态性,那么拥有类的类就是具体的实例。在所有其他类中,您可以使用指向该对象的纯指针(首选为类成员)或引用(如果不允许null,则首选为参数)。

案例1-共有

class IWorkOnBaseObjects
{
    std::vector<std::shared_ptr<Base>> mySubset;
};
class MeToo
{
    std::shared_ptr<Base> iNeedThisOne;
};

案例2

class HomeOfBaseObjects
{
    std::vector<std::uniqe_ptr<Base>> baseObjects;
};
class IWorkOnBaseObjects
{
    std::vector<Base*> mySubset;
};

案例3

class A : public Base{};
class B : public Base{};
class HomeOfAObjects
{
    std::vector<A> aObjects;
};
class HomeOfBObjects
{
    std::vector<B> bObjects;
};
class INeedABaseObject
{
    Base* thisOne;
};