2

我想知道在 C++ 中完成设计困境的最佳方法可能是什么......

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

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

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

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

5 回答 5

1

@hansmaad 是对的,如果您在控制对象的生命周期时遇到问题,您应该与创建或管理它的人共享其所有权。
您有 2 个选项:
1)boost::shared_ptr或者std::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
您可以轻松地使其线程安全,并且可以将其作为原始指针或智能指针传递,因为它可以从原始指针构造,因为引用计数是在类中实现的,但是您应该更改类的定义并添加引用计数机制

于 2012-08-29T09:45:14.747 回答
1

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

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

于 2012-08-29T09:27:58.023 回答
0

你应该考虑所有权。谁拥有这些对象?如果这个问题没有明确的答案,您应该使用std::shared_ptr<Base>(共享所有权)。如果有一个类拥有该对象,而所有其他类只使用它们,则可以使用 a 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;
};
于 2012-08-29T09:25:36.363 回答
0

我会为您的向量(即 , vector<Base *>not vector<Base>)和您的容器类使用指针,原因如下:

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

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

于 2012-08-29T09:31:05.150 回答
0

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

如果是这样,那么引用成员变量或多或少马上就不可能了,因为它们不能被重新安置。在一些奇怪的情况下,无论如何你都可以使用它们,但你不妨假设你不会,并使用指针。

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

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

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

[*] 这是一个错误,因为最终有人会不小心在初始化程序中使用临时对象,然后您的类的实例及其引用数据成员将比临时对象寿命更长。出于这个原因,在返回后存储引用以供使用的东西不应该带const &参数,即使它们不修改对象。他们可以const *取而代之。在 C++11 中,如果还有一个右值引用重载,我想它们可能没问题,以防止const&为临时选择重载,但这不是我尝试过的东西。

于 2012-08-29T09:35:31.540 回答