How is memory deallocated by delete operator with pointer to base class
Asked Answered
D

2

5

Consider this code:

class base{
    T* obj=new T[40];
    //...
public:
    base(){/*...*/}
    virtual ~base(){
        delete[] obj;
        //...
    }
    ...
};
class derived : public base{
    T* obj2=new T[20];
    //...
public:
    derived(){/*...*/}
    ~derived(){
        delete[] obj2;
        //...
    }
    ...
};

void func(){
    base&& exmp=giveder();    //giveder() returns derived
    base* dis=new derived[50];
    //...
    delete[] dis;
}

In the above code exmp will be destructed properly as destructors are declared virtual. But my question is whether free store pointed to by dis will be deallocated as expected and if it is then how?

It is apparent that sizeof(base) and sizeof(derived) is different. But that's not gonna mess with exmp though will it mess with dis? What I think is that It will not work as there's no way to figure out the sizeof(derived) from a pointer to base and as a result it can't figure out how many bytes are needed to be freed. Although I really do want to know the language specification and whether it is legal. If that is legal, what is the workaround of freeing what is in acquisition?

A side question, arrays don't know of its own size(right?), then how are delete[] obj and delete[] obj2 in the destructors going to release memory either?

I'm new to pointer and memory management, so I'd prefer descriptive answer. Thanks

Distill answered 6/9, 2019 at 5:26 Comment(6)
The memory for all the arrays will be freed correctly. new[] stores bookkeeping info that delete[] uses when freeing memory. Virtual destructors are called correctly whether the objects are in automatic memory or dynamic memory, and the correct number of bytes are released after the destructors are calledCypriot
@RemyLebeau, the OP's code has undefined behavior.Masoretic
Why do you allocate the memory in your class prototype and not the constructor?Woolpack
Arrays don’t know their own size, but the memory manager knows because it has stored that size in a special place.Aeolipile
@Woolpack if it were real code then I'd have really allocated the memory in the constructor(and of course I might wanted the user to give the size as argument)Distill
if char A[10]; Then sizeof(A) == 10. I'm just saying...Uintathere
M
7

Your code has undefined behavior.

It's ok to use

base* ptr = new derived();
delete ptr;

but it is not ok to use

base* ptr = new derived[10];
delete [] ptr;

Here's the relevant text from the C++11 Standard (emphasis mine):

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

Masoretic answered 6/9, 2019 at 5:44 Comment(0)
W
0

As others already noticed, this:

base* dis=new derived[50];
delete[] dis;

is undefined behavior. Read, e.g, this question to understand why: Why is it undefined behavior to delete[] an array of derived objects via a base pointer?.

As a solution, I suggest to use a vector of unique pointers:

struct Base { virtual ~Base() = default; };
struct Derived : Base {
  Derived() { std::cout << "Derived constructed...\n"; }
  ~Derived() { std::cout << "Derived destroyed...\n"; }
};

int main() {
  std::vector<std::unique_ptr<Base>> v;
  std::generate_n(std::back_inserter(v), 50,
                  []{ return std::make_unique<Derived>(); });
}

Live demo: https://wandbox.org/permlink/jcG71bDb1U7wsp2T.

Of course, in this case, Derived objects will not be placed in the contiguous memory chunk.

Wonted answered 6/9, 2019 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.