*新总是错误的.永远

*new is always wrong. ALWAYS

本文关键字:永远 错误 新总      更新时间:2023-10-16

为了解释这个问题中的指针和引用,我写了这段代码。

MyClass& MyClass::MyInstance()
{       
    static MyClass & myLocalVariable = * new MyClass(/*parameters*/);
    return myLocalVariable ;
}

其中一条评论是由一位非常令人印象深刻的高声誉SO用户发表的,他简单地说:*新总是错误的。永远

这是我第一次被告知:这是一个我们都应该知道的著名编码标准吗?背后的原因是什么?

我通常很务实,但这对我来说太过分了!

static MyClass & myLocalVariable = * new MyClass(/*parameters*/);

说真的吗?为什么不简单:

static MyClass myLocalVariable{/*parameters*/};

最明显的原因是,如果你不保留新返回的指针的副本,你就不可能对它调用delete

在更人性化的层面上,它会让阅读你代码的人对你的评价降低,这也从来都不是一件好事。

我相信上述用户的意思是,使用new分配静态对象是危险的,因为内存很可能会泄漏。更重要的是,你的变量不是指针,而是引用,所以你永远不会释放new返回的内存的可能性更大(你多久删除一次引用的地址?)。

问题不仅仅是无用的分配。

如果没有人对它调用delete,它就不会被删除。当然,当程序结束但没有调用其析构函数时,内存会被释放。

比较以下代码中使用Get()Get2()的输出:

    #include <iostream>
    struct A
    {
        ~A(){std::cout << "Deletedn";}
    };
    A& Get()
    {
      static A & a = *new A;
      return a;
    }
    A& Get2()
    {
      static A a;
      return a;
    }
    int main()
    {
       //Case 1
       Get();
       //Case 2
       Get2();
    }

当调用Get时该程序的输出为零,当调用Get2时该程序输出为Deleted
换句话说,在情况1中,具有非平凡析构函数的资源(例如关闭文件句柄时提交)在程序终止时不会被正确销毁,但在情况2中会被正确销毁。

问题是原始new没有指定所有权。如果我新建一个对象并将其归还,谁拥有它?创建函数/对象拥有它,还是调用函数拥有它?如果您返回智能指针(std::shared_ptr和std::unique_ptr),则表示您指定了所有权。

不指定所有权是泄露内存的最简单方法之一。即使是专业程序员,我也最难让人们理解所有权并使用它。这主要是通过使用指定所有权的好类型(智能指针)来防止的,而不仅仅是通过现有。

type* function();    // Unspecified ownership.
                     // Must be well documented and all users must read
                     // and follow the documentation.
std::unique_ptr<type> function(); // Calling function owns returned pointer.
                                  // Single ownership.
std::shared_ptr<type> function(); // Calling function owns returned pointer.
                                  // Shared ownership.  Can have multiple owners.
std::weak_ptr<type> function();   // Calling function references returned pointer.
                                  // Must lock pointer to get owned object, if not deleted.
                                  // Shared ownership.  Can have multiple owners.

这些不同类型的指针仅通过存在不同于原始指针的指针来表示所有权。

至于new总是错的。这是过于笼统的概括。CCD_ 7是使用全局函数CCD_。截至C++11,没有std::make_unique,但这将在C++14中固定。创建std::unique_ptr的唯一方法是使用new并立即将指针分配给std::unique_ptr

也有一些地方你想要一个原始指针并手动使用newdelete,但它们往往是非常低级的,大多数程序员很少会遇到它们。


真正让我对你的代码感到尴尬的不是你在使用new,而是你在取消引用指针并将其分配给引用。几乎不可能保证析构函数会被调用。它也倾向于泄漏内存,尽管在分配给静态变量的情况下,它会在程序终止时被释放,所以你并没有真正关注内存泄漏。

MyClass& MyClass::MyInstance()
{       
    static MyClass & myLocalVariable = * new MyClass(/*parameters*/);
    return myLocalVariable ;
}

我更喜欢通过值而不是通过引用来创建静态变量。这样可以防止将对象放到堆上。根据MyClass,它还可以允许对象从可执行文件映射到内存,而不必运行任何代码来初始化它

MyClass& MyClass::MyInstance()
{
    static MyClass myLocalVariable(/*parameters*/);
    return myLocalVariable ;
}