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

时间:2012-08-29 09:18:08

标签: c++ inheritance class-members

我想知道在C ++中实现设计困境的最佳方法是什么......

我有一个类,其中包含另一个类的Base类型的成员变量,并且创建的真实对象创建为Derived Base

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

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

  1. 将成员变量创建为Base*,让我们管理它们以及它们使用的内存。这导致了经典的内存泄漏问题......当有人不再使用它时,有人会忘记删除它。
  2. 将成员变量创建为Base&,让我们祈祷,当它们超出范围时,它们不会消失。

5 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

@hansmaad是正确的,如果您在控制对象的生命周期方面遇到问题,您应该与创建或管理对象的人共享其所有权。
您有2个选择:
 1)boost::shared_ptrstd::tr1::shared_ptr
    您可以轻松地将此类用于任何类型Base而无需更改Base,但如果您在多线程环境中工作,则很难实现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
    您可以轻松地使其成为线程安全的,您可以将其作为原始指针或智能指针传递,因为它可以从原始指针构造,因为引用计数是在类中实现的,但您应该更改类的定义并添加引用计数机制< / p>

答案 2 :(得分:0)

你应该考虑所有权。你拥有那些物品?如果对此问题没有明确答案,则应使用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;
};

答案 3 :(得分:0)

我会选择指针,包括你的矢量(即vector<Base *>,而不是vector<Base>)和容器类,原因如下:

  • 如果将Derived对象存储在矢量中,则该矢量可能会被重新调整大小,从而导致所有对象移动到&#39;到记忆中的新位置。这将使所有未完成的指针和引用无效
  • 如果你的容器包含引用,你就不能像它包含指针那样容易地复制它,因为引用只能在定义时绑定(所以在构造函数中通过MyClass::MyClass(int &a) : memberA(a) {},如果内存服务)
  • 指针可以根据需要通过其他方式更改,例如set方法,如果没有信息,可以设置为null

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

答案 4 :(得分:0)

使用引用成员变量时要考虑的第一件事是你的类(不是Derived,将要有一个指针的数据成员或对Base的引用的类)是否需要值语义(这是另一种说法,“复制和分配正确”)。

如果是这样,那么引用成员变量或多或少都会立即解决,因为它们无法重新定位。有些奇怪的情况你无论如何都可以使用它们,但你可以假设你不会,并使用指针。

参考数据成员偶尔会对具有“实体语义”的类型有用(也就是说,它们根本不会分配,可能会也可能不会复制),但它们仍然不会给你带来很多好处。他们还可以引发您编写带有const Base&参数的构造函数并将其存储在引用数据成员[*]中的错误。

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

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

[*]这是一个错误,因为最终有人会意外地在初始化程序中使用临时对象,然后具有其引用数据成员的类的实例将比临时对象更长。因此,在返回后存储引用以供使用的内容不应采用const &参数,即使它们不修改对象也是如此。他们可以改为const *。在C ++ 11中,我认为如果还存在rvalue引用重载,它们可能没问题,以防止为临时选择const&重载,但这不是我已经尝试过的。