【问题标题】:Smart pointers & destructor智能指针和析构函数
【发布时间】:2012-04-17 12:41:49
【问题描述】:

我想知道当我不再使用原始指针时是否需要在类中编写析构函数?只需提升智能指针。

【问题讨论】:

  • 也许@simchona 知道一些我不知道的事情,但智能指针并不能减轻清理的需要,它们只是改变了清理的方式(时间)。
  • 我正在阅读这个问题,因为在几乎所有情况下,提升智能指针本身是否都不需要编写析构函数?对吗?
  • @是的,如果你有一个 deep_pointer(或其他任何名称),那么你不需要赋值/移动/析构函数/构造函数。 (虽然构造函数有时仍然很方便)

标签: class boost destructor smart-pointers


【解决方案1】:

Boost 智能指针本身与对析构函数的需求没有任何关系。他们所做的只是消除您对他们有效管理的已分配内存调用 delete 的需要。话虽如此,如果在您开始使用智能指针之前,您在析构函数中的所有操作都是调用 delete 和 delete[] 以释放动态分配的类成员的内存,并且您现在已将所有这些常规指针切换为智能指针,您可以可能只是切换到一个空的析构函数,因为它们现在会在超出范围时自行清理。

但是,如果出于某种原因,您有一个需要进行清理的类(文件清理、套接字、其他资源等),您仍然需要提供一个析构函数来执行此操作。

如果有帮助,请告诉我。

【讨论】:

  • 所有这些东西都可以通过将它们包装在 RAII 类中来清理,例如 smart_ptr。在 C++11 中,类析构函数应该很少见。
  • @MooingDuck 你是在建议空析构函数,还是完全删除它们?最好有一个非用户定义的析构函数,对吧?
  • @MaxBarraclough:对。析构函数是一个绝对令人惊叹的概念,它们是如此令人惊叹,以至于开发人员几乎不必编写一个。他们应该总是使用默认的析构函数。见The Rule of Zero
【解决方案2】:

每种资源类型都应该有一个 RAII 类来管理该资源。如果您还有一个具有深拷贝语义的智能指针(很容易做到),那么您在 99.9% 的时间里都需要管理您的资源。不知道为什么unique_ptr不做深拷贝,也不做任何boost智能指针,但是如果你有这两个东西,你就不需要写拷贝构造函数,移动构造函数,赋值操作符,移动赋值操作符,也不析构函数。您可能需要也可能不需要提供其他构造函数(包括默认构造函数),但这样会少 5 个出错的地方。

#include <memory>

template<class Type, class Del = std::default_delete<Type> >
class deep_ptr : public std::unique_ptr<Type, Del> {
public: 
     typedef std::unique_ptr<Type, Del> base;
     typedef typename base::element_type element_type;
     typedef typename base::deleter_type deleter_type;
     typedef typename base::pointer pointer;

     deep_ptr() : base() {}
     //deep_ptr(std::nullptr_t p) : base(p) {}  //GCC no has nullptr_t?
     explicit deep_ptr(pointer p) : base() {}
     deep_ptr(pointer p, const typename std::remove_reference<Del>::type &d) : base(p, d) {} //I faked this, it isn't quite right
    deep_ptr(pointer p, typename std::remove_reference<Del>::type&& d): base(p, d) {}
    deep_ptr(const deep_ptr& rhs) : base(new Type(*rhs)) {}
    template<class Type2, class Del2>
    deep_ptr(const deep_ptr<Type2, Del2>& rhs) : base(new Type(*rhs)) {}
    deep_ptr(deep_ptr&& rhs) : base(std::move(rhs)) {}
    template<class Type2, class Del2>
    deep_ptr(deep_ptr<Type2, Del2>&& rhs) : base(std::move(rhs)) {}

    deep_ptr& operator=(const deep_ptr& rhs) {base::reset(new Type(*rhs)); return *this;}
    template<class Type2, class Del2>
    deep_ptr& operator=(const deep_ptr<Type2, Del2>& rhs) {base::reset(new Type(*rhs)); return *this;}
    deep_ptr& operator=(deep_ptr&& rhs) {base::reset(rhs.release()); return *this;}
    template<class Type2, class Del2>
    deep_ptr& operator=(deep_ptr<Type2, Del2>&& rhs) {base::reset(rhs.release()); return *this;}
    void swap(deep_ptr& rhs) {base::swap(rhs.ptr);}
    friend void swap(deep_ptr& lhs, deep_ptr& rhs) {lhs.swap(rhs.ptr);}
};

有了这个类(或类似的),你根本不需要太多!

struct dog {
   deep_ptr<std::string> name;
};

int main() {
    dog first; //default construct a dog
    first.name.reset(new std::string("Fred"));
    dog second(first); //copy construct a dog
    std::cout << *first.name << ' ' << *second.name << '\n';
    second.name->at(3) = 'o';
    std::cout << *first.name << ' ' << *second.name << '\n';
    second = first; //assign a dog
    std::cout << *first.name << ' ' << *second.name << '\n';
}

正如http://ideone.com/Kdhj8所展示的那样

【讨论】:

    【解决方案3】:

    您应该始终考虑提供析构函数。您将使用它来释放您的班级持有的任何资源。我的 smart_ptr 类析构函数通常是空的,但并非总是如此。文件流、数据库连接等都需要适当的清理。

    【讨论】:

    • 所有这些东西都可以通过将它们包装在 RAII 类中来清理,例如 smart_ptr。在 C++11 中,类析构函数应该很少见。
    • @Mooing 不开玩笑。智能指针/RAII 大大减少了我的代码,而且我的析构函数通常是空的。它们需要一段时间才能习惯,但它们确实值得学习。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 2018-02-02
    • 2016-03-20
    • 1970-01-01
    • 2018-05-13
    • 2016-10-13
    • 1970-01-01
    相关资源
    最近更新 更多