Home > front end >  C destructor destroys objects couple times
C destructor destroys objects couple times

Time:12-23

I am new in C so have a question.

There is C code:

class Test
{        
    public:
        std::string name;
        Test(){};
        Test(std::string name) {
        
            std::cout << "Create " << name << '\n';
            Test::name = name;
        };
        ~Test() {std::cout << "Destroy " << name << '\n';} 
};

std::vector<Test> test {Test("one"), Test("two"), Test("three")};

void main()
{
    for (auto i : test)
        std::cout << i.name << '\n';
    
    std::cout << "Clear\n";
    test.clear();
}

And this is output:

Create one
Create two
Create three
Destroy three
Destroy two
Destroy one
one
Destroy one
two
Destroy two
three
Destroy three
Clear
Destroy one
Destroy two
Destroy three

Why compiler destroy objects in vector couple times, when must do it once? What wrong in that code?

Compile Microsoft cl.exe x64 with default options.

CodePudding user response:

Let's add a copy constructor (and use a smaller test case, to cut the verbosity) and see what happens...

#include <iostream>
#include <string>
#include <vector>

class Test
{        
    public:
        std::string name;
        Test(){};
        Test(std::string name) : name(name) {        
            std::cout << "New " << name << '\n';
        }
        Test(const Test& other) : name("Copy of "   other.name) {
            std::cout << "Copied " << other.name << '\n';
        }
        ~Test() {std::cout << "Destroy " << name << '\n';} 
};

std::vector<Test> test {Test("Original") };

int main()
{
    std::cout << "Loop:\n";
    for (auto i : test)
        std::cout << "This is " << i.name << '\n';   
    std::cout << "Clear\n";
    test.clear();
}

This produces

New Original
Copied Original
Destroy Original
Loop:
Copied Copy of Original
This is Copy of Copy of Original
Destroy Copy of Copy of Original
Clear
Destroy Copy of Original

Explanation:

New Original -- The object in the initialzer list
Copied Original -- Here it gets copied into the vector
Destroy Original -- The original is destroyed along with the initializer list
Loop:
Copied Copy of Original -- Copied into the loop variable
This is Copy of Copy of Original -- Printing in the loop
Destroy Copy of Copy of Original -- The temporary loop object is destroyed
Clear
Destroy Copy of Original -- Clearing the vector

If you loop over references, i will refer to the object inside the vector instead of a copy of it - just changing one line to

for (auto& i : test)

changes the output to

New Original
Copied Original
Destroy Original
Loop:
This is Copy of Original
Clear
Destroy Copy of Original

And you can get rid of further copying by creating the object inside the vector directly:

int main()
{
    std::vector<Test> test;
    test.emplace_back("Original");
    std::cout << "Loop:\n";
    for (auto& i : test)
        std::cout << "This is " << i.name << '\n';   
    std::cout << "Clear\n";
    test.clear();
}

Output:

Original
Loop:
This is Original
Clear
Destroy Original

CodePudding user response:

Because in the for loop:

for (auto i : test)
    std::cout << i.name << '\n';

you're actually creating another copy of the Test elements in std::vector<Test> test, not the element itself, so that's why it creates (and destroys another copy)

Change your for loop to:

for (auto &i : test) // reference:
    std::cout << i.name << '\n';

produce:

Create one
Create two
Create three
Destroy three
Destroy two
Destroy one
one
two
three
Clear
Destroy one
Destroy two
Destroy three

which is what you expect:

Try it live (including all the proper headers)


Also, main() returns an int, so change your main() definition to:

int main()

CodePudding user response:

int main() not void main(). Your compiler should have warned you about that. Make sure you enable your compiler warnings.

for (auto i : test) makes a copy of the object. You probably wanted for (auto&& i : test) or for (auto const& i : test). Note that auto&& has a bit of a different meaning than Test&&, since auto&& follows the template rules.

The code neglects to consider the implicit copy constructor. Here I've added one in, which should help significantly for understanding what is going on:

#include <iostream>
#include <string>
#include <utility>
#include <vector>

struct Test {
    std::string name;

    ~Test() {
        std::cout << "Destroy " << name << "\n";
    }

    Test() {
        std::cout << "Default-Create (empty)\n";
    }

    Test(std::string name_) : name{std::move(name_)} {
        std::cout << "Create " << name << "\n";
    }

    Test(Test const& other) : name{other.name   "!"} {
        std::cout << "Create-by-Copy " << name << "\n";
    }
};

int main() {
    auto test = std::vector<Test>{Test{"one"}, Test{"two"}, Test{"three"}};

    for (auto i : test)
        std::cout << i.name << "\n";

    std::cout << "Clear\n";
    test.clear();
}

CodePudding user response:

What wrong in that code?

main must return int. It must not return void. To fix this, change the return type to int

Other than that, your expectations are wrong.

When you create a variable or a temporary object, it will be destroyed. When you create a copy of an object, both the copy and the original object will be destroyed eventually (except for cases involving memory leaks).

  • Related