C++:作为引用或指针的基类型成员变量
C++ : Base type member variables as References, or Pointer
我想知道在C++中解决设计难题的最佳方法是什么。。。
我有一个类,它包含另一个类的Base
类型的成员变量,并且创建的实际对象被创建为Base
的Derived
。
类不需要修改这些变量,它只是在使用它们。其他人正在创建这些变量。这些Derived
类还需要转到我的类中的容器(std::vector、QList等)类,因此它们应该执行正确的复制构造和赋值。
所以,我想知道什么可能是最好的:
- 将成员变量创建为
Base*
,并让我们管理它们和它们使用的内存。这导致了经典的内存泄漏问题。。。有些人只是在不再使用对象时忘记删除它 - 将成员变量创建为
Base&
,让我们祈祷它们在某个地方超出范围时不会消失
拥有引用成员变量总是一个糟糕的选择,因为编译器生成的赋值和移动赋值做了错误的事情,或者不是人们所期望的。
坚持使用成员变量的指针或智能指针。
@hansmaad是正确的,如果您在控制对象的生命周期方面有问题,您应该与创建或管理对象的人共享其所有权。
您有两个选项:
1) boost::shared_ptr
或std::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;
};
- 是否可以初始化不可复制类型的成员变量(或基类)
- 当基类是依赖类型时,这是一个缺陷吗
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 为什么允许dynamic_cast到非唯一的基类类型?
- 基类类型向量中的派生结构
- C++ 模板:重载时找不到基类类型参数方法
- 如果类包含基类类型的成员作为第一个元素,后跟其他成员,编译器是否可以优化空基?
- 如何在不转换回基类类型的情况下返回派生类型的对象?
- 声明基类类型的指针,但随后通过指向子类来实例化它.这是良好的编程实践吗?
- 获取继承基类类型的元组
- 如何从指针调用派生类函数到基类类型 (C++)
- 如何检查对象是否是基类类型
- 来自存储在带有基类类型的地图中的派生类的调用函数
- 采用基类类型参数的虚拟函数
- 用派生类填充基类类型的2D矢量
- 基类类型的指针忘记了原始对象类型
- 在c++中推回基类类型2d vector中的子类对象
- 子类对象,用作定义为c++基类类型的参数
- 从基类类型的向量访问派生类方法
- 从基类类型的指针数组调用派生类的方法.(c++)