C ++对象实例化
我是一个试图理解C ++的C程序员。 很多教程都演示了如何使用代码片段实现对象:
Dog* sparky = new Dog();
这意味着稍后你会做:
delete sparky;
这是有道理的。 现在,在不需要dynamic内存分配的情况下,有没有什么理由使用上面的代替
Dog sparky;
让一旦闪烁超出范围调用析构函数?
谢谢!
相反,你应该总是喜欢堆栈分配,在一定程度上,你永远不应该在你的用户代码中有新的/删除。
正如你所说,当variables在堆栈中被声明时,它的析构函数在超出范围时被自动调用,这是跟踪资源生命周期和避免泄漏的主要工具。
所以一般来说,每次需要分配资源时,无论是内存(通过调用new),文件句柄,套接字还是其他任何东西,都将其包装在构造函数获取资源的类中,然后析构函数释放它。 然后你可以在堆栈上创build一个这种types的对象,并保证你的资源在超出作用域时被释放。 这样你就不必在任何地方跟踪你的新/删除对,以确保你避免了内存泄漏。
这个成语最常用的名字是RAII
还要研究智能指针类,这些智能指针类用于在极less数情况下包装所产生的指针,当您必须在专用的RAII对象之外分配新的东西。 您将指针传递给一个智能指针,然后通过引用计数来跟踪其生命周期,并在最后一个引用超出范围时调用析构函数。 标准库具有std::unique_ptr
用于简单的基于范围的pipe理, std::shared_ptr
用于引用计数来实现共享所有权。
许多教程演示使用片段的对象实例化,如…
所以你发现大多数教程都很糟糕。 ;)大多数教程教你糟糕的C ++实践,包括在不需要的时候调用new / delete来创buildvariables,并且让你很难追踪分配的生命周期。
尽pipe在分配和自动释放事物方面可能是一个优点,但它有一些缺点。
-
你可能不想在堆栈上分配巨大的对象。
-
运行时types! 考虑这个代码:
#include <iostream> class A { public: virtual void f(); }; class B : public A { public: virtual void f(); }; void A::f() {cout << "A\n";} void B::f() {cout << "B\n";} int main(void) { A *a = new B(); a->f(); delete a; return 0; }
显然会打印“B \ n”。 现在让我们看看使用堆栈时会发生什么:
int main(void) { A a = B(); a->f(); return 0; }
它会(对于C ++的人明显)打印“A \ n”。 对于那些与Java或其他OOP风格语言类似的人来说,这可能并不直观。 原因是,你不再有一个B的实例的指针。 但是创build了一个B的实例,然后将其复制到A的一个实例(隐式地新创build)。
不幸的是,在我看来,这是C ++的重大devise问题之一。 许多事情不直观地发生。 在C你有你的指针,就是这样。 你知道如何使用它们,它们总是一样的。 在C ++中,情况并非如此。 试想一下,当在这个例子中使用a作为方法的参数时会发生什么事情 – 事情变得越来越复杂,如果a是A或A *types,并且方法是call-by价值观或引用参考(许多组合是可能的,他们[相当]所有行为以其他方式)。
我看到了那些不太清楚操作符地址的人的反模式。 如果他们需要用一个指针调用一个函数,他们将总是在堆上分配,以便得到一个指针。
void FeedTheDog(Dog* hungryDog); Dog* badDog = new Dog; FeedTheDog(badDog); delete badDog; Dog goodDog; FeedTheDog(&goodDog);
那么,使用这个指针的原因和使用malloc分配的C中的指针的原因是完全一样的:如果你希望你的对象比你的variables长!
如果可以避免的话,强烈build议不要使用新的操作符。 特别是如果你使用例外。 一般来说,让编译器释放你的对象会更安全。
把堆作为一个非常重要的房地产,并非常明智地使用它。 基本的拇指规则是尽可能使用堆栈, 只要没有其他方法就使用堆栈。 通过在堆栈上分配对象可以获得许多好处,例如:
(1)。 如果发生exception情况,您不必担心堆栈展开
(2)。 您不必担心由堆pipe理器分配的空间超过必要的空间所导致的内存碎片。
我担心的唯一原因是Dog现在被分配在堆栈上,而不是堆中。 所以如果狗是兆字节的大小,你可能有一个问题,
如果你确实需要去新的/删除的路线,请注意例外情况。 因此,您应该使用auto_ptr或其中一种boost智能指针types来pipe理对象的生命周期。
当你可以在堆栈上进行分配时(没有理由)新的堆栈(除非由于某种原因你有一个小堆栈并且想要使用这个堆栈。
如果您希望在堆上分配,您可能需要考虑使用标准库中的shared_ptr(或其中一个变体)。 一旦所有对shared_ptr的引用都没有存在,这将会处理删除操作。
还有一个额外的原因,其他人没有提到,为什么你可能会selectdynamic创build你的对象。 dynamic的,基于堆的对象允许你使用多态 。
我在Visual Studio中有同样的问题。 你必须使用:
yourClass->类方法();
而不是:
yourClass.classMethod();