Home > Mobile >  How can a class know, that it should not free its allocated memory in the destructor. Because the cl
How can a class know, that it should not free its allocated memory in the destructor. Because the cl

Time:07-14

How can a class know, that it should not free its allocated memory in the destructor. Because the class is a pointee to a pointer?

Some example code (untested) that does not work. The problem is at the constructor of MyOtherClass.

class MyClass {
    ~MyClass() { free(...); } // free some vars, does not matter what.
};
class MyOtherClass {
    MyClass *m_ptr;
    MyOtherClass(MyClass pointee) {
        m_ptr = &pointee;
        // when this constructor is finished, the destructor of parameter "pointee" will be called.
        // and therefore the attribute "m_ptr" points to nothing. 
    }
};

Could it be possible that MyClass uses something like this?

class MyClass {
    bool i_am_a_pointee = false; // but how and where to assign this?
    ~MyClass() { 
        if (!i_am_a_pointee) { free(...); }
    }
};

The goal is to create a container like class for multiple different classes. But with pointers.

Code:

class MyClassA {
    MyClassA() {}
    // here the pointer is cleared because that is required for my class. 
    // So when the var goes out of scope like in the constructor of "MyContainer(MyClassB)" all the vars will be deleted and therefore the pointer technically points to nothing.
    ~MyClassA() { free(...); } 
};
class MyClassB {
    MyClassB() {}
    // here the pointer is cleared because that is required for my class. 
    // So when the var goes out of scope like in the constructor of "MyContainer(MyClassB)" all the vars will be deleted and therefore the pointer technically points to nothing.
    ~MyClassB() { free(...); }
};
class MyContainer {
    MyClassA *m_class_a;
    MyClassB *m_class_b;
    MyContainer(MyClassA x) {
        m_class_a = &x // here the problem.
    }
    operator MyClassA() {
        return *m_class_a;
    }
    MyContainer(MyClassB x) {
        m_class_b = &x // here the problem.
    }
    operator MyClassB() {
        return *m_class_b;
    }
};
int main() {
    MyContainer container;
    MyClassA a;
    MyClassB b;
    container = a; // here the pointer is empty because of the problem.
    MyClassA a1 = container;
    container = b;
    MyClassB = container;
}

CodePudding user response:

MyOtherClass(MyClass pointee) {
    m_ptr = &pointee;
}

MyContainer(MyClassA x) {
    m_class_a = &x // here the problem.
}

Here you are taking pointee / x by value, that means the compiler will make a copy. You need to define a meaningful copy constructor for that to work with pointers.

Then you take the address of that argument, which is a local stack variable. At the end of the function that variable goes away and your pointer is dangling. No matter how correct you implement MyClassA this can never work.

You need a pointer or reference there so you can get the address of the object from the caller instead of the local variable. I would suggest smart pointers.


Never store raw pointers. There are too many ways to mess that up. When a class is the sole owner of a pointer then use unique_ptr. When you need to store the pointer in multiple places then use shared_ptr. If unsure, like when returning a pointer the user might or might not store, default to shared_ptr.

Apart from avoiding errors this also simplifies your code. Often you don't need a destructor because unique_ptr/shared_ptr handle the cleanup for you.

CodePudding user response:

How can a class know, that it should not free its allocated memory in the destructor. Because the class is a pointee to a pointer?

The short answer is that if you want to hold a raw pointer that may or may not be an owning-pointer, then you'll need to track that information somehow. As a crude approach, you could have a boolean member-variable named delete_pointer_in_destructor and set it to true iff the pointer should be deleted in the destructor, or false if it should not. If the object was allocated somewhere else in your program, then the calling code might have to pass in a value of this boolean as an argument (along with the pointer) to tell your code what it needs to do, since there would be no way for your code to know, just from the raw-pointer alone, which setting would be appropriate.

A much better and less error-prone approach, OTOH, would be to avoid raw owning-pointers altogether -- either store objects by-value only (in which case you don't ever need to call new or delete at all), or (if you can't do that, e.g. because you need polymorphic behavior) then at least use smart pointers like std::shared_ptr or std::unique_ptr as they will handle all of the decisions about when (or if) to call delete for you, and spare you the risk of getting those decisions wrong.

MyOtherClass(MyClass pointee) {
    m_ptr = &pointee;
    // when this constructor is finished, the destructor of parameter "pointee" will be called.
    // and therefore the attribute "m_ptr" points to nothing. 
}

You should try to avoid ever keeping a pointer to an object past the time the object has been destroyed; holding a dangling pointer is useless and asking for trouble, since any attempt to actually use the dangling pointer will invoke undefined behavior. So the fix for the above code would be to set m_ptr to NULL at the end of the constructor (or better yet, never set it to &pointee in the first place)

  • Related