I am trying to trace this program however for some reason it displays
Process finished with exit code 4.
and end without calling the destructors? what could be the cause?
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A ctor" << endl; }
A(const A& a) { cout << "A copy ctor" << endl; }
virtual ~A() { cout << "A dtor" << endl; }
virtual void foo() { cout << "A foo()" << endl; }
virtual A& operator=(const A& rhs) { cout << "A op=" << endl; }
};
class B : public A {
public:
B() { cout << "B ctor" << endl; }
virtual ~B() { cout << "B dtor" << endl; }
virtual void foo() { cout << "B foo()" << endl; }
protected:
A mInstanceOfA; // don't forget about me!
};
A foo(A& input)
{
input.foo();
return input;
}
int main()
{
B myB;
B myOtherB;
A myA;
myOtherB = myB;
myA = foo(myOtherB);
}
Your program exhibits undefined behavior, by way of reaching the closing brace of a non-void function (here, operator=) without encountering return statement.
For me (with gcc 8.3.0) it works well. But I get a warning:
test.cpp: In member function ‘virtual A& A::operator=(const A&)’:
test.cpp:10:63: warning: no return statement in function returning non-void [-Wreturn-type]
virtual A& operator=(const A& rhs) { cout << "A op=" << endl; }
And it prints out:
A ctor
A ctor
B ctor
A ctor
A ctor
B ctor
A ctor
A op=
A op=
B foo()
A copy ctor
A op=
A dtor
A dtor
B dtor
A dtor
A dtor
B dtor
A dtor
A dtor
Maybe you should try to solve the warning.
Related
Take the following example
struct A {
int x = 0;
};
struct B {
std::shared_ptr<A> mA;
void setA(std::shared_ptr<A> a) {
mA = a;
}
};
struct C {
B initB() {
A a;
A *aPtr = &a;
B b;
b.setA(std::make_shared<A>(aPtr));
return b;
}
};
Now in main() method
C c;
B b = c.initB();
Local variable a in initB() goes out of scope one function finishes executing. However there is a shared pointer pointing to it. Will the object the shared pointer points get deleted when a goes out of scope?
Finally will *(b.mA) give a valid instance of A?
Firstly, this doesn't compile.
B initB() {
A a;
A* aPtr = &a;
B b;
b.setA(std::make_shared<A>(/*aPtr*/a););
return b;
}
You have to pass the actual object being made shared, not a pointer to it. Now, to find out the answer to this problem, we can write notifiers for each function, and add constructors and destructors so we can see what is happening.
#include <memory>
#include <iostream>
struct A {
int x = 0;
A() {
std::cout << "A's CTOR" << std::endl;
}
~A() {
std::cout << "A's DTOR" << std::endl;
}
};
struct B {
B() {
std::cout << "B's CTOR" << std::endl;
}
~B() {
std::cout << "B's DTOR" << std::endl;
}
std::shared_ptr<A> mA;
void setA(std::shared_ptr<A> a) {
std::cout << "Entering setA()" << std::endl;
mA = a;
std::cout << "Exiting setA()" << std::endl;
}
};
struct C {
C() {
std::cout << "C's CTOR" << std::endl;
}
~C() {
std::cout << "C's DTOR" << std::endl;
}
B initB() {
std::cout << "Entering initB()" << std::endl;
A a;
A* aPtr = &a;
B b;
b.setA(std::make_shared<A>(/*aPtr*/a));
std::cout << "Exiting initB()" << std::endl;
return b;
}
};
int main() {
std::cout << "Entering Main" << std::endl;
C c;
B b = c.initB();
std::cout << "Exiting Main" << std::endl;
return 0;
}
The output:
Entering Main
C's CTOR
Entering initB()
A's CTOR
B's CTOR
Entering setA()
Exiting setA()
Exiting initB()
B's DTOR
A's DTOR
Exiting Main
B's DTOR
A's DTOR
C's DTOR
Interesting, did you catch what happened? There are 2 A's DTORs. std::make_share<A>(a) actually makes a copy of a and then makes a shared_ptr to a. Since we didn't define a copy assignment operator/constructor, the compiler automatically makes one, and that is why we only have one A's CTOR. So even though I can't imagine a place where you would do this, it will have a valid instance of A.
I got this code:
Edit: The full 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:
B(){}
virtual ~B() {
cout << "B dtor" << endl;
}
void type() const override {
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 << "applying function g:" << endl;
g(*pa).type();
delete pa;
return 0;
}
I noticed when debugging the code that after making and passing a copy of *pa to f and ending the function the destructor of the copy (*pa) that was passed to f is not called.
Only when type() ended (the same line) both the copies (I assume) were erased by the destructors
I was sure that when ending the function a destructor will be called and erase the current copy which did not happened in this case. I would like to get an explanation to the order in which the constructors and desturctors are being called in the code (I am not very knowledgeable about the order when methods are involved and I could not find much about it online).
thank you.
When you do f(*pa).type();, the Copy Constructor of A is called with an A object, so it will create an A to pass into your f function. When f returns, it is a different A being returned, since it's not by ref, however, it is NOT immediately destroyed, and instead uses copy-elision to stick around until after the .type() is called.
After that point, the destructor of both the temporary object passed into f, and the temporary object return by f are destroyed, so ~A() gets called twice.
#include <iostream>
class A {
public:
A() { std::cout << "Constructor" << std::endl; }
A(const A& a) { std::cout << "Copy Constructor" << std::endl; }
A& operator=(const A& a) { std::cout << "Copy = operator" << std::endl; }
A(A&& a) { std::cout << "Move Constructor" << std::endl; }
A& operator=(A&& a) { std::cout << "Move = operator" << std::endl; }
~A() { std::cout << "Destructor" << std::endl; }
};
void f(A&& a) { std::cout << "function" << std::endl; }
int main() {
f(A());
return 0;
}
The output of the following program is:
Constructor
function
Destructor
Why is the move-constructor not called here? It seems like copy elision occurs even if I compile with the flag -fno-elide-constructors: g++ test.cpp -fno-elide-constructors -std=c++11
Short answer: You are not move-constructing anything.
You are just creating a temporary Aobject and then passing a reference to it. If you want to see move construction, you could e.g. change the signature of f to
void f(A a)
I have the following code:
#include <iostream>
#include <vector>
struct A
{
std::vector<int> x;
A()
{
std::cout << "A()" << std::endl;
}
A(const A&)
{
std::cout << "A(const A&)" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
};
struct B : public A
{
std::vector<int> y;
B()
{
std::cout << "B()" << std::endl;
}
B(const A&a)
{
std::cout << "B(const A&)" << std::endl;
x = std::move(a.x);
y.resize(x.size());
}
B(const A&&a)
{
std::cout << "B(const A&&)" << std::endl;
x = std::move(a.x);
y.resize(x.size());
}
B(const B&)
{
std::cout << "B(const B&)" << std::endl;
}
~B()
{
std::cout << "~B()" << std::endl;
}
};
A ret_a()
{
A a;
a.x.resize(10);
return a;
}
int main()
{
std::cout << "section I" << std::endl << std::endl;
A a = ret_a();
B b(a);
std::cout << "a.x.size=" << a.x.size() << std::endl;
std::cout << std::endl << "section II" << std::endl << std::endl;
B b2(ret_a());
std::cout << "b.x.size=" << b.x.size() << std::endl;
std::cout << std::endl << "section III" << std::endl << std::endl;
return 0;
}
With output (VS2013, Release build)
section I
A()
A()
B(const A&)
a.x.size=10
section II
A()
A()
B(const A&&)
~A()
b.x.size=10
section III
~B()
~A()
~B()
~A()
~A()
Why a.x.size() within "section I" has size 10? I thought that std::move should move all data from a.x to y.x
Why did "section II" call constructor A() twice? I thought that B(const A&&) would prevent excessive copying of A
UPDATE
see fixed code at http://pastebin.com/70Nmt9sT
T&& and const T&& are not the same type. You almost never want a const rvalue reference - you can't steal its resources since you made it const! x = std::move(a.x); in B(const A&a) copies a.x since the return type of std::move(a.x) is const vector<int>&&.
The constructor, B(const A&&) calls the default constructor of A since it is derived from A, and the member initializer list does not make an attempt to construct the base A. This is the second A call.
Why a.x.size() within "section I" has size 10? I thought that std::move should move all data from a.x to y.x
This is because of B(const A&& a). Since a is const within that constructor, you only have const access to its member x, and calling std::move on a vector<T> const results in a vector<T> const&& which cannot bind to vector's move constructor (which takes a vector<T>&& argument). Instead it ends up calling the copy constructor, which leaves the source object unmodified.
Why did "section II" call constructor A() twice? I thought that B(const A&&) would prevent excessive copying of A
The first default construction occurs within the body of ret_a(). The second default construction is that of the A sub-object of B. To avoid the second one move the A instance in the member initializer list.
B(const A&&a)
: A(std::move(a))
{
std::cout << "B(const A&&)" << std::endl;
y.resize(x.size());
}
Note that the move doesn't actually result in moving the contents of a due to the same reason as explained above. Moreover, even modifying the signature to B(A&& a) would not result in the contents of a being moved because the user provided copy constructor and destructor definitions prevent implicit generation of a move constructor for A, and it'll be copied instead.
#include <iostream>
using namespace std;
struct A
{
A() {}
A(const A &a) {
cout << "copy constructor" << endl;
}
A& operator=(const A &a) {
cout << "assigment operator" << endl;
}
A(A &&a) {
cout << "move" << endl;
}
A& operator=(A &&a) {
cout << "move" << endl;
}
};
struct B {
A a;
};
B func() {
B b;
return b;
}
int main() {
B b = func();
}
This prints "copy constructor".
For class B the move constructor and the move assignment operator should be automatic generated correct? But why is it using the copy constructor of class A and not the move constructor?
For me it doesn't print anything at all because the copy/move has been elided. However if I thwart RVO with something like:
extern bool choice;
B func() {
B b1, b2;
if (choice)
return b1;
return b2;
}
Then it prints:
move
It may be that your compiler does not yet implement the automatic generation of the move members.