C ++对象实例化

时间:2008-12-02 09:23:36

标签: c++ instantiation

我是一名试图理解C ++的C程序员。许多教程使用代码段演示对象实例化,例如:

Dog* sparky = new Dog();

这暗示你以后会做:

delete sparky;

这是有道理的。现在,在不需要动态内存分配的情况下,有没有理由使用上面的代替

Dog sparky;

让一旦sparky超出范围就调用析构函数?

谢谢!

9 个答案:

答案 0 :(得分:160)

相反,你应该总是更喜欢堆栈分配,在一定程度上,你的用户代码中不应该有新的/删除。

正如你所说,当变量在堆栈上声明时,它的析构函数会在超出范围时自动调用,这是跟踪资源生命周期和避免泄漏的主要工具。

所以一般来说,每次需要分配资源时,无论是内存(通过调用new),文件句柄,套接字还是其他任何东西,都将它包装在构造函数获取资源的类中,析构函数将其释放。然后,您可以在堆栈上创建该类型的对象,并确保在资源超出范围时释放资源。这样您就不必在任何地方跟踪新的/删除对,以确保避免内存泄漏。

这个习语的最常见名称是RAII

另请参阅智能指针类,这些类用于在极少数情况下包装结果指针,当您必须在专用RAII对象外部分配新内容时。您将指针传递给智能指针,然后智能指针跟踪其生命周期,例如通过引用计数,并在最后一个引用超出范围时调用析构函数。标准库具有std::unique_ptr用于简单的基于范围的管理,std::shared_ptr用于引用计数以实现共享所有权。

  

许多教程演示了对象   使用诸如......的片段进行实例化。

所以你发现的是大多数教程都很糟糕。 ;) 大多数教程都教你糟糕的C ++实践,包括调用new / delete来在不需要的时候创建变量,并让你很难跟踪你的分配的生命周期。

答案 1 :(得分:21)

尽管堆栈中的东西在分配和自动释放方面可能是一个优势,但它有一些缺点。

  1. 您可能不希望在堆栈上分配大型对象。

  2. 动态发送!请考虑以下代码:

  3. #include <iostream>
    
    class A {
    public:
      virtual void f();
      virtual ~A() {}
    };
    
    class B : public A {
    public:
      virtual void f();
    };
    
    void A::f() {cout << "A";}
    void B::f() {cout << "B";}
    
    int main(void) {
      A *a = new B();
      a->f();
      delete a;
      return 0;
    }
    

    这将打印“B”。现在让我们看看使用Stack时会发生什么:

    int main(void) {
      A a = B();
      a.f();
      return 0;
    }
    

    这将打印“A”,对于熟悉Java或其他面向对象语言的人来说可能不太直观。原因是您没有指向B实例的指针。而是创建B的实例并将其复制到a类型的A变量。

    有些事情可能不直观地发生,特别是当你不熟悉C ++时。在C中你有你的指针就是这样。你知道如何使用它们,它们总是一样。在C ++中,情况并非如此。想象一下,当你在这个例子中使用a作为方法的参数时会发生什么 - 事情变得更复杂,如果a属于A类型或A*,它会产生巨大的差异甚至A&(按引用调用)。许多组合都是可能的,它们的行为都不同。

答案 2 :(得分:13)

嗯,使用指针的原因与使用malloc分配的C指针的原因完全相同:如果你希望你的对象比你的变量活得更久!

如果可以避免使用新操作员,甚至强烈建议不要使用。特别是如果你使用例外。通常,让编译器释放你的对象会更安全。

答案 3 :(得分:13)

我从那些不太了解&amp; amp;运营商地址。如果他们需要使用指针调用函数,他们将始终在堆上进行分配,以便获得指针。

void FeedTheDog(Dog* hungryDog);

Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;

Dog goodDog;
FeedTheDog(&goodDog);

答案 4 :(得分:7)

将堆视为一个非常重要的房地产并非常明智地使用它。基本的拇指规则是尽可能使用堆栈 ,并在没有其他方法时使用堆。通过在堆栈上分配对象,您可以获得许多好处,例如:

(1)。在异常情况下,您无需担心堆栈展开

(2)。您不必担心由堆管理器分配的空间超出所需的内存碎片。

答案 5 :(得分:5)

我担心的唯一原因是Dog现在被分配到堆栈而不是堆。因此,如果Dog的大小为兆字节,则可能会出现问题,

如果您确实需要使用新的/删除路线,请注意例外情况。因此,您应该使用auto_ptr或其中一种boost智能指针类型来管理对象的生命周期。

答案 6 :(得分:1)

没有理由在堆上分配新的(在堆上)(除非由于某种原因你有一个小堆并且想要使用堆。

如果您想在堆上进行分配,可能需要考虑使用标准库中的shared_ptr(或其变体之一)。

,一旦对shared_ptr的所有引用都已经存在,那将会处理删除。

答案 7 :(得分:0)

还有一个原因,没有其他人提到,为什么你可能会选择动态创建你的对象。动态的基于堆的对象允许您使用polymorphism

答案 8 :(得分:-4)

我在Visual Studio中遇到了同样的问题。你必须使用:

yourClass-&GT;类方法();

而不是:

yourClass.classMethod();