3

I try to learn C++ and programming in general. Currently I am studying open source with help of UML. Learning is my hobby and great one too.

My understanding of memory allocation in C++ is that it should be symmetrical. A class is responsible for its resources. If memory is allocated using new it should be returned using delete in the same class. It is like in a library you, the class, are responsibility for the books you have borrowed and you return them then you are done.

This, in my mind, makes sense. It makes memory management more manageable so to speak.

So far so good. The problem is that this is not how it works in the real world.

In Qt for instance, you create QtObjects with new and then hand over the ownership of the object to Qt. In other words you create QtObjects and Qt destroys them for you. Thus unsymmetrical memory management. Obviously the people behind Qt must have a good reason for doing this. It must be beneficial in some kind of way,

My questions is:

  • What is the problem with Bjarne Stroustrups idea about a symmetrical memory management contained within a class?
  • What do you gain by splitting new and delete so you create an object and destroy it in different classes like you do in Qt.
  • Is it common to split new and delete and why in such case, in other projects not involving Qt?

Thanks for any help shedding light on this mystery!

  • 1
    In other words you create QtObjects and Qt destroys them for you I do not know about Qt, but I know that C++ does not this for you. So you must keep the code tidy in order to avoid forgetting to delete objects (memory leak) or deleting them twice (unspecified behavior). – SJuan76 Jun 11 '14 at 18:47
6

In modern C++ you typically want to use the Resource Acquisition Is Initialization (RAII) idiom. The idea is that any resource that you allocate should be wrapped into a class, and it should be deleted/closed/cleaned up by the class's destructor. A resource is anything that you need to de-allocate, close, or otherwise clean up when you are done with it. Most often a resource is memory, but it could be other things like files or network connections.

In some cases the wrapping class can be complex, representing some piece of program logic. In most cases the wrapping class can be very simple, existing solely for the purpose of de-allocating the resource at the right time in the right way. An example of that is a smart pointer.

I haven't looked at Qt in a very long time. However, letting the user allocate an object, and pass a raw pointer into a library function that later de-allocates it is a rather outdated pattern. If the Qt function throws an exception before it has a chance to delete your object, you will inevitably have a memory leak. The object should be wrapped into a smart pointer, whose destructor will delete it automagically, including the case when an exception is thrown.

  • The RAII concept still applies, it is great to hear. At least I got that part right. But considering that std::shared_ptr<> is part of C++11 the way of Qt is not outdated. Rather the opposite. – Jim G Jun 12 '14 at 13:04
  • Wait, I was saying that passing a raw pointer into a function, that would have to delete it explicitly is outdated. Passing smart pointers around is certainly not outdated. I don't know what Qt is doing these days. – Dima Jun 12 '14 at 13:08
  • I am junior so please no offence, but the shared object provided to the constructor of shared_ptr is not copied. You create the shared object, with new, then hand it over to shared_ptr. – Jim G Jun 12 '14 at 13:19
  • Oh, no offence at all. :) The shared object is allocated immediately before constructing the shared_ptr, literally in the same statement ptr = shared_ptr<MyClass>(new MyClass), and then it is deleted by the shared_ptr's destructor when necessary. I suppose you can argue that this is not symmetric memory management, but I think it is pretty close to it. – Dima Jun 12 '14 at 13:35
  • Which is the odd wobbly bit The need for new outside shared_ptr. That part puzzles me. The purpose of the copy constructor is to omit this need. Is there objects that can't be copied ? – Jim G Jun 12 '14 at 14:19
1
  • What is the problem with Bjarne Stroustrups idea about a symmetrical memory management contained within a class?

In most cases, there is no problem at all with doing symmetrical memory management. This is also what the principle of RAII is based upon.
There are, however, always situations where the class that allocated some memory isn't the last one to need to reference that memory. Sometimes this can be resolved by passing the ownership (and thus the responsibility for deletion) on to another class. Sometimes it is not even clear who will be the last to reference a piece of memory. In those cases, garbage collection techniques like reference counting come in handy.

  • What do you gain by splitting new and delete so you create an object and destroy it in different classes like you do in Qt.

You gain additional design possibilities. For example, if you have a polymorphic container, the class that puts items in it isn't required to outlive the container itself and the classes that read from it. This can be useful in implementing GUIs.

  • Is it common to split new and delete and why in such case, in other projects not involving Qt?

Given the popularity of boost::shared_ptr<> and the introduction of std::shared_ptr<> in the recent C++ update, there must be some demand for a-symmetric memory management. Both classes implement shared memory ownership based on reference counting.

  • Did not know about std::shared_ptr<>. So asymmetrical memory management is part of the C++ standard. I played with it and true, the copy constructor of the shared object is not called, thus is correct to write std::shared_ptr<Foo> m_foo(new Foo);. No leaks. The copy constructor of the delete object is called however. That is a relief. – Jim G Jun 12 '14 at 13:06
1

I would approach it from the opposite direction: why is Bjarne's position a good idea? Then we can see when it falls apart.

The primary reason for Bjarne's position of symmetrical memory management is that C++ can cause memory leaks if memory ownership is not handled properly. Bjanrne's position is based on the idea that symmetric memory management increases the likelyhood of developers managing memory properly, and minimizing leaks.

It does this by keeping memory management out of the interactions between classes. Generally speaking, function calls don't explicitly notate memory management behaviors. Those tend to be relegated to comments. Comments can be overlooked.

In the real world, it is not always convenient to do symetric memory management. For example, any data required to properly manage the memory must have a lifespan as long as the data itself (otherwise it would be invalid when it was needed). In real life code, developers will cut corners in order to improve readability of their API.

Modern development has seen the introduction of shared_ptr. shared_ptr solves these issues by putting the memory management rules directly into the function definition, and does so in a way which is so popular amongst developers, that people find few reasons to roll their own.

Shared_ptr meets Bjarne's rule by putting memory management all in one place: shared_ptr, and meets real life needs by allowing ownership of the memory to be passed from place to place.

  • Excellent explained, textbook style! You can provide you own delete and allocate object to shared_ptr. Make sense. The only odd thing with std::shared_ptr which I don't understand is why the shared object is not copied when initializing shared_ptr. This is asymmetric memory management. – Jim G Jun 12 '14 at 13:11
  • Yes it is. The reason for that was simply a limitation in C++, mostly due to varargs constructor argument passing. It just wasn't reasonable to make it 100% symetric. If you look in C++11 at the new std::make_shared function, it is designed to improve the symmetry by creating the object inside shared_ptr using new syntax only available in C++11. – Cort Ammon Jun 12 '14 at 18:12
0

What is the problem with Bjarne Stroustrups idea about a symmetrical memory management contained within a class?

What do you gain by splitting new and delete so you create an object and destroy it in different classes like you do in Qt.

When you use symmetrical memory management you have to remember if you called "delete" operator on object or not. It increases complexity of programming (which can increase amount of memory leaks) but gives you control over variable's lifecycle. On the other hand if you use some kind of garbage collecting (i.e. like in Java - you can create object but don't handle its deletion) it decreases complexity but sometimes it can lead to problems with performance.

Is it common to split new and delete and why in such case, in other projects not involving Qt?

Depends on projects. It is question of performance needed for project.

The idea is choice between lower complexity (in case of GC) or higher performance (in case of manual memory management).

P.S. Idea behind garbage collection and passing pointer to some master object is all the same: encapsulation of memory management details. But GC is much better way (you don't have to control if you passed pointer to master object - it is hidden from user, so complexity is lower).

  • 1
    You don't have to remember whether you deleted the object: Right after you delete it, set the pointer to null and be done. It's true that there might be other pointers around, but then your problem is one of ownership, not of memory management. – user7043 Jun 11 '14 at 19:30
  • You need to put this magic word "delete" always (!) then you want to delete some object. That is what I meant, not checking if object is deleted or not e.g. which you can do setting variable to NULL after using delete and checking if (x == NULL) { ... blah-blah } afterwards. If you forgot to put delete somewhere - it is memory leak. So you should always remember to put "delete" for object somewhere - and so complexity is higher. :) P.S. Sorry if I've written unclear, English is not my native language. – metamaker Jun 11 '14 at 19:34
  • @metamaker, uhm... C++ doesn't have garbage collection... – Dima Jun 11 '14 at 19:47
  • 1
    No, that's not really feasible. It took long enough for Boehm to get anywhere near something that's remotely usable and stable, and it's still far from perfect. You can implement reference counting (or just use a shared_ptr) but that's a different matter. – user7043 Jun 11 '14 at 19:48
  • 1
    Anyway, the question wasn't about implementing GC in C++ - it was about advantages and disadvantages of QT's approach to memory management which idea is GC-like (as far as I understood from first message of topic). – metamaker Jun 11 '14 at 19:53
0

Keep in mind that Qt has been around a long time, and predates C++11 by at least a decade. I would love to see Qt use more modern approaches and to rely more on std:: classes and containers and the like, but that would represent a lot of work on their behalf and break compatibility with the rather large installed code base.

You might consider CopperSpice as an alternative to Qt, which has been modernized.

https://github.com/copperspice

  • This looks more like a comment than an answer. – joshp Jun 9 '18 at 17:21
0

Qt uses different paradigm for memory management (at least in case of QObject instances), which is quite natural in its case.

Graphical UIs are typically built as hierarchies of widgets (or controls/views/windows in other toolkits). When a child widget is added to a parent, it is expected to be part of hierarchy until either a) it is explicitly removed from it (in Qt, you can just call delete to do this), or b) parent object is destroyed (for example, a top level window is closed by a user).

In other words, in this case parent QObjects control lifetime of their children, and for this reason are responsible for destroying them.

I'd say that symmetrical memory management is a good default approach, but in this particular case, passing ownership to another object seems to be more natural.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.