编程语言
首页 > 编程语言> > 为什么C程序员应该尽量减少“新”的使用?

为什么C程序员应该尽量减少“新”的使用?

作者:互联网

我偶然发现Stack Overflow问题Memory leak with std::string when using std::list<std::string>,one of the comments说:

Stop using new so much. I can’t see any reason you used new anywhere
you did. You can create objects by value in C++ and it’s one of the
huge advantages to using the language. You do not have to allocate
everything on the heap. Stop thinking like a Java programmer.

我不太确定他的意思是什么.为什么要尽可能经常地通过C中的值创建对象,它在内部有什么区别?我误解了答案吗?

解决方法:

有两种广泛使用的内存分配技术:自动分配和动态分配.通常,每个都有一个相应的内存区域:堆栈和堆.

堆栈总是以顺序方式分配内存.它可以这样做,因为它要求您以相反的顺序释放内存(First-In,Last-Out:FILO).这是许多编程语言中局部变量的内存分配技术.它非常非常快,因为它需要最少的簿记,下一个要分配的地址是隐含的.

在C中,这称为自动存储,因为存储在范围结束时自动声明.一旦完成当前代码块(使用{}分隔)的执行,就会自动收集该块中所有变量的内存.这也是调用析构函数来清理资源的时刻.

堆允许更灵活的内存分配模式.簿记更复杂,分配更慢.因为没有隐式释放点,所以必须使用delete或delete []手动释放内存(在C中为free).但是,缺少隐式释放点是堆灵活性的关键.

使用动态分配的原因

即使使用堆较慢并且可能导致内存泄漏或内存碎片,动态分配也有很好的用例,因为它的限制较少.

使用动态分配的两个主要原因:

>您不知道在编译时需要多少内存.例如,在将文本文件读入字符串时,通常不知道文件的大小,因此在运行程序之前无法确定要分配的内存量.
>您想要分配在离开当前块后将保留的内存.例如,您可能希望编写一个返回文件内容的函数字符串readfile(字符串路径).在这种情况下,即使堆栈可以保存整个文件内容,也无法从函数返回并保留分配的内存块.

为什么动态分配通常是不必要的

在C中有一个称为析构函数的简洁结构.此机制允许您通过将资源的生命周期与变量的生命周期对齐来管理资源.这种技术称为RAII,是C的区别点.它将资源“包装”到对象中. std :: string是一个很好的例子.这个片段:

int main ( int argc, char* argv[] )
{
    std::string program(argv[0]);
}

实际上分配了可变数量的内存. std :: string对象使用堆分配内存并在其析构函数中释放它.在这种情况下,您不需要手动管理任何资源,仍然可以获得动态内存分配的好处.

特别是,它暗示在这个片段中:

int main ( int argc, char* argv[] )
{
    std::string * program = new std::string(argv[0]);  // Bad!
    delete program;
}

有不必要的动态内存分配.该程序需要更多的输入(!)并引入忘记释放内存的风险.这样做没有明显的好处.

为什么要尽可能经常使用自动存储

基本上,最后一段总结了它.尽可能经常使用自动存储使您的程序:

>更快打字;
>跑步时速度更快;
>不太容易出现内存/资源泄漏.

奖励积分

在引用的问题中,还有其他问题.特别是以下课程:

class Line {
public:
    Line();
    ~Line();
    std::string* mString;
};

Line::Line() {
    mString = new std::string("foo_bar");
}

Line::~Line() {
    delete mString;
}

实际上使用风险比以下风险更大:

class Line {
public:
    Line();
    std::string mString;
};

Line::Line() {
    mString = "foo_bar";
    // note: there is a cleaner way to write this.
}

原因是std :: string正确定义了一个拷贝构造函数.考虑以下程序:

int main ()
{
    Line l1;
    Line l2 = l1;
}

使用原始版本,该程序可能会崩溃,因为它在同一个字符串上使用delete两次.使用修改后的版本,每个Line实例将拥有自己的字符串实例,每个实例都有自己的内存,两者都将在程序结束时释放.

其他说明

由于上述所有原因,广泛使用RAII被认为是C语言中的最佳实践.但是,还有一个额外的好处并不是很明显.基本上,它比它的各个部分的总和更好.整个机制组成.它可以扩展.

如果您使用Line类作为构建块:

 class Table
 {
      Line borders[4];
 };

然后

 int main ()
 {
     Table table;
 }

分配四个std :: string实例,四个Line实例,一个Table实例和所有字符串的内容,所有内容都自动释放.

标签:c,memory-management,new-operator,heap,c-faq
来源: https://codeday.me/bug/20190910/1802512.html