3

I was testing out the following code and what confused me is that there is more destructor calls than constructor calls:

#include <iostream>

struct A {
    A() { std::cout << 1; }
    A(int) { std::cout << 7; }
    ~A() { std::cout << 5; }
};

struct B {
    B() { std::cout << 2; }
    B(int) { std::cout << 9; }
    ~B() {std::cout << 3;}
};

struct C {
    B b;
    A a1;
    A a2;
    C() : a1(3){
        b = 3;
        a2 = 7;
    }
};

int main(){
    C c;
    return 0;
}

The output is like follows:

B()  A(int) A() B(int) ~B() A(int) ~A() ~A() ~A() ~B() 
 2     7     1    9      3     7     5    5    5    3

My guess is that on the lines b = 3 and a2 = 7, 3 and 7 must be implicitly converted to B and A and when they get copied into b and a2, they get destroyed.

Is this correct or is something else going on here?

RisingSun
  • 1,693
  • 27
  • 45
Dzamba
  • 167
  • 8
  • 2
    In the output provided by you, I see the same numbers of calls to constructors and destructors: *3* call pairs for `A` and *2* call pairs for `B`. What am I missing? – JFMR Dec 22 '20 at 18:36
  • 1
    For some reason I thought that the A(int) constructor was a copy constructor and not a basic constructor. I figured it out now.. – Dzamba Dec 22 '20 at 18:38
  • 2
    You would have had better results if you output the value of `this` instead of those numbers. You would quickly see that some of the objects being destroyed were not properly tracked by your code. – PaulMcKenzie Dec 22 '20 at 18:40
  • Now that I've read all these answers I feel kind of dumb for asking, because it's clear as day to me now what is going on here. I was for some reason thinking that the A(int) is copying the integer value into b/a2, where it is actually constructing a temporary object that is then getting copy assigned into b/a2. Thanks for the help! – Dzamba Dec 22 '20 at 18:45

1 Answers1

7

You are not logging the copy constructors and copy assignment operators, eg:

struct A {
    A() { ... }
    A(int) { ... }
    A(const A&) { ... } // <-- add this
    ~A() { ... }
    A& operator=(const A&) { ...; return *this; } // <-- add this
};

struct B {
    B() { ... }
    B(int) { ... }
    B(const B&) { ... } // <-- add this
    ~B() { ... }
    B& operator=(const B&) { ...; return *this; } // <-- add this
};

But, if you actually count the outputs you have shown, you will see that you ARE seeing matching constructors and destructors. You have 3 A constructor calls and 3 ~A destructor calls. And you have 2 B constructor calls, and 2 ~B destructor calls.

B()         C::b
A(int)      C::a1
A()         C::a2
B(int)      temp
B=          temp -> C::b
~B()        temp
A(int)      temp
A=          temp -> C::a2
~A()        temp
~A()        C::a2
~A()        C::a1
~B()        C::b

Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    http://coliru.stacked-crooked.com/a/64da8fe376f01e45 It's copy assignment, not copy constructors that are the issue – Mooing Duck Dec 22 '20 at 18:26
  • Sidenote: I really thought that the user defined destructor would delete the default-generated copy constructor and assignments. I'm shocked that this wasn't the case. – Mooing Duck Dec 22 '20 at 18:27
  • 1
    @MooingDuck - Why on earth would it? – StoryTeller - Unslander Monica Dec 22 '20 at 18:30
  • 3
    @Dzamba you ARE seeing the temp objects being destructed. Count the outputs. You have 3 `A` objects being constructed and destructed, and 2 `B` objects being constructed and destructed. – Remy Lebeau Dec 22 '20 at 18:39
  • @StoryTeller-UnslanderMonica: I assumed because a custom destructor almost always means RAII, which means non-trivial copies. Instead, it only checks for copy/move ctors+assignment. https://stackoverflow.com/questions/4943958/conditions-for-automatic-generation-of-default-copy-move-ctor-and-copy-move-assi – Mooing Duck Dec 22 '20 at 18:50