1

I'm building a Java-like ArrayList class in C++ just for practice (yes, I know std::vector and other great alternatives exist).

I understand that the new keyword is paired with the delete keyword and malloc, calloc, realloc is paired with free.

So, let's say I have a class ArrayList that contains pointers to some generic type T. In other words, the underlying array in my ArrayList looks like this

T* array where T is some template <typename T>.

I'm defining my destructor for ArrayList and I have a few questions..

  1. The destructor should be responsible for freeing all the memory stored in the ArrayList. But I don't know if the T*s that it's holding were created using a new or a malloc-type, so how do I dealloc them? I feel like running into this problem is a sign of an anti-pattern, so any advice would be appreciated!

  2. The ArrayList itself can be instantiated in two ways

On the stack...

ArrayList arr;

or on the heap...

ArrayList arr = new ArrayList();

If it was initialized on the stack, I don't have to worry about managing arr's memory, but can I assume that its destructor will be called before the arr itself is somehow deallocated?

If it was initialized on the heap, does calling delete arr call its destructor and deallocate arr itself?

Thank you!

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
Carpetfizz
  • 8,707
  • 22
  • 85
  • 146
  • 1) Why are you writing `ArrayList`, if `std::vector` is exactly that? 2) Typically, container classes manage their own memory, hence you know how memory for `array` was allocated. Hint: you should avoid using `malloc` where possible, due to the fact, that it doesn't invoke constructors, where `new` does. 3) Typically, classes shouldn't care how they were allocated. It changes nothing for class internal structure. – Algirdas Preidžius Aug 22 '18 at 17:33
  • 1
    `malloc`/`free` don't construct or destruct objects and are not appropriate for most class types. `new`/`delete` does. – François Andrieux Aug 22 '18 at 17:34
  • As mentioned in the original post, I'm doing it for practice. I don't expect `malloc` to invoke constructors, I'm assuming that the `ArrayList`, will store pointers to pre-constructed objects/primitives. – Carpetfizz Aug 22 '18 at 17:35
  • Yes, that's why the class interface doesn't use `malloc/free`, I'm using it internally to build a resizable array class. – Carpetfizz Aug 22 '18 at 17:36
  • *deconstructor* -- The term is *destructor*, not *deconstructor*. – PaulMcKenzie Aug 22 '18 at 17:37
  • @Carpetfizz There's no reason to use `malloc` to create arrays, `new` is perfectly capable of it as well. – François Andrieux Aug 22 '18 at 17:37
  • cant you just do a for loop and then `delete arraylist[i]` ? – Chad K Aug 22 '18 at 17:40
  • @ChadK sure, but that's only assuming that the `T*` point to an object created using the `new` keyword. What if I'm storing pointers to blocks that were `malloc`'d? – Carpetfizz Aug 22 '18 at 17:41
  • 1
    @Carpetfizz *But I don't know if the T that it's holding were created using a new or a malloc-type, so how do I dealloc them?* -- You don't know, unless the client tells you how to deallocate. The client could have just stored an address of a non-dynamically created object, or an object created with an operating system API call (i.e. LocalAlloc from Windows). – PaulMcKenzie Aug 22 '18 at 17:42
  • @PaulMcKenzie would you suggest that it's good design to restrict the usage of `ArrayList` to objects that were created using the `new` keyword? What are they called? What about primitives? – Carpetfizz Aug 22 '18 at 17:43
  • 1
    @Carpetfizz -- You can do what the smart pointer classes `std::shared_ptr` and `std::unique_ptr` do. Assume that the "deleter" is `delete`, and have your class have a custom deleter supplied by the client. [See here](https://stackoverflow.com/questions/12340810/using-custom-deleter-with-stdshared-ptr) – PaulMcKenzie Aug 22 '18 at 17:45
  • Do you mind linking to how to accomplish the last part with the template? – Carpetfizz Aug 22 '18 at 17:47

1 Answers1

1

The deconstructor should be responsible for freeing all the memory stored in the ArrayList. But I don't know if the T*s that it's holding were created using a new or a malloc-type, so how do I dealloc them? I feel like running into this problem is a sign of an anti-pattern, so any advice would be appreciated!

You should not be taking in any T*s. You are correct that you have no way to determine how such pointers should be freed. You should handle the allocations yourself (as std::vector does), in which case you no longer have this problem.

If it was initialized on the stack, I don't have to worry about managing arr's memory, but can I assume that its deconstructor will be called before the arr itself is somehow deallocated?

Yes. When a local variable goes out of scope, the object (if any) is destroyed. This involves calling its destructor (again, if any).

If it was initialized on the heap, does calling delete arr call its deconstructor and deallocate arr itself?

Yes. delete arr calls arr's destructor (again, if any) and frees the reserved memory.

  • Thank you for clearing the confusion! I have a question about your first point. What do you mean by handing the allocations myself? A user is constructing the objects however they wish and just giving it to `ArrayList` to store. – Carpetfizz Aug 22 '18 at 17:40
  • 1
    @Carpetfizz If objects are being given to `ArrayList` then you need to store them by copy constructing them into the `ArrayList` .The `ArrayList` is now responsible for deleting the copies. – Richard Critten Aug 22 '18 at 17:52
  • @RichardCritten gotcha thanks! Is this what the standard libraries do (in Java and std::vector)? – Carpetfizz Aug 22 '18 at 17:53
  • 1
    @Carpetfizz That is how `std::vector` works. Java however has a completely different mechanism based on every object being a reference (with reference counting) and storing the reference in the container. and using garbage collection in-place of explicit destruction. – Richard Critten Aug 22 '18 at 17:56
  • @RichardCritten thanks again. So if someone stores objects in std::vector and destroys std::vector they should expect to manually delete all the objects they ended up storing in std::vector right? – Carpetfizz Aug 22 '18 at 17:57
  • 1
    @Carpetfizz If you have a `std::vector`, then the vector will take care of destroying any `Foo` objects in its destructor. If you have a `std::vector`, then when that vector is destroyed, the pointed-to `Foo` objects will not be. –  Aug 22 '18 at 18:01
  • 1
    @Carpetfizz last comment (cos I think you need to read a book). Objects are put into a `std::vector` by copying them. The `std::vector` is responsible for deleting these copies. What happens to the originals is the responsibility of the caller. Book list: https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282 – Richard Critten Aug 22 '18 at 18:01
  • @RichardCritten FWIW, Java does not use reference counting (or at least not exclusively). Reference counting cannot handle circular references, but Java does handle them. –  Aug 22 '18 at 18:03
  • @hvd I had a few lines to describe the differences in memory model between Java and C++ and how this affects the containers in each language. Please cut me some slack. – Richard Critten Aug 22 '18 at 18:07