0

Here I have a C++ code:

#include <iostream>
#include <map>
#include <string>
#include <cstdlib>

using namespace std;

class Person {
    private:
        int year;
        Person(const Person& pers);
    public:
        Person(int y): year(y)
        { cout << "Default constructor" << endl;}

        ~Person()
        {
            cout << "Destructor " << endl;
        }

        int get_year() const
        {
            return year;
        }
};


int main()
{
    map<string, Person*> test;
    test.insert(pair<string, Person*>("ini_1", new Person(2)));
    return 0;
}

Output

Default constructor
  1. From the output, I would like to know, how I can delete the value of test map given new Person(2) without coding it like first
Person* per = new Person(2)
test.insert(pair<string, Person*>("ini_1", per));

delete per;
  1. Without defining like this first
Person* per = new Person(2)
test.insert(pair<string, Person*>("ini_1", per));

Will it lead to undefined behaviour? Can you describe more detail of the undefined behaviour? Especially how does it exist in the memory? Thanks.

  1. If it is not correct, can I do like this which use Person instead new Person? Will it lead to any undefined behaviour?
#include <iostream>
#include <map>
#include <string>
#include <cstdlib>

using namespace std;

class Person {
    private:
        int year;

    public:
        Person(int y): year(y)
        { cout << "constructor" << endl;}

        Person(const Person& pers)
        {
            cout << "copy constructor" << endl;
        }
        ~Person()
        {
            cout << "Destructor " << endl;
        }

        int get_year() const
        {
            return year;
        }
};


int main()
{
    map<string, Person> test;

    test.insert(pair<string, Person>("ini_1", Person(2)));

    return 0;
}

Output:

constructor
copy constructor
copy constructor
Destructor
Destructor
Destructor
  1. I don't understand why the constructor ran for once and copy constructor ran for twice. Can you please explain where they happened?

Thanks.

wangmyde
  • 77
  • 8
  • 2
    Sounds like you could use a [good C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – NathanOliver Dec 17 '19 at 16:27
  • What do you mean by "defining" a pointer, here? – user253751 Dec 17 '19 at 16:28
  • 3
    I don't see any undefined behavior here. In the pointer case your destructor doesn't run, but that's because there's no `delete`. You could correct that by using a smart pointer instead of a raw pointer. But there's nothing wrong here with using the values instead of pointers.. – Fred Larson Dec 17 '19 at 16:30
  • 1
    @wangmyde you cannot test Undefined Behavior, you have to know. And there is not such thing in C++ as "new pointer" or "undefined pointer", you should use common terminology if you want to be understood. – Slava Dec 17 '19 at 16:32
  • 2
    On 4. You create the `Person` temporary, that gets copied to construct the `pair<>` temporary, and copied again into the insert function. – parktomatomi Dec 17 '19 at 16:34
  • Please ask only one question – Thomas Sablik Dec 17 '19 at 16:41
  • @user253751, I directly did `test.insert(pair("ini_1", new Person(2)));` but not first `Person* per = new Person(2)` then `test.insert(pair("ini_1", per));` – wangmyde Dec 17 '19 at 16:41
  • 1
    @wangmyde That is like doing `doSomething(2);` instead of `int i = 2; doSomething(i);` - i.e. it makes no difference whatsoever. – user253751 Dec 17 '19 at 16:42
  • 1
    Possible duplicate of [Why does the map.insert() method invoke the copy constructor twice?](https://stackoverflow.com/questions/15247262/why-does-the-map-insert-method-invoke-the-copy-constructor-twice). – François Andrieux Dec 17 '19 at 16:44
  • One option would be to use smart pointers in your map, either std::unique_ptr or std::shared_ptr. This would ensure that the allocated Person objects are deleted when the map is destroyed, but they come with caveats. – Gem Taylor Dec 17 '19 at 16:53

1 Answers1

1

From the output, I can see the destructor did not run. I would like to know, how I can delete the new pointer without defining it?

You declared a map of ;pointers to Person

map<string, Person*> test;

So an object of the type Person is created only once in this statement

test.insert(pair<string, Person*>("ini_1", new Person(2)));

Further the map deals with the pointer not with the object.

You will need to delete the created object explicitly. For example

for ( auto &item : test )
{
    delete item.second;
     item.second = nullptr;
}

If you will not delete the allocated object (or objects) then there will be a memory leak.

I don't understand why the constructor ran for once and copy constructor ran for twice. Can you please explain where they happened?

In this statement

test.insert(pair<string, Person>("ini_1", Person(2)));

the conversion constructor is called explicitly to create an object of the type Person Person(2).

Then the copy constructor of the class Person is called to create an object of the type pair<string, Person>.

And at last this object is copied to the map again calling the copy constructor of the type Person for the data member second of the pair.

So three objects were created and three destructors for the objects were invoked.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Hi, @Vlad from Moscow. If I use `test.insert(pair("ini_1", new Person(2)));` but not `Person* per = new Person(2); test.insert(pair("ini_1", per));`, will it lead to undefined behaviour? Like here as what @h0r53 said [link](https://stackoverflow.com/questions/59376783/how-is-the-process-for-a-desctructed-class-that-can-still-exist-in-a-map-contain?noredirect=1#comment104947343_59376783) `Using new will almost certainly cause issues...` – wangmyde Dec 17 '19 at 16:59
  • `Using new will almost certainly cause issues, because the Person object will be stored on heap allocated memory. 1) Unless you call delete on the object, you'll have a memory leak. 2) Even if you do delete the object, the heap memory it allocated will be available for reuse, while your map still points to that memory. This is even worse.` Is that correct? – wangmyde Dec 17 '19 at 17:00
  • 1
    @wangmyde Neither undefined behavior will be in this case. The undefined behavior can occur if you will try to delete the memory two times using the locally declared pointer and the pointer stored in the map. – Vlad from Moscow Dec 17 '19 at 17:14