我应该通过引用、值或ptr来存储一个完全封装的成员吗



所以我有一个标准的C++设置,其中有一个存储另一个对象的对象。存储的对象是完全拥有的,它从未泄漏到外部。该成员是非常数。

class Container
{
private:
    Contained item;
}

据我所知,当我的Container被实例化时,默认构造函数将在item成员上调用,我不必在初始化器列表中管理它。此外,当我的对象被破坏时,item上的dtor将被自动调用,我是否正确理解这一点?

另一种选择是通过参考来存储

class Container
{
private:
    Contained& item;
public:
    Container() : Contained()
    {
    }
}

在这种情况下,我不知道是否应该在dtor中delete它。

另一种选择是通过ptr 存储

class Container
{
private:
    Contained* item;
public:
    Container()
    {
        item = new Contained();
    }
    ~Container()
    {
        delete item;
    }
}

知道我的item从未返回给调用方,也从未渗入外部API,也从未被重新分配,那么最好的方法是什么?正如我所提到的,item成员不是常量(它将是一个自调整大小的数据结构)。

最简单的方法是存储对象本身。我想说,将引用用于此目的是令人困惑的。使用指针的一个优点是,您可以避免在头文件中定义Containerd类型,而是可以转发声明Containerd,并将所有详细信息保留在.cpp文件中。

在我看来,第一种方法是最好的方法(似乎不需要延迟构建对象)。第二种方法要求您从外部传入对象,并且它的生存期是有保证的,而第三种方法只有在您想要惰性实例化(即,只在第一次使用时创建对象)时才是好的。

在这种情况下,最好存储对象本身,是的。通过引用存储它只会为对象创建一个别名,因此类不是实际的所有者。通过指针存储是无用的,除非您的对象是基类并且您可能想要存储派生对象。

与Luchian Grigore相反,我倾向于指针/引用方法:将封装的对象存储为一个或多个指针,可以向前声明它,从而节省编译时间。

除此之外,它还允许您拥有init()destroy()成员函数,它们将依次调用封装对象的构造函数和析构函数,以及执行对象其他部分的初始化。通过这种方式,可以通过init()的返回值来处理错误的初始化。

大多数时候,您希望减少对类的依赖。如果这个类构成了接口的一个组成部分(即使成员是私有的),那么您可以假设任何使用您的类的人都将使用这个类。

在这种情况下,将其作为成员变量是有意义的。

如果它是类的实现详细信息,则应通过使用正向声明向用户隐藏此详细信息,因此应使用允许正向声明的类型。

它极不可能成为参考。引用必须在类的构造时草签,因此构造函数可能必须传入它所引用的对象。用new和取消引用来声明它会导致混乱。

如果它是一个指针,那么类可以使用析构函数来管理它的生存期。在这种情况下,我经常使用一个原始指针,因为它可以很好地控制,并且我的析构函数可以很高兴地删除它,假设我的类是不可复制的。

如果使用shared_ptr,则可以使用正向声明。但是要注意,现在的语义是,如果复制对象,所有副本都将有一个指向同一底层对象的指针。如果这不是您想要的,那么shared_ptr可能是错误的。此外,如果在类不可复制的情况下使用shared_ptr,则它并不是真正共享的。

因此,除非您可以使用允许前向声明的unique_ptr,否则我会选择原始指针和不可复制的类。

如果您的成员仍然是实现细节,但是相当标准的东西,如映射或向量,则不值得将其"封装"到使用正向声明的程度,只封装映射或向量中包含的类型,而不封装映射或矢量本身。