为什么使用"新"是个坏主意?

Why is it a bad idea to use 'new'?

本文关键字:为什么      更新时间:2023-10-16

可能重复:
在C++中,为什么要尽可能少地使用new

在C++中实例化类时使用"new"真的是个坏主意吗?在这里找到。

我知道使用原始指针是不明智的,但既然这是一种糟糕的做法,为什么还要使用"new"关键字呢?还是这样?

关键是new与妊娠非常相似,它创建了一个手动管理的资源(即由(,因此它具有责任

C++是一种用于库编写的语言,每当你看到责任时,"C++"方法就是编写一个库元素来处理这个,而且只有这个责任。对于动态内存分配,这些库组件已经存在,概括地称为"智能指针";您将需要查看std::unique_ptrstd::shared_ptr(或它们的TR1或Boost等价物(。

在编写这些单一责任构建块时,您确实需要说newdelete。但您只需执行一次,然后仔细考虑并确保提供正确的复制、分配和销毁语义。(从异常安全的角度来看,单一责任至关重要,因为一次处理多个单一资源是非常不可扩展的。(

一旦你把所有的东西都考虑到合适的构建块中,你就将这些构建块组成越来越大的代码系统,但此时你就不需要再承担任何手动责任了,因为构建块已经为你做了这件事。

由于标准库为绝大多数用例(动态数组、智能指针、文件句柄、字符串(提供了资源管理类,因此关键是,一个精心设计的C++项目应该不需要任何类型的手动资源管理,其中包括new的使用。所有的处理程序对象要么是自动的(作用域(,要么是其他类的成员,这些类的实例反过来又由某人作用域或管理。

考虑到这一点,您应该说new的唯一时间是在创建新的资源管理对象时;尽管即便如此,这并不总是必要的:

std::unique_ptr<Foo> p1(new Foo(1, 'a', -2.5));   // unique pointer
std::shared_ptr<Foo> p2(new Foo(1, 'a', -2.5));   // shared pointer
auto p3 = std::make_shared<Foo>(1, 'a', -2.5);    // equivalent to p2, but better

更新:我想我可能只解决了OP一半的担忧。许多来自其他语言的人似乎认为任何对象都必须用new类型的表达式实例化。当使用C++时,这本身就是一种非常无益的心态:

C++中的关键区别是对象lifetime或"存储类"。这可以是以下其中之一:自动(作用域(、静态动态。全局变量具有静态生存期。绝大多数变量(在本地范围内声明为Foo x;(都具有自动生存期。只有对于动态存储,我们才使用new表达式。当从另一种OO语言使用C++时,最重要的一点是,大多数对象只需要具有自动生存期,因此无需担心。

因此,第一个实现应该是"C++很少需要动态存储"。我觉得这可能是OP问题的一部分。这个问题最好用"动态分配对象真的是个坏主意吗?"来表达。只有在之后,您才决定真正需要动态存储,我们才能讨论您是否应该经常使用newdelete,或者是否有更好的替代方案,这就是我最初回答的要点。

尽可能避免new意味着许多好处,例如:

  • 首先,还要避免delete语句。尽管聪明的指针可以在这里帮助你。所以这不是重点。

  • 您可以避免"三条规则"(在C++03中(或"五条规则"。如果在设计类时使用new,也就是说,当类在内部管理原始内存时,可能必须考虑此规则。

  • 当您不使用new时,很容易实现异常安全代码。否则,您将面临大量问题,从而确保代码异常的安全性。

不必要地使用new意味着你会引发问题。我看到,当一个没有经验的程序员使用new时,他通常有更好的替代方案,比如使用标准容器和算法。使用标准容器避免了显式使用new所带来的大多数问题。

这还不错,但用new分配的所有内容都必须用delete来处理。这样做并不总是微不足道的,尤其是当您考虑到异常情况时。我想这就是那个帖子的意思。

这不是一个"使用新东西的坏主意"——发帖者错误地陈述了他的情况。相反,使用新的和不给你两种不同的东西。

new为您提供了一个新的、单独分配的类实例,并返回一个指向该实例的指针。

使用不带new的类名会创建一个类的自动实例,当它超出作用域时,该实例将变为"poof"。这将"返回"实例本身,而不是指针(因此会返回其他线程中的语法错误(。

如果在引用的情况下使用new并添加*来传递编译器,则会导致对象泄漏。另一方面,如果您将一个参数传递给一个要将其存储在某个地方的方法,并且您传递了一个非新实例,使其与&一起工作,那么您最终会存储一个悬空指针。

new什么是deletefree什么是malloc(不要把它们混合在一起,你会遇到麻烦的(。有时你必须使用new,因为用new分配的数据不会超出范围。。。除非数据指针丢失(这是new的全部问题(。但这是程序员的错误,而不是关键词。

这取决于代码的需求。它是您引用的回复,向量包含客户端实例,而不是指向客户端实例的指针。

在C++中,您可以直接在堆栈上创建对象,而无需使用新的,如下面代码中的V1和V2:

void someFct()
{
     std::vector<client> V1;
     //....
     std::vector<client*> V2;
}

使用V2时,您必须使用新操作创建新的客户端实例,但当V2超出范围时,客户端对象将不会被释放(删除(。没有垃圾收集器。在离开函数之前,必须删除对象。

要自动删除创建的实例,可以使用std::shared_ptr。这使得代码的编写时间更长,但从长远来看维护起来更简单:

void someFct()
{
    typedef std::shared_ptr<client> client_ptr;
    typedef std::vector<client_ptr> client_array;
    client_array V2;
    V2.push_back(client_ptr(new client()));
    // The client instance are now automatically released when the function ends, 
    // even if an exception is thrown.
}

在C++中实例化类时使用"new"真的是个坏主意吗?

它通常很糟糕,因为它不是必要的,而且当你不恶意使用它时,代码会变得容易得多。如果你不使用它就可以逃脱惩罚,那就这样做吧。我已经在不使用new的情况下编写了整个库。

我知道使用原始指针是不明智的,但既然这是一种糟糕的做法,为什么还要使用"new"关键字呢?还是这样?

这并不是普遍的糟糕,只是大多数时候没有必要。但也有合适的时候,这就是为什么会有这样一个关键词。也就是说,如果没有关键字,C++本可以逃脱惩罚,因为new合并了两个概念:1。它分配内存,以及2。它将内存初始化为对象。

您可以通过使用其他内存分配方式,然后调用构造函数("placement-new"(来解耦这些进程。这实际上是通过分配器在所有地方完成的,比如标准库。

另一方面,客户端代码管理未初始化的内存很少有意义(读作:never(,因此不解耦这两个进程是有意义的。因此CCD_ 35的存在。