【问题标题】:smart pointers and constructor exceptions [duplicate]智能指针和构造函数异常[重复]
【发布时间】:2016-10-13 18:03:25
【问题描述】:

我读到智能指针在构造函数产生一些异常的情况下很有帮助。

问题是构造函数在生成异常之前获得了一些资源,但没有调用析构函数(并且资源永久繁忙)。

但我无法正确理解它。 我的代码:

#include <memory>
#include <iostream>

class resOwner {
    public:
    resOwner() {
        std::cout << "Map some huge resources\n";
        throw "hi";
    }

    ~resOwner() {
        std::cout << "Free some huge resources\n";
    }
};

class normal : resOwner {
};

int main (){
    try {
        std::shared_ptr<resOwner> k (new resOwner());
    } catch (...) {}
}

输出为Map some huge resources。 如何用智能指针解决这种资源泄漏?

【问题讨论】:

  • 别担心 - 如果 c-tor 抛出,内存会被释放 - 没有智能指针的合作 - 请参阅 stackoverflow.com/questions/1674980/…
  • resOwner 拥有的任何资源都必须是某种智能指针 unique_ptrshared_ptr 等。否则您将最终导致资源泄漏,因为您计划在析构函数中清理的任何内容都不会被调用。
  • 值得指出的是,如果单个构造函数运行完成(构造函数委托)并且委托构造函数抛出,则将调用对象的析构函数。

标签: c++ memory-leaks shared-ptr smart-pointers resource-leak


【解决方案1】:

如何用智能指针解决这个资源泄露问题?

将资源包装在 RAII 容器中,并将容器存储为 resOwner 的成员。您不一定需要智能指针。

struct resource {
    resource() {
        std::cout << "Map some huge resources\n";
    }
    ~resource() {
        std::cout << "Free some huge resources\n";
    }
};

struct resOwner {
    resource res;
    resOwner() {
        throw "hi";
    }
};

如果那个巨大的资源是一个动态分配的对象,那么你不需要实现一个单独的resource 类,因为标准库已经有了一个容器:std::unique_ptr。如果是动态分配的数组,那么可以使用std::vector

其工作方式:当构造函数体或子对象构造函数之一抛出时,所有已经构造的子对象(成员和基础对象)都将被销毁。这保证了~resource 将被调用。

【讨论】:

    【解决方案2】:

    您应该对资源使用智能指针,它们将帮助您避免资源泄漏。如:

    class resource {
    public:
        resource() {
            std::cout << "resource allocated\n";
        }
        ~resource() {
            std::cout << "resource deallocated\n";
        }
    };
    
    class resOwner {
        std::shared_ptr<resource> res;
    public:
        resOwner() : res(new resource) {
            std::cout << "Map some huge resources\n";
            throw "hi";
            // res will be destroyed even though exception happened
        }
        ~resOwner() {
            // nothing need to do here
        }
    };
    

    LIVE

    【讨论】:

      【解决方案3】:

      在实际创建对象之前抛出异常。由于没有创建对象,因此不会调用其析构函数。

      另一方面,如果在构造函数中抛出异常,对于构造函数完成执行的所有子对象,将调用析构函数。试试这个进行比较:

      class Resource
      {
      public:
          Resource()
          {
              std::cout << "constructing resource" << std::endl;
          }
          ~Resource()
          {
              std::cout << "destroying resource" << std::endl;
          }
      };
      
      class Owner
      {
          ::std::unique_ptr <Resource> theResource;
      public:
          Owner()
                  : theResource(new Resource())
          {
              std::cout << "Map some huge resources\n";
              throw "hi";
          }
      
          ~Owner()
          {
              std::cout << "Free some huge resources\n";
          }
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-17
        • 1970-01-01
        • 1970-01-01
        • 2017-07-04
        • 2010-10-08
        • 2012-02-10
        • 1970-01-01
        相关资源
        最近更新 更多