作为对象的类成员 - 指针与否?C++

Mar*_*ark 42 c++ memory pointers class-design member

如果我创建一个MyClass类并且它有一些私有成员说MyOtherClass,那么将MyOtherClass作为指针是否更好?将它作为存储在内存中的指针而不是指针也意味着什么?创建类时是否会创建对象?

我注意到QT中的示例通常在类成员时将类成员声明为指针.

问候

标记

jus*_*tin 32

如果我创建一个MyClass类并且它有一些私有成员说MyOtherClass,那么将MyOtherClass作为指针是否更好?

你通常应该将它声明为你班级的价值.它将是本地的,错误的机会更少,分配更少 - 最终可能出错的事情更少,编译器总是可以知道它在指定的偏移位置,所以...它有助于优化和二进制减少几个级别.在一些情况下,你知道你必须处理指针(即多态,共享,需要重新分配),通常最好只在必要时使用指针 - 特别是当它是私有/封装时.

将它作为存储在内存中的指针而不是指针也意味着什么?

它的地址将接近(或等于)this- gcc(例如)有一些高级选项来转储类数据(大小,vtable,偏移)

创建类时是否会创建对象?

是的 - MyClass的大小将增加sizeof(MyOtherClass),或者更多,如果编译器重新调整它(例如,它的自然对齐)

  • 在较大的项目中,它的一大缺点是强制声明MyOtherClass的头部的#include.这很快就会导致编译时间非常慢.如果使用(智能)指针,则可以使用前向声明. (12认同)

Wol*_*ngP 26

您的会员在哪里存储在内存中?

看看这个例子:

struct Foo { int m; };
struct A {
  Foo foo;
};
struct B {
  Foo *foo;
  B() : foo(new Foo()) { } // ctor: allocate Foo on heap
  ~B() { delete foo; } // dtor: Don't forget this!
};

void bar() {
  A a_stack; // a_stack is on stack
             // a_stack.foo is on stack too
  A* a_heap = new A(); // a_heap is on stack (it's a pointer)
                       // *a_heap (the pointee) is on heap
                       // a_heap->foo is on heap
  B b_stack; // b_stack is on stack
             // b_stack.foo is on stack
             // *b_stack.foo is on heap
  B* b_heap = new B(); // b_heap is on stack
                       // *b_heap is on heap
                       // b_heap->foo is on heap
                       // *(b_heap->foo is on heap
  delete a_heap;
  delete b_heap;
  // B::~B() will delete b_heap->foo!
} 
Run Code Online (Sandbox Code Playgroud)

我们定义了两个类AB.A存储foo类型的公共成员Foo.B有一个foo类型的成员pointer to Foo.

情况如何A:

  • 如果在堆栈上创建a_stack类型的变量,那么对象(显然)及其成员也在堆栈上.A
  • 如果你在上面的例子中创建了一个A类似a_heap的指针,那么指针变量就在堆栈上 ; 其他一切(对象和它的成员)都在堆上.

在以下情况下情况如何B:

  • B堆栈上创建:然后对象及其成员foo都在堆栈上,但foo指向(指针对象)的对象在堆上.简而言之:( b_stack.foo指针)在堆栈上,但*b_stack.foo(指针)在堆上.
  • 你创建一个指向Bnamed 的指针b_heap:( b_heap指针)在堆栈上,*b_heap(指针对象)在堆上,以及成员b_heap->foo*b_heap->foo.

该对象是否会自动创建?

  • 如果是A:是,foo将通过调用隐式默认构造函数自动创建Foo.这将创建一个integer不会初始化它(它将有一个随机数)!
  • 在B的情况下:如果省略我们的ctor和dtor,那么foo(指针)也将被创建并用随机数初始化,这意味着它将指向堆上的随机位置.但请注意,指针存在!另请注意,隐式默认构造函数不会foo为您分配内容,您必须明确地执行此操作.这就是为什么你通常需要一个显式的构造函数和一个附带的析构函数来分配和删除你的成员指针的指针.不要忘记复制语义:如果复制对象(通过复制构造或赋值),指针对象会发生什么?

所有这一切有什么意义?

有几个使用指向成员的指针的用例:

  • 指向您不拥有的对象.假设您的类需要访问庞大的数据结构,复制成本非常高.然后你可以保存一个指向这个数据结构的指针.请注意,在这种情况下,创建删除数据结构超出了您的类的范围.其他人必须要小心.
  • 增加编译时间,因为在头文件中不必定义指针.
  • 更高级; 当你的类有一个指向另一个存储所有私有成员的类的指针时,可以使用"Pimpl idiom":http://c2.com/cgi/wiki ?PimplIdiom ,看看Sutter,H.(2000):Exceptional C++,p.99--119
  • 还有其他人,看看其他答案

忠告

如果您的成员是指针而您拥有它们,请格外小心.您必须编写适当的构造函数,析构函数并考虑复制构造函数和赋值运算符.如果复制对象,指针对象会发生什么?通常你也必须复制构造指针!


jal*_*alf 18

在C++中,指针本身就是对象.他们并没有真正依赖于他们指向的东西,并且指针和它的指针之间没有特殊的交互(这是一个单词吗?)

如果创建指针,则无需创建指针.您不创建它可能或可能不指向的对象.当指针超出范围时,指向的对象不受影响.指针不会以任何方式影响它所指向的生命周期.

所以一般来说,默认情况下应该使用指针.如果您的类包含另一个对象,则该另一个对象不应该是指针.

但是,如果你的类知道另一个对象,那么指针可能是表示它的一种好方法(因为你的类的多个实例可以指向同一个实例,而不需要拥有它,并且不控制它的生命周期)

  • 另一方面,PIMPL通过在可见性中引入一层间接来减少依赖性. (2认同)

Bar*_*nau 7

C++中的常识是尽可能避免使用(裸)指针.特别是指向动态分配内存的裸指针.

原因是因为指针使得编写健壮的类变得更加困难,尤其是当您还必须考虑抛出异常的可能性时.


小智 5

我遵循以下规则:如果成员对象与封装对象一起生存和死亡,则不要使用指针。如果成员对象由于某种原因必须比封装对象存活时间更长,则您将需要一个指针。取决于手头的任务。

如果成员对象是给你的而不是你创建的,通常你会使用指针。那么你通常也不必销毁它。