首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >可能的内存泄漏与智能指针

可能的内存泄漏与智能指针
EN

Stack Overflow用户
提问于 2016-07-11 01:35:02
回答 3查看 8.9K关注 0票数 15

我在C++社区已经有一段时间了,听说原始指针“是邪恶的”,应该尽可能地避免它们。与原始指针相比,使用智能指针的主要原因之一是“防止”内存泄漏。因此,我的问题是:即使在使用智能指针时,是否仍有可能发生内存泄漏?如果是的话,那怎么可能?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-07-11 01:57:31

即使在使用智能指针时,是否仍有可能发生内存泄漏?

是的,如果您不小心避免在引用中创建一个循环。

如果是的话,那怎么可能?

当与对象关联的引用计数降到零时,基于引用计数的智能指针(如shared_ptr)将删除指向对象。但是,如果引用中有一个循环(A->B->A,或者更复杂的循环),那么循环中的引用数永远不会降到零,因为智能指针“保持对方活着”。

下面是一个简单程序的示例,尽管它只使用shared_ptr作为指针,但却会泄漏内存。请注意,当您运行它时,构造函数会打印一条消息,但析构函数不会这样做:

代码语言:javascript
复制
#include <stdio.h>
#include <memory>

using namespace std;

class C
{
public:
   C() {printf("Constructor for C:  this=%p\n", this);}
   ~C() {printf("Destructor for C:  this=%p\n", this);}

   void setSharedPointer(shared_ptr<C> p) {pC = p;}

private:
   shared_ptr<C> pC;
};

int main(int argc, char ** argv)
{
   shared_ptr<C> pC(new C);
   shared_ptr<C> pD(new C);

   pC->setSharedPointer(pD);
   pD->setSharedPointer(pC);

   return 0;
}
票数 21
EN

Stack Overflow用户

发布于 2018-07-19 01:01:57

除了具有循环引用之外,泄漏智能指针的另一种方法是做一些看起来相当天真的事情:

代码语言:javascript
复制
processThing(std::shared_ptr<MyThing>(new MyThing()), get_num_samples());

熟悉C++的人可能会认为函数参数是从左到右计算的。这是一件自然的事情,但不幸的是,它是错误的(RIP直觉和最小惊讶原则)。事实上,只有clang保证从左到右函数参数计算(AFAIK,也许它不是一个保证)。大多数其他编译器从右到左进行计算(包括gccicc)。

但是,无论任何特定的编译器做什么,C++语言标准(除了C++17,详见end )并不规定计算参数的顺序,所以编译器完全有可能按任何顺序计算函数参数。

来自cppreference:

几乎所有C++运算符的运算数的求值顺序(包括函数调用表达式中函数参数的求值顺序和任何表达式中子表达式的求值顺序)都是未指定的。编译器可以按任意顺序计算操作数,并可以在再次计算相同表达式时选择另一个顺序。

因此,完全有可能按以下顺序计算上面的processThing函数参数:

  1. new MyThing()
  2. get_num_samples()
  3. std::shared_ptr<MyThing>()

这可能会导致泄漏,因为get_num_samples()可能引发异常,因此可能永远不会调用std::shared_ptr<MyThing>()。强调五月。根据语言规范,这是可能的,但实际上我还没有看到任何编译器进行这种转换(诚然,gcc/icc/clang是我在编写本报告时使用的唯一编译器)。我没能强迫gcc或克莱尔去做这件事(经过大约一个小时的努力/研究,我放弃了)。也许编译器专家可以给我们举一个更好的例子(如果您正在阅读这篇文章,并且是编译器专家!)

这里有一个玩具例子,我用gcc来强迫这个订单。我欺骗了一些人,因为事实证明很难强迫gcc编译器任意地重新排序参数计算(它看起来还是很无辜的,它确实泄露了一些消息给stderr):

代码语言:javascript
复制
#include <iostream>
#include <stdexcept>
#include <memory>

struct MyThing {
    MyThing() { std::cerr << "CONSTRUCTOR CALLED." << std::endl; }
    ~MyThing() { std::cerr << "DESTRUCTOR CALLED." << std::endl; }
};

void processThing(std::shared_ptr<MyThing> thing, int num_samples) {
    // Doesn't matter what happens here                                                                                                                                                                     
}

int get_num_samples() {
    throw std::runtime_error("Can't get the number of samples for some reason...and I've decided to bomb.");
    return 0;
}

int main() {
    try {
        auto thing = new MyThing();
        processThing(std::shared_ptr<MyThing>(thing), get_num_samples());
    }
    catch (...) {
    }
}

用gcc 4.9编写,MacOS:

代码语言:javascript
复制
Matthews-MacBook-Pro:stackoverflow matt$ g++ --version
g++-4.9 (Homebrew GCC 4.9.4_1) 4.9.4
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Matthews-MacBook-Pro:stackoverflow matt$ g++ -std=c++14 -o test.out test.cpp
Matthews-MacBook-Pro:stackoverflow matt$ ./test.out 
CONSTRUCTOR CALLED.
Matthews-MacBook-Pro:stackoverflow matt$

请注意,DESTRUCTOR CALLED从未打印到stderr。

您可以通过确保您有一个用于创建shared_ptr的不同语句来解决此问题,然后将该结果传递给一个函数。这是因为编译器在不同语句之间没有(很大的)自由度(相对于同一语句)。下面是如何修复上面的玩具示例:

代码语言:javascript
复制
// ensures entire shared_ptr allocation statement is executed before get_num_samples()
auto memory_related_arg = std::shared_ptr<MyThing>(new MyThing());
processThing(memory_related_arg, get_num_samples());

这都是被盗的“有效C++",第三版,由斯科特迈尔斯。如果你每天使用C++,那绝对是一本值得一读的书。C++是很难得到正确的,这本书做了一个很好的工作,提供了良好的指导方针,如何使它更正确。您仍然可能会错误地遵循指导方针,但您将是一个更好的C++开发人员,了解本书中的策略。

P.S.S. C++17解决了这个问题。详见此处:What are the evaluation order guarantees introduced by C++17?

票数 9
EN

Stack Overflow用户

发布于 2016-07-11 01:39:30

有一些函数可以从智能指针中释放内存。在这种情况下,您要求智能指针停止管理内存。在那之后,你就不能泄露记忆了

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38298008

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档