如果我创建了一个MyClass类,并且它有一些私有成员说MyOtherClass,那么将MyOtherClass作为指针是否更好?将它作为存储在内存中的指针而不是指针也意味着什么?在创建类时是否会创建对象?
我注意到QT中的示例通常在类成员时将类成员声明为指针。
此致
标记
答案 0 :(得分:30)
如果我创建了一个MyClass类并且它有一些私有成员说MyOtherClass,那么将MyOtherClass作为指针是否更好?
您通常应将其声明为班级中的值。它将是本地的,错误的机会更少,分配更少 - 最终可能出错的事情更少,编译器总是可以知道它在指定的偏移位置,所以...它有助于优化和二进制减少几个级别。在一些情况下,你知道你必须处理指针(即多态,共享,需要重新分配),通常最好只在必要时使用指针 - 特别是当它是私有/封装时。
将它作为指针存储在内存中的位置也是什么意思呢?
它的地址将接近(或等于)this
- gcc(例如)有一些高级选项来转储类数据(大小,vtable,偏移)
创建类时是否会创建对象?
是的 - MyClass的大小将增加sizeof(MyOtherClass),如果编译器重新调整它(例如,它的自然对齐),则会增加更多
答案 1 :(得分:23)
看一下这个例子:
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!
}
我们定义了两个类A
和B
。 A
存储foo
类型的公开成员Foo
。 B
的成员foo
的类型为pointer to Foo
。
A
的情况如何:
a_stack
的变量A
,则该对象(显然)及其成员位于堆栈也是。A
的{{1}}指针,那么只有指针变量位于堆栈;其他一切(对象及其成员)都在堆。 a_heap
:
B
:然后对象及其成员B
都在堆栈上,但对象{ {1}}指向(指针对象)位于堆上。简而言之:foo
(指针)在堆栈上,但foo
(指针)在堆上。b_stack.foo
*b_stack.foo
的指针:B
(指针)在堆栈上,b_heap
(指针对象)在堆上,以及成员b_heap
和*b_heap
。b_heap->foo
的隐式默认构造函数自动创建*b_heap->foo
。这将创建一个foo
但不对其进行初始化(它将有一个随机数)!Foo
(指针)也将被创建并用随机数初始化,这意味着它将指向随机位置强>在堆上。但请注意,指针存在!另请注意,隐式默认构造函数不会为您分配integer
的内容,您必须执行此操作显式。这就是为什么你通常需要一个显式构造函数和一个附带的析构函数来分配和删除你的成员指针的指针。不要忘记复制语义:如果复制对象(通过复制构造或赋值),指针对象会发生什么?使用指向成员的指针有几种用例:
如果您的成员是指针并且您拥有它们,请格外小心。您必须编写适当的构造函数,析构函数并考虑复制构造函数和赋值运算符。如果复制对象,指针对象会发生什么?通常你也必须复制构造指针对象!
答案 2 :(得分:17)
在C ++中,指针本身就是对象。他们并没有真正依赖于他们指向的东西,并且指针和它的指针之间没有特殊的交互(这是一个单词吗?)
如果创建指针,则创建指针而不创建其他。您不创建它可能或可能不指向的对象。当指针超出范围时,指向的对象不受影响。指针不会以任何方式影响它指向的生命周期。
因此,一般情况下,默认情况下不应该使用指针。如果您的类包含另一个对象,则该另一个对象不应该是指针。
但是,如果您的类知道另一个对象,那么指针可能是表示它的好方法(因为您的类的多个实例可以指向同一个实例,而不需要拥有它,并且没有控制它的寿命)
答案 3 :(得分:6)
C ++中的常识是尽可能避免使用(裸)指针。特别是指向动态分配内存的裸指针。
原因是因为指针使编写健壮的类变得更加困难,尤其是当您还必须考虑抛出异常的可能性时。
答案 4 :(得分:3)
这个问题可以无休止地审议,但基本要点是:
如果MyOtherClass不是指针:
如果MyOtherClass是指针:
NULL
,这可能在您的上下文中有意义并且可以节省内存答案 5 :(得分:3)
我遵循以下规则:如果成员对象与封装对象一起生存和死亡,请不要使用指针。如果成员对象由于某种原因必须比封装对象更长,则需要指针。取决于手头的任务。
如果成员对象是给你的而不是你创建的,通常使用指针。然后你通常也不必破坏它。
答案 6 :(得分:3)
指针成员的一些优点:
将成员作为对象的优点:
答案 7 :(得分:1)
如果将MyOtherClass对象作为MyClass的成员:
size of MyClass = size of MyClass + size of MyOtherClass
如果将MyOtherClass对象作为MyClass的指针成员:
size of MyClass = size of MyClass + size of any pointer on your system
您可能希望将MyOtherClass保留为指针成员,因为它使您可以灵活地将其指向从其派生的任何其他类。基本上可以帮助您实现动态多态性。
答案 8 :(得分:1)
这取决于......: - )
如果您使用指针来说class A
,则必须创建类型A的对象,例如在你的类的构造函数中
m_pA = new A();
此外,不要忘记销毁析构函数中的对象或者你有内存泄漏:
delete m_pA;
m_pA = NULL;
相反,在类中聚合类型A的对象更容易,您不能忘记销毁它,因为这是在对象的生命周期结束时自动完成的。
另一方面,拥有指针具有以下优点:
如果你的对象被分配了 堆栈和类型A使用大量内存 这不会从中分配 堆栈但是从堆中。
您可以稍后构建您的A对象(例如,在方法Create
中)或稍早销毁它(在方法Close
中)
答案 9 :(得分:1)
保持与成员对象的关系的父类作为成员对象的(std :: auto_ptr)指针的一个优点是,您可以转发声明对象而不必包含对象的头文件。
这会在构建时解耦类,允许修改成员对象的头类,而不会导致父类的所有客户端被重新编译,即使它们可能不访问成员对象的函数。
当您使用auto_ptr时,您只需要处理构造,这通常可以在初始化列表中执行。 auto_ptr保证了与父对象的破坏。
答案 10 :(得分:0)
简单的做法是将您的成员声明为对象。这样,您就不必关心复制构造,销毁和分配。这都是自动处理的。
但是,在某些情况下,您仍需要指针。毕竟,托管语言(如C#或Java)实际上是通过指针保存成员对象。
最明显的情况是要保留的对象是多态的。在Qt中,正如您所指出的,大多数对象属于多态类的庞大层次结构,并且必须通过指针保存它们,因为您事先并不知道成员对象具有的大小。
请注意这种情况下的一些常见陷阱,尤其是在处理泛型类时。例外安全是一个大问题:
struct Foo
{
Foo()
{
bar_ = new Bar();
baz_ = new Baz(); // If this line throw, bar_ is never reclaimed
// See copy constructor for a workaround
}
Foo(Foo const& x)
{
bar_ = x.bar_.clone();
try { baz_ = x.baz_.clone(); }
catch (...) { delete bar_; throw; }
}
// Copy and swap idiom is perfect for this.
// It yields exception safe operator= if the copy constructor
// is exception safe.
void swap(Foo& x) throw()
{ std::swap(bar_, x.bar_); std::swap(baz_, x.baz_); }
Foo& operator=(Foo x) { x.swap(*this); return *this; }
private:
Bar* bar_;
Baz* baz_;
};
如您所见,在存在指针的情况下使用异常安全构造函数非常麻烦。你应该看看RAII和智能指针(这里有很多资源和网络上的其他地方)。