使用 STL 在共享指针中调用两次析构函数

Posted

技术标签:

【中文标题】使用 STL 在共享指针中调用两次析构函数【英文标题】:Destructor called twice in Shared Pointer with STL's 【发布时间】:2018-01-13 19:06:39 【问题描述】:
// I developed my own shared pointer . I know it is not required to create 
//ur own when already there is a tested one available . But this is purely 
//for learning, understanding and thinking purpose . The below shared 
//pointer works fine in most of the cases and not causing any memory leak 
//either . But it is not working in case of STL's . Below is my shared 
//pointer .

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;

template <class T>
class SharedPointer


    template<class U>
    friend class SharedPointer;

    T *p;
    int *rc;

    public:

    SharedPointer():p(NULL),rc(NULL)   

    template <class U>
    explicit SharedPointer(U *q)
    
        p = static_cast<T *>(q);
        rc = new int();
        ++*rc;
    

    ~SharedPointer()
    
        if(rc != NULL && --*rc == 0)
        
             delete p;
             p = NULL;
             delete rc;
             rc = NULL;
        
    

    T * operator -> ()  return p; 
    T & operator * ()  return *p; 

    template<class U>
    explicit SharedPointer(const SharedPointer<U> &q)
    
        if(q.p)
        
           p = static_cast<U *>(q.get());
           rc = q.rc;
           ++*rc;
        
    

    template<class U>
    SharedPointer<T> & operator = (const SharedPointer<U> &sp)
    
        if(this->p != static_cast<T *>(const_cast<U *>(sp.get())))
        
            if(p != NULL)
            
                if(--*rc == 0)
                
                    delete p;
                    delete rc;
                    p = NULL;
                    rc = NULL;
                
            
            if(sp.get())
            
                p = static_cast<T *>(sp.get());
                rc = sp.rc;
                ++*rc;
            
            else
            
                p = NULL;
                rc = NULL;
            
        
        return *this;
    
;
// And now below is the usage .
class A

    int i;
    public:
    A(int pi):i(pi)  
    ~A() 
    
        cout<<"Destructor Called for "<<i<<endl;
    
    void show()
    
        cout<<i<<endl;
    
    int intake()     return i;   
;
struct A_Comp

    bool operator()(const SharedPointer<A> &lhs, const SharedPointer<A> &rhs)
    
        return lhs->intake() < rhs->intake();
    
;
int main()

    set<SharedPointer<A>,A_Comp> myset;
    A * a = new A(30);
    myset.insert(SharedPointer<A>(a));   //   Line X
    set<SharedPointer<A> >::iterator itr;
    for(itr = myset.begin(); itr != myset.end(); itr++)
    
       (*itr)->show();
    
    return 0;

输出:

调用 30 的析构函数

4151280

实际上发生的事情是,X 行(参见 cmets)正在执行,它正在调用显式 SharedPointer(U *q),这与预期的一样,但是当此构造函数完成时,将调用 SharedPointer 的析构函数正在删除该变量。因此,即将到来的第二行是垃圾值,在不同的执行中可能会有所不同。我的问题是为什么 ~SharedPointer 应该在 main function 的 return 语句之前执行时才被调用。虽然 ~SharedPointer 在 main 返回之前被调用,但我无法弄清楚为什么它第一次被调用。标准的 shared_ptr 没有这种行为。

我正在使用 -fpermissive 选项来避免与 const 相关的错误。

【问题讨论】:

你能把它减少到minimal reproducible example吗? @R Sahu,我删除了额外的功能。谢谢。 @manni ,我使用了调试器,只有在 SharedPointer(U *p) 构造函数完成时才知道 ~SharedPointer 被调用。但无法弄清楚为什么该对象在 main 的 return 语句之前不会超出范围。 要使 shared_ptr 工作,您需要将指针保存到 shared_ptr 本身以外的对象上,如果 shared_ptr 拥有指针,您将遇到复制和赋值操作问题,其中一个shared_ptr(甚至是临时的)超出范围并被销毁,并带上指针。 【参考方案1】:

SharedPointer 具有隐式编译器生成的复制构造函数和复制赋值运算符,它们执行简单的成员复制并且不维护引用计数。在Line X 调用的正是这个复制构造函数(在模板化构造函数中放置一个断点或打印一些东西——你会发现它从未被调用过)。

模板构造函数永远不是复制构造函数,因此不会阻止隐式声明的复制构造函数。你需要添加SharedPointer(const SharedPointer&amp;)构造函数。

【讨论】:

以上是关于使用 STL 在共享指针中调用两次析构函数的主要内容,如果未能解决你的问题,请参考以下文章

018-智能指针

构造函数 析构函数

析构函数调用线

为多态基类声明一个虚析构函数

在不调用析构函数的情况下结束 STL 容器的生命周期

C++ 堆栈分配对象,显式析构函数调用