Search code examples
c++c++11memory-managementshared-ptrvoid-pointers

shared pointer behavior when container object passed as (void*)


I have shared_ptr variable in my class object (ObjA). There is a requirement where this object is to be stored as (void*) entity of another Class' object (ObjB).

My question is, what will be the behaviour of shared_ptr (will associated heap memory be freed?, what will happen to its reference count?)-

  1. when ObjA is converted to void*

  2. when void* entity of ObjB is cast back to (ClassA *)

Simplified Code:

Class AA{

    shared_ptr<int> aptr;

    public:
        AA(){
            aptr = make_shared<int>(100);
        }
        shared_ptr<int> get_aptr(){
            return aptr;
        }
};


Class BB{

    void *smpl;

    public:

        void setter(void* p){
            smpl = p;
        }

        void* getter(){
            return smpl;
        }
};

int main(){

    AA *objA = new AA();
    BB *objB = new BB();

    objB->setter(objA);
    //status of allocated int in class AA here?

    //some code later
    AA *objA_2 = (AA*)objB->getter();
    //status of allocated int in class AA here?

    shared_ptr<int> p2 = objA_2->get_aptr();
    //is this allowed

}

My prime concern is- how to deallocate the memory of shared_ptr. (Given it is enclosed in void* object, which can not be deleted- shared_ptr remains in scope at all times) I tried to delete objB, with appropriate casting to its component void* in destructor-

BB::~BB(){
    delete (*AA)smpl;
}

but this is giving error as shared_ptr variable in class AA does not allows explicit delete- it frees its allocated area only when it goes out of scope. (In this case, it never knows when it goes out of scope!)

Also, class BB is a third party class- I cant modify the type void* of smpl member variable (_userData variable in Node class, in Cocos2dx). Please help to resolve this memory leak..


Solution

  • The void * here acts an observing pointer. It does not affect the shared pointer at all, because the shared pointer doesn't know the raw pointer to its contained object even exists.

    That would be different if you choose a shared_ptr<void> instead of the raw pointer (and construct it through the original shared pointer, i.e. your setter should be replaced by void setter(std::shared_ptr<void> const& p) for example).

    In this case, the reference count is affected and the living of the pointed-to object would be ensured. With regard to this option, see here.


    EDIT: From your comment, it seems as if you're looking for a setup similarly to the following:

    struct AA
    {
        //...
    };
    
    
    struct BB
    {
        std::shared_ptr<void> smpl;
    
        void setter(std::shared_ptr<void> const& p)
        {
            smpl = p;  //now the ref count of your shared_tr to AA is increased
        }
    };
    
    int main()
    {    
        auto objA = std::make_shared<AA>();
        auto objB = std::make_shared<BB>();
    
        objB->setter(objA);
    
        std::cout<<objA.use_count()<<std::endl;   //prints "2"
    }
    

    DEMO

    The reference count after calling the setter is 2. The shared_ptr<void> acts like a normal shared-pointer in that it keeps the AA-object alive, or destroys it object properly if the pointer goes out of scope (and is itself the last one ointing to the given AA).

    That works, because the deleter of the original shared-pointer has been copied which knows who to do the destruction.


    EDIT2: To answer the questions in the code of the OP:

    objB->setter(objA);
    //status of allocated int in class AA here?
    

    The shared-pointer inside AA is not affected at all.

    //some code later
    AA *objA_2 = (AA*)objB->getter();
    //status of allocated int in class AA here?
    

    Again, the shared-pointer is unaffected. This step, however, required care: if the original object is not alive anymore, you'll get a dangling pointer. Thus, better use a shared-pointer.

    shared_ptr<int> p2 = objA_2->get_aptr();
    //is this allowed
    

    If the pointer is valid, of course.

    One thing I didn't get is why you use smart-pointers inside the class, but raw pointers outside. Is there a reason for that?