Pregunta

I was trying to write a sample code for implementing shared pointer [just for practice]. In this following example,

  • why compiler is not complaining about modifying other_T
  • And why copy constructor SharedPtr(const T& other_T) is not getting called ? Here is the code snippet.

    #include <iostream>
    using namespace std;
    #define DBG cout<<"[DEBUG]"<<__PRETTY_FUNCTION__<<endl
    class RefCount
    {
    protected:
        int m_ref;
        RefCount(){ DBG; m_ref = 1 ; }
        void reference(){ DBG; ++m_ref; }
        void dereference(){ DBG;--m_ref;    }
    };
    
     template <class T>
     class SharedPtr : public RefCount
     {
        T* m_T;
     public:
        SharedPtr() {  DBG; m_T = new T; }
        SharedPtr(const T& other_T){
            DBG;
            m_T = other_T.m_T;
            other_T.dereference();
            other_T.m_T = NULL;
        }
        ~SharedPtr() {
            DBG;
            dereference();
           cout<<m_ref<<endl;
           if(m_ref <= 0 && m_T != NULL ){
               cout<<"Destroying"<<endl;
               delete m_T;
               m_T = NULL;
            }
        }
    };
    
    class A{};
    int main()
    {
        SharedPtr<A> obj;
        cout<<"assigning "<<endl;
        SharedPtr<A> obj2 = obj;
        cout<<"END"<<endl;
        return 0;
    }
    

and the result is segfault.

¿Fue útil?

Solución

Your primary problem is that the copy constructor is being called--but you haven't defined a copy constructor, so you're getting the copy constructor that's defined by the compiler by default.

That copy constructor just does a member-wise copy. That means you've allocated one A with new, then pointed two SharedPtr objects at that same A. The first one to get destroyed deletes the A object. Then the second one gets destroyed, attempts to delete the same object again, and havoc ensues.

In the end, it doesn't look to me like much (any?) of this is going to make any real difference though. I'm pretty sure your basic design is broken. To get a working shared pointer, you have one reference count and "raw" pointer to the final object. Then you have N SharedPtr objects referring to that one ref count/pointer structure that in turn refers to the final object.

You're trying to combine the raw pointer/ref count into the individual SharedPtr, and I don't see any way that can actually work.

It also seems to me that the basic concept of what you've called a RefCount is really part of the design of a SharedPtr. As such, I think its definition should be nested inside that of SharedPtr (and probably made private, since the outside world has no reason to know it exists, not to mention being able to access it directly).

With those taken into account, the code might end up something like this:

#include <iostream>
using namespace std;

#define DBG cout<<"[DEBUG]"<<__PRETTY_FUNCTION__<<endl

template <class T>
class SharedPtr {

    template <class U>
    struct Ref {
        mutable int m_ref;
        U *data;
        Ref(T *data) : m_ref(1), data(data) { DBG; }
        void add_ref() const { DBG; ++m_ref; std::cout << "m_ref=" << m_ref << "\n"; }
        void sub_ref() const { DBG; --m_ref; std::cout << "m_ref=" << m_ref << "\n"; }
        ~Ref() { delete data; }
    };

    Ref<T> *r;
public:
    SharedPtr(T *data) { DBG; r = new Ref<T>(data); }
    SharedPtr(SharedPtr const &p) : r(p.r) { DBG; r->add_ref(); }
    ~SharedPtr() {
        DBG;
        r->sub_ref();
        if (0 == r->m_ref) {
            delete r;
            std::cout << "deleted pointee\n";
        }
    }
};

class A{};

int main() {
    SharedPtr<A> obj(new A);
    cout<<"copying "<<endl;
    SharedPtr<A> obj2 = obj;
    cout<<"END"<<endl;
    return 0;
}

Notes: though this fixes at least some of the basic design, it's still quite a ways short of usable. It's missing the dereference operator, so you can't use the pointer to get to the value it points at. It'll break completely in a multi-threaded environment. I haven't thought enough about it to be sure, but my immediate guess is that it's probably not exception safe either.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top