[英]C++ Object Instantiation
我是一名试图理解C ++的C程序员。 许多教程使用片段演示对象实例化,例如:
Dog* sparky = new Dog();
这暗示你以后会这样做:
delete sparky;
这是有道理的。 现在,在不需要动态内存分配的情况下,是否有任何理由使用上述代替
Dog sparky;
一旦sparky超出范围,让析构函数被调用?
谢谢!
相反,您应该总是更喜欢堆栈分配,在一定程度上,您的用户代码中永远不应该有新的/删除。
正如您所说,当变量在堆栈上声明时,它的析构函数会在超出范围时自动调用,这是跟踪资源生命周期和避免泄漏的主要工具。
因此,通常,每次需要分配资源时,无论是内存(通过调用new),文件句柄,套接字还是其他任何东西,都将它包装在构造函数获取资源的类中,析构函数将其释放。 然后,您可以在堆栈上创建该类型的对象,并确保在资源超出范围时释放资源。 这样您就不必在任何地方跟踪新的/删除对,以确保避免内存泄漏。
这个成语最常见的名字是RAII
还要研究智能指针类,它们用于在极少数情况下包装结果指针,当您必须在专用RAII对象外部分配新内容时。 您将指针传递给智能指针,然后智能指针跟踪其生命周期,例如通过引用计数,并在最后一个引用超出范围时调用析构函数。 标准库具有std::unique_ptr
用于简单的基于范围的管理,而std::shared_ptr
用于引用计数以实现共享所有权。
许多教程使用片段演示对象实例化,例如...
所以你发现的是大多数教程很糟糕。 ;)大多数教程教你糟糕的C ++实践,包括调用new / delete来创建变量(如果没有必要),并让你很难跟踪你的分配生命周期。
虽然堆栈上的东西在分配和自动释放方面可能是一个优势,但它有一些缺点。
您可能不希望在堆栈上分配大型对象。
动态调度! 考虑以下代码:
#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&
(按引用调用),它会产生巨大的差异。 许多组合都是可能的,它们的行为都不同。
好吧,使用指针的原因与在使用malloc分配的C中使用指针的原因完全相同:如果你希望你的对象比你的变量活得更久!
如果可以避免使用新操作员,甚至强烈建议不要使用它。 特别是如果你使用例外。 通常,让编译器释放对象会更安全。
我从那些没有得到&address-of运算符的人那里看到了这种反模式。 如果他们需要使用指针调用函数,他们将始终在堆上进行分配,以便获得指针。
void FeedTheDog(Dog* hungryDog);
Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;
Dog goodDog;
FeedTheDog(&goodDog);
将堆视为一个非常重要的房地产,并非常明智地使用它。 基本的经验法则是使用堆栈,并尽可能使用堆每当有没有其他办法。 通过在堆栈上分配对象,您可以获得许多好处,例如:
(1)。 如果出现异常,您无需担心堆栈展开
(2)。 您不必担心由堆管理器分配的空间超出所需的内存碎片。
我担心的唯一原因是Dog现在被分配在堆栈上,而不是堆。 因此,如果Dog的大小为兆字节,则可能会出现问题,
如果确实需要使用新的/删除路由,请注意例外情况。 因此,您应该使用auto_ptr或其中一种boost智能指针类型来管理对象的生命周期。
当你可以在堆栈上分配时,没有理由新(在堆上)(除非由于某种原因你有一个小堆栈并想要使用堆。
如果您想在堆上分配,可能需要考虑使用标准库中的shared_ptr(或其变体之一)。 一旦对shared_ptr的所有引用都已经存在,那将为您执行删除操作。
还有一个原因,没有其他人提到过,为什么你可以选择动态创建对象。 动态的,基于堆的对象允许您使用多态 。
我在Visual Studio中遇到了同样的问题。 你必须使用:
yourClass->类方法();
而不是:
yourClass.classMethod();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.