4

I am a C++ beginner. And I am doing the exercises in C++ Primer (5th Edition). I found a reference to Exercise 13.8 from Github (Here), which is shown below.

#include <string>
#include <iostream>

using std::cout;
using std::endl;

class HasPtr {
public:
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }
    HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
    HasPtr& operator=(const HasPtr &hp) {
        std::string *new_ps = new std::string(*hp.ps);
        delete ps;          // I don't know why it is needed here? 
                            // But when I delete this line, it also works.
        ps = new_ps;
        i = hp.i;
        return *this;
    }

    void print() {
        cout << *(this->ps) << endl;
        cout << this->i << endl;
    }

private:
    std::string *ps;
    int i;
};

int main() {
    HasPtr hp1("hello"), hp2("world");
    hp1.print();
    hp1 = hp2;
    cout << "After the assignment:" << endl;
    hp1.print();
}

What makes me confusing is the HasPtr& operator=(const HasPtr &hp) function. I don't know why delete ps; is needed here. I thought it was an error, but it worked when I compiled the code. However, it also works when I delete the line of delete ps;. So, I do not know whether delete ps; is needed and what is the advantage if it is reserved.

Wieshawn
  • 213
  • 1
  • 10
  • 3
    That is a great example of how **not** to write C++! You should still understand it, but please keep in mind that it is unnecessarily fragile. `std::string` works fine by value in most cases, as does a lot of things in C++, and for things that don't, smart pointers should be used. – Jan Hudec Jan 07 '16 at 10:39
  • 2
    All comments are correct to state that you might as well allocate `ps` as a normal non-pointer member, That said, even if you keep `ps` as a pointer, it doesn't make sense to `delete` it and then immediately re-`new` it. You can just write `*ps = *hp.ps` in the assignment operator. You **must** delete it in the destuctor, however. – MSalters Jan 07 '16 at 11:18
  • @MSalters Thanks for your advice. It really doesn't make sense. Maybe it is merely an exercise. – Wieshawn Jan 07 '16 at 11:44
  • 1
    Don't use `std::endl` unless you need all the extra stuff that it does. `'\n'` starts a new line. – Pete Becker Jan 07 '16 at 15:15

4 Answers4

9

HasPtr::ps is an heap-allocated std::string pointer.

It is allocated and constructed using new in all HasPtr constructors. Therefore, when HasPtr::ps gets replaced by another heap-allocated pointer, the existing memory has to be released using delete to avoid memory leaks.

Note that, in modern C++, you should almost never use new and delete for managing objects like this. Use smart pointers instead, like std::unique_ptr or std::shared_ptr, which take care of memory management for you, safely and conveniently.

I still suggest getting familiar with new and delete, as an huge amount of existing code makes use of them. cppreference.com is a great place to find detailed information about the language.

As Jan Hudec mentioned in the comments, it is generally pretty silly to store an std::string on the heap - std::string is a wrapper around an heap-allocated character array, which already manages the memory for you.

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 2
    I would add, that heap-allocating strings is, most of the time, stupid even via smart pointer. std::string works best as value type. – Jan Hudec Jan 07 '16 at 10:36
4

It's needed to prevent a memory leak: every new must be balanced with a delete.

You allocate memory for ps with a new in the constructors.

When you allocate memory for new_ps and assign this to ps, you need to free the constructor-allocated memory before you lose the old pointer value.

You program will indeed "work" if you omit that line, but it will steadily consume more and more memory, until, eventually there is no more memory left.

Note that you have another memory leak: you need to create a destructor, and call delete ps in it.

As you can see, this is getting unduly complicated. Better still, ditch all this pointer stuff and use a std::string s as the member variable - it takes care of all the memory management for you.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

The delete is needed to avoid a memory leak.

The line ps = new_ps; edits the address of ps to point somewhere else.

The memory that ps was previously pointing at still needs to be freed. The effects of removing this line are not instantly visible and the program will still 'work', but you have a memory leak.

Eg.

ps = address 0 with value 'f'; new_ps = address 1 with value 'g'
Now let ps = new_ps;
ps = address 1 with value 'g'; new_ps = address 1 with value 'g'
So address 0 is no longer something we can access, but it's not been freed either
Nathan Cooper
  • 6,262
  • 4
  • 36
  • 75
0

In C++ programming, calling delete on a class object automatically invokes the destructor of the class.

In general practice, destructor holds the code to free up the resources i.e file pointers,deletion of objects created inside the class.This way, when you call delete on an object,the resources it allocated are freed up i.e given back to the system

If these resources are not freed up,the memory allocated for these resources will not be given back to the system.Every time your object gets called,you lose a certain portion of memory and over a period of time you would have lost a major chunk of memory.This is called as memory leak.

Thus when you call delete on an object,you ensure that the resources you acquired are released, provided you have done those operations in destructor.

Rajesh
  • 356
  • 1
  • 5
  • 15