Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When the object destructed while I pass the argument by-value?

given the following code:

#include <iostream>
using namespace std;
class A {
public:
    A() {
    }
    A(const A& a) {
        cout << "A copy ctor" << endl;
    }
    virtual ~A() {
        cout << "A dtor" << endl;
    }
    virtual void type() const {
        cout << "This is A" << endl;
    }
};
class B: public A {
public:
    virtual ~B() {
        cout << "B dtor" << endl;
    }
    virtual void type() const {
        cout << "This is B" << endl;
    }
};
A f(A a) {
    a.type();
    return a;
}
const A& g(const A& a) {
    a.type();
    return a;
}
int main() {
    A *pa = new B();
    cout << "applying function f:" << endl;
    f(*pa).type();
    cout << "~~~ delete: ~~~" << endl;
    delete pa;
    return 0;
}

I get the following output:

applying function f:
A copy ctor
This is A
A copy ctor
This is A
A dtor
A dtor
~~~ delete: ~~~
B dtor
A dtor

But I don't understand something. It's seen that while we exists from f the object there is not destructed. Why it's not destructed? (After all, we get out of the scope of the function, so it has to be destroyed, no?)

Note: I emphasized the problematic lines (which I do not understand why it happens in this order)

We going to the function f, at this point, we call to the copy c'tor and printed "A copy ctor" (Ok) , after it, we returns to the function f and going to type() , and then, it's prints "This is A" , now we escpaes from the function f so we calls to the copy c'tor , hence will be printed "A copy ctor" , But, now, what the destructor is not called (while we escape from the function f)..?

I thought that the output will be the following (according to the description above) :

applying function f:
A copy ctor
This is A
A copy ctor (escape from type)
A dtor (escape from type)
This is A (at the main)
~~~ delete: ~~~
B dtor
A dtor

like image 325
Software_t Avatar asked Oct 20 '25 03:10

Software_t


1 Answers

The C++ standard permits by-value function arguments to be destroyed either within the scope of the function, or in the calling scope.

If in the calling scope, it is destroyed at the end of the full expression (usually the ;). If within the function, it is destroyed after the return value is constructed and automatic storage locals are destroyed.

A *pa = new B();

A B is created, with an A subobject base.

cout << "applying function f:" << endl;
f(*pa)//.type();

A sliced copy of *pa is created as the argument to f. Output is A copy ctor\n.

A f(A a) {
  a.type();
  return a;
}

A .type. is invoked on an instance of A. Note that this A is a copy of *pa, but it is a copy of only the A portion of *pa.

Output is This is A, followed by A(A&&) move ctor, followed optionally by A dtor. In your case you have a copy ctor and not a move ctor, so it is called. This copy/move cannot be elided, as you aren't allowed to elide from function arguments. Output is A copy ctor.

At this point, the compiler may optionally destory the A that is an argument to f. Your compiler does not destroy the argumen to f here.

f(*pa).type();

The temporary A returned by f now has .type() invoked on it. There is no polymorphism here; the method A::type() is called directly. Output is This is A.

Then we reach tne end of the full expression.

Now the temporary returned by f is destroyed, followed by the argument of f if it wasn't destroyed earlier. So output is A dtor.

cout << "~~~ delete: ~~~" << endl;
delete pa;

The B object *pa is destroyed, and then the memory is recycled. As ~A is virtual the proper destructor is called on *pa.

Output is B dtor\nA dtor\n.

like image 153
Yakk - Adam Nevraumont Avatar answered Oct 21 '25 17:10

Yakk - Adam Nevraumont



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!