1

I have started to C++ and need some clarifications regarding memory management in C++. I have come across smart pointers, but I wish to understand some basic concepts.

Here's a sample structure

struct A
{
    private:
    int a;
    void* b;
    public:
    A(int i, void* m) { a=i; b=m; }
};

main()
{
    A * a1 = new A(10, 0);
    // 
    //Some Code
    if(on some condition) {
         delete a1;
         a1=nullptr;
    }
}

When I delete a1, will m also be deleted automatically or should i explicitly delete m before deleting a1 as given below?

delete a1->b;
a1->b = nullptr;
delete a1;
a1=nullptr;
vsk
  • 35
  • 5
  • 2
    _I have come across smart pointers_ I don't see any [smart pointers](https://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one). However, if you would have used them, there would be no need for an explicit destructor that Basile mentioned in his answer – muXXmit2X Aug 27 '18 at 05:33
  • 1
    A few notes: Avoid `void*` in C++ as there is almost always a better way (notable exception: legacy interfaces that require `void *`). There are many ways around `void*` , the most common probably being templates and inheritance. One of C++'s most important idioms is [Resource Allocation Is Initialization](https://en.cppreference.com/w/cpp/language/raii). Observing RAII eliminates many common families of bugs. – user4581301 Aug 27 '18 at 05:48
  • You don't really delete pointers, you delete the objects they point to. If you have many pointers to the same object, you need to take care so you only delete the object once. – molbdnilo Aug 27 '18 at 06:13
  • Smart pointers are called that for a reason. Normal pointers are dumb, they **never** delete themselves, one way or another you have to do it. – john Aug 27 '18 at 07:42
  • There is no data member `m` in `A`, you can't `delete a1->m`. You can't also `delete a1->b` outside `A`, because `b` is private. – Evg Aug 27 '18 at 07:56
  • How would the compiler know whether to use `delete` or `delete[]`? Or perhaps `release_resource_to_OS(b);` ? – MSalters Aug 27 '18 at 08:58
  • @Evgeny, thank you for pointing out the error (I have rectified it). So if had created b with default access specifiers, would I then be able to use delete->b? – vsk Aug 27 '18 at 10:15
  • In `struct` - yes. But don't do it anyway, as the answers below explain. An object should only be deleted by the one who created it. Destruction by its very nature should be performed in the reverse order w.r.t construction. In your case the object that will be pointed to by `b` is created *before* `A` is constructed, and there is no sense in deleting it *before* `A` is destructed. If you want to transfer ownership, use smart pointers. – Evg Aug 27 '18 at 10:31

2 Answers2

4

When I delete a1, will m also be deleted automatically

No, it won't (your code probably has a memory leak). You need an explicit destructor deleting it.

BTW, using a void*b; pointer field is poor taste. You should prefer some more explicit type (e.g. double*b; or SomeClass* b;) if you know it. This makes your code more readable, and give more opportunities for helpful type checking at compile time.

// inside struct A
~A() { delete b; };

Read about the rule of five.

Notice that struct-s are very similar to class-es in C++.

Avoid memory leaks. Tools like valgrind could be helpful. And using systematically smart pointers and standard containers should help to avoid them. If your field b was declared std::shared_ptr<std::string> b; the default destructor would have freed it appropriately. And perhaps you want it to be some std::vector<std::string> (again, the default destructor is releasing memory appropriately).

A good coding hint is to avoid, when possible, declaring raw pointers (prefer smart pointers and containers). When you have to declare one, you need to code its delete appropriately.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 4
    I don't think it's possible to say from the code posted whether there's a memory leak. That will depend on whether that pointer is referenced by any other source in the application. (And the example of passing 0 is unhelpful in determining that.) It's fairly common to have weak pointers that are not owned by a `struct` or object of a `class` which makes use of them. – user1118321 Aug 27 '18 at 05:41
2

Welcome to C++, a very powerful language that requires you to take responsibility of the details to achieve the flexibility that allows such power. If you like, you can make it very complex, however for most constructs their is an easy way as well.

First of all, you ain't required to release memory, if your program exits, it will clean it. However, as you don't call delete, the Dtor will not be called which might cause specific code to not be executed. So in general, it's good practice to clean up the allocated memory.

If you don't need the heap, don't use it

new A(10, 0) will allocate memory on the heap. If you don't want that, this can as well be created on the stack. Which causes auto cleanup: A a{10, nullptr};

Use RAII

As soon as you decide, you need heap allocated memory, you should default to std::unique_ptr. Which changes the code to: auto a = std::make_unique<A>(10, nullptr); With this, ownership is within the unique_ptr, which can be moved around (std::move). If you don't want to transfer ownership, you can dereference it or call the method get.

Applying these 2 practices, including for members, will prevent a lot of memory leaks and will reduce the time you need to think about it.

Don't use void*

void* is evil, don't use it unless you have to (and you only have to when interfacing with C). There are many ways of avoiding it. The best one is introducing an interface.

class I {
public:
    virtual ~I() = default;
};

class M : public I
{
    // ...
};

class A
 {
      // ...
      std::unique_ptr<I> m;
      // ...
  };

Need something special?

Some times, you need something special in the Dtor, only in that case you should implement the Dtor explicitly. Given your question, I'm gonna assume you are a beginner and as such don't need to know about more details for now.

JVApen
  • 11,008
  • 5
  • 31
  • 67
  • what are the special details that can be added to a Destructor, Can you please provide a link? – vsk Aug 27 '18 at 10:17
  • 2
    @vsk, examples for "something special" are 1) `std::ofstream` destructor flushes the internal buffer, and closes the file; 2) `std::lock_guard` destructor unlocks the associated mutex automatically; 3) `std::map` deletes all nodes in the Balanced Binary Tree, while calling the destructors of all keys and data. There are many more examples – Michael Veksler Aug 27 '18 at 10:40