智能指针和析构函数

10

我想知道当我不再使用裸指针,只使用Boost智能指针时,是否需要在类中编写析构函数?


也许@simchona知道我不知道的东西,但智能指针并不能消除清理的需要,它们只是改变了清理发生的方式(时间)。 - user645280
2
我理解这个问题是在问,Boost智能指针本身是否在几乎所有情况下都消除了编写析构函数的需要?这样理解正确吗? - gymbrall
如果你有一个深度指针(或称为什么),那么你就不需要赋值/移动/析构/构造函数。(尽管构造函数有时仍然很方便) - Mooing Duck
3个回答

14
Boost智能指针本身与需要析构函数无关,它们所做的只是消除您在有效管理的分配内存时需要调用delete的需要。因此,如果在开始使用智能指针之前,您的析构函数中只有对delete和delete[]的调用来释放动态分配的类成员的内存,而现在您已经将所有这些常规指针转换为智能指针,您可能只需切换到一个空析构函数,因为它们在超出范围时会自行清理。
但是,如果由于某种原因,您的类需要进行清理(文件清理,套接字,其他资源等),则仍然需要提供析构函数来完成此操作。
请让我知道这是否有帮助。

1
所有这些事情都可以通过将它们封装在 RAII 类中(例如 smart_ptr)来进行清理。使用 C++11,类析构函数应该很少见。 - Mooing Duck
@MooingDuck你是在建议空析构函数,还是完全去除它们?有一个非用户定义的析构函数会更好,不是吗? - Max Barraclough
1
@MaxBarraclough:没错,析构函数是一个绝对令人惊叹的概念,而且它们如此神奇,以至于开发人员几乎不必编写一个。他们应该始终使用默认的析构函数。请参见《零规则》(The Rule of Zero)。(https://web.archive.org/web/20121127171954/http://rmartinho.github.com/cxx11/2012/08/15/rule-of-zero.html) - Mooing Duck

4
每种资源类型都应该有一个RAII类来管理该资源。如果您还具有具有深度复制语义的智能指针(相当容易实现),那么您在99.9%的情况下就可以管理您的资源了。我不知道为什么unique_ptr不执行深度复制,也不知道任何boost智能指针,但如果您拥有这两个东西,您就不需要编写复制构造函数、移动构造函数、赋值运算符、移动赋值运算符或析构函数。您可能需要提供其他构造函数(包括默认构造函数),但这是少犯错误的五个地方。
#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上演示的那样,


-2

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


1
所有这些东西都可以通过将它们包装在一个RAII类中(例如smart_ptr)来清理。使用C++11,类析构函数应该很少使用。 - Mooing Duck
1
@Mooing 不开玩笑。智能指针/RAII 让我的代码大大减少,我的析构函数通常是空的。它们需要一段时间来适应,但学习它们绝对是值得的。 - Aura

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接