动态分配的正确用法是什么 - 应该避免这种情况吗? (在单独的范围内使用new和delete)

时间:2014-10-17 19:14:27

标签: c++ memory-management

我无法理解new关键字的正确用法。我的问题是:

  1. 我怀疑以下是不好的设计?
  2. 如果没有,我应该在哪里拨打delete
  3. 如果是,那么更好的做法是什么?
  4. #include <string>
    #include <iostream>
    
    struct myOptions {
        int myInt;
        std::string myString;
    };
    
    myOptions* packageOptions() {
        myOptions* options = new myOptions;
        options->myInt = 42;
        options->myString = "hello world";
    
        return options;
    }
    
    int main() {
        myOptions* options = packageOptions();
    
        std::cout << options->myString << std::endl;
        std::cout << options->myInt << std::endl;
    
        delete myOptions; // this just feels wrong to put here
    }
    

    我的直觉告诉我这很糟糕,因为主要功能不应该管理其他功能分配的内存,因为它打破了某种封装。我想过做一个类构造函数/解构函数,但这似乎有点矫枉过正。

6 个答案:

答案 0 :(得分:2)

没有理由像你一样手动追逐内存。我只是在堆栈上声明你的变量,然后按值返回它。然后让RAII在变量超出范围时为您清理内存。

myOptions packageOptions() {
    myOptions options;
    options.myInt = 42;
    options.myString = "hello world";

    return options;
}

int main() {
    myOptions options = packageOptions();

    std::cout << options.myString << std::endl;
    std::cout << options.myInt << std::endl;
}

答案 1 :(得分:1)

delete关键字应该只出现在智能指针类的实现中。您可以按照网络建议的值返回,或者在不理想的情况下返回(例如,按值返回会导致派生类型的切片),您可以返回std::unique_ptr并将其存储在相同的局部变量中类型,当指针超出范围时,析构函数将自动清理对象及其内存。

对每个案例“做一个类构造函数/析构函数”都是过度的。只需利用现有的高度可重用的智能指针。

答案 2 :(得分:0)

使用shared_ptr不是您目前的最佳选择。我给出了这个例子来向你展示它的存在,然后你不必费心去掉对象的删除时间。每当对象没有引用时,shared_ptr将调用析构函数/删除,即在示例中main的末尾。 (也是在C ++ 11中引入的shared_ptr,在C ++ 03中不可用)

#include <string>
#include <iostream>
#include <memory>

struct myOptions {
    int myInt;
    std::string myString;
};

using OptionsPtr = std::shared_ptr<myOptions>;

OptionsPtr packageOptions() {
    OptionsPtr options = std::make_shared<myOptions>();
    options->myInt = 42;
    options->myString = "hello world";

    return options;
}

int main() {
    OptionsPtr options = packageOptions();

    std::cout << options->myString << std::endl;
    std::cout << options->myInt << std::endl;
}

无论如何,在你的情况下,堆栈分配,比如@Cyber​​答案,更可取。

答案 3 :(得分:0)

从严格的技术角度来看,你所做的很好。

但是你问过new 正确的用法是什么[在C ++中] - 答案可能让你感到惊讶。

使用new的最佳方式是delete也是如此。您应该使用new等智能指针以及随附的delete,而不是使用std::shared_ptr / make_shared

我并不是说没有例外,但这些例外情况会很不寻常,通常是设计可能会被修改的结果。

事实上,我还会提出另一个问题:动态分配的正确使用是什么?同样,我建议的答案是不要使用动态分配。显然这也有例外,实际上比上面的“不使用新”指南更多例外 - 但随着你对C ++语言的语法和语义越来越有经验,你会发现动态分配在较少的情况下需要。

答案 4 :(得分:0)

不要以这种方式使用结构,而是尝试使用类和构造函数/析构函数。

答案 5 :(得分:0)

就管理内存而言,您所演示的内容是正确的。但从风格上来说,从维护的角度来看,它可能会好得多。

两种易于维护的方法是:

  1. 使用智能指针
  2. 重新设计代码,以便将结构传递给optionsPackage,允许函数填充传入的结构,并调用以担心结构的生命周期和使用情况。
  3. 创建了智能指针以帮助防止内存泄漏,这在大型项目中可能是一个长期的痛苦。查看std::shared_ptr或查看共享指针建议的答案。

    传入实际结构允许调用者担心使用的内存空间,包括分配和释放它。在智能指针出现之前,这是首选方法,并且仍然是经验法则(让需要数据的代码管理与之关联的数据对象)。

    传入实际结构会使代码看起来像这样:

    #include <string>
    #include <iostream>
    
    struct myOptions {
        int myInt;
        std::string myString;
    };
    
    
    void packageOptions( myOptions& theInputOptions) {
        theInputOptions.myInt = 42;
        theInputOptions.myString = "hello world";
    }
    
    
    int main() {
        myOptions options;
        packageOptions( options );
    
        std::cout << options.myString << std::endl;
        std::cout << options.myInt << std::endl;
    
    }
    

    我发现这种风格更容易维护代码。它可以在需要时与智能指针结合使用。