C++ 在类中存储指针或对象?

C++ 在类中存储指针或对象?,c++,class,object,pointers,move-semantics,C++,Class,Object,Pointers,Move Semantics,只是一个设计/优化问题。什么时候存储指针或对象?为什么?例如,我相信这两种方法都有效(除非编译错误): A类{ std::唯一的\u ptr对象\u ptr; }; A::A():对象_ptr(新对象()){} B类{ 对象对象; }; B::B():对象(对象()){} 我相信在堆栈或堆上实例化时会有一个区别 例如: int main(){ std::unique_ptr<A> a_ptr; std::unique_ptr<B> b_pt

只是一个设计/优化问题。什么时候存储指针或对象?为什么?例如,我相信这两种方法都有效(除非编译错误):

A类{
std::唯一的\u ptr对象\u ptr;
};
A::A():对象_ptr(新对象()){}
B类{
对象对象;
};
B::B():对象(对象()){}
我相信在堆栈或堆上实例化时会有一个区别

例如:

   int main(){
      std::unique_ptr<A> a_ptr;
      std::unique_ptr<B> b_ptr;
      a_ptr = new A(); //(*object_ptr) on heap, (*a_ptr) on heap?
      b_ptr = new B(); //(*object_ptr) on heap, object on heap?

      A a;   //a on stack, (*object_ptr) on heap?
      B b;   //b on stack, object on stack?
}
intmain(){
std::unique\u ptr


谢谢!

如果您的
类中有一个大对象,那么我会存储指向它的指针,但是对于小对象或基本类型,在大多数情况下,您不应该真的需要存储指针

此外,当某些内容存储在堆栈或堆(freestore)上时,实际上依赖于实现,并且
A
并不总是保证在堆栈上

最好将其称为自动对象,因为它的存储持续时间由声明它的函数的范围决定。当函数返回时,
a
将被销毁

指针需要使用
new
,它确实会带来一些开销,但在今天的机器上,我想说,在大多数情况下,它是微不足道的,除非您已经开始使用
new
创建数百万个对象,否则您将开始看到性能问题

每种情况都是不同的,何时应该和不应该使用指针而不是自动对象在很大程度上取决于您的情况。

堆并不是“慢”的堆分配可能比堆栈分配慢,如果您将对象和数据结构设计为不存在大量连续内存访问的方式,则较差的缓存位置可能会导致大量缓存未命中。因此,从这个角度来看,这取决于您的设计和代码使用目标

即使撇开这一点不谈,您也必须质疑您的复制语义。如果您想要对象的深度复制(并且对象的对象也被深度复制),那么为什么还要存储指针呢?如果由于复制语义可以共享内存,那么就存储指针,但确保在dtor中不会释放两次内存

我倾向于在两种情况下使用指针:类成员初始化顺序非常重要,并且我正在向对象注入依赖项。在大多数其他情况下,我使用非指针类型


编辑:当我使用指针时,还有两种情况:1)避免循环包含依赖项(尽管在某些情况下我可能使用引用),2)打算使用多态函数调用。

这取决于许多特定因素,并且任何一种方法都有其优点。我想说的是,如果您将通过动态分配专门使用外部对象,那么您最好让所有成员直接成为成员,并避免额外的成员分配。另一方面而且,如果外部对象是自动分配的,则可能应该通过
唯一的\u ptr
来处理大型成员


仅通过指针处理成员还有一个额外的好处:可以删除编译时依赖项,外部类的头文件可以通过内部类的前向声明而不需要包含内部类的头文件(“PIMPL”)。在大型项目中,这种解耦可能在经济上是合理的。

在一些情况下,您几乎别无选择,只能存储指针。一个明显的情况是,当您创建类似于二叉树的东西时:

template <class T>
struct tree_node { 
    struct tree_node *left, *right;
    T data;
}
只有几个(通常是有效的)假设:

  • 指针开始时有效,并且
  • 分配有效指针永远不会引发异常
  • …这种交换无条件地提供
    nothrow
    保证(即,我们可以说:“交换不会抛出”)。如果
    parent
    直接存储对象而不是指针,我们只能有条件地保证(例如,
    swap
    将在且仅当T的复制构造函数或赋值运算符抛出时抛出。”)

    对于C++11,经常(通常?)使用这样的指针可以很容易地提供非常高效的移动构造函数(这也提供了
    nothrow
    保证)。当然,使用指向(大部分)数据的指针并不是快速移动构造的唯一可能途径,但这是一个简单的途径

    最后,我怀疑你在问这个问题时想到了一些情况——其中所涉及的逻辑并不一定表明你应该使用自动分配还是动态分配一个判断性的调用。从纯粹的理论观点来看,在这些情况下,你可能完全没有区别。从实用的观点来看,它可以产生很大的差异。即使C和C++标准都不保证(甚至暗示)。不管怎样,现实情况是,在大多数典型的系统上,使用自动分配的对象最终都会出现在堆栈上。在大多数典型的系统(例如Windows、Linux)上,堆栈仅限于可用内存的一小部分(通常是一位数到两位数的低MB)

    这意味着,如果在任何给定时间可能存在的所有这些类型的对象都可能超过几兆字节(或更多),则需要确保(至少大部分)数据是动态分配的,而不是自动分配的。有两种方法可以做到这一点:要么让用户在父对象可能超过可用堆栈空间时动态分配父对象,要么让用户处理相对较小的“外壳”对象,代表用户动态分配空间

    如果这可能是一个问题,那么类处理动态分配几乎总是比强迫用户这样做更好。这有两个明显的优点:
    template <class T>
    struct tree_node { 
        struct tree_node *left, *right;
        T data;
    }
    
    template <class T>
    class some_string { 
        static const limit = 20;
        size_t allocated;
        size_t in_use;
        union { 
            T short_data[limit];
            T *long_data;
        };
    
        // ...
    
    };
    
    template <class T>
    class parent { 
        T *data;
    
        void friend swap(parent &a, parent &b) throw() { 
             T *temp = a.data;
             a.data = b.data;
             b.data = temp;
        }
    };