Lifetime of object pointed to by shared pointer - c++

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.

Related

how to initialize a class object reference in C++ to simulate NRVO?

I meet a course programming problem, which asks me to initialize the A a using passing by reference (initialize the A a in the func). How can I call A's constructor by A's reference?
#include <iostream>
using namespace std;
class A
{
public:
int x;
A()
{
cout << "default constructor" << endl;
x = 1;
}
A(int x)
{
cout << "constructor with param = " << x << endl;
this->x = x;
}
~A() {
cout << "destructor" << endl;
}
void print() {
cout << x << endl;
}
};
void fun(A& a)
{
a.A::A(10); // error!
return;
}
int main()
{
A a;
fun(a);
a.print();
return EXIT_SUCCESS;
}
There is a background of this problem. The teacher want us to replicate the NRVO(named return value optimization) result.
#include <iostream>
using namespace std;
class A
{
public:
int x;
A()
{
cout << "default constructor" << endl;
x = 1;
}
A(int x)
{
cout << "constructor with param = " << x << endl;
this->x = x;
}
~A() {
cout << "destructor" << endl;
}
void print() {
cout << x << endl;
}
};
A fun() {
A a = A(10);
return a;
}
int main()
{
A a = fun();
return EXIT_SUCCESS;
}
default g++ compiler:
constructor with param = 10
destructor
if we close the NRVO:
g++ test.cpp -fno-elide-constructors
constructor with param = 10
destructor
destructor
destructor
destructor
The teacher want us to replicate the NRVO(named return value optimization) result by passing by reference.
The syntax a.A::A(10); is incorrect.
Constructor is used to create an object of a class, you cannot call it on an already existing object. Even a constructor cannot be explicitly called. It is implicitly called by the compiler.
From general-1.sentence-2:
Constructors do not have names.
Thus, you cannot call a constructor explicitly. The compiler will automatically call the constructor when an object of that class-type is created.
You can not, not like this.
A reference always points to an initialized object. So you already failed before you called the function. The "return" argument is already initialized. And you can't initialized an initialized value again, not legally.
You can cheat by calling
std::construct_at(&a, 10);
For it to really reflect NRVO you could have something like this:
void fun(A *a)
{
std::construct_at(a, 10);
}
union UninitializedA {
std::byte uninitialized[sizeof(A)];
A a;
};
int main()
{
UninitializedA u;
fun(&u.a);
u.a.print();
u.a.~A();
return EXIT_SUCCESS;
}

C++: What is the order of destructor call with methods?

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.

Destructors not called?

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.

Difference in calling a constructor with delegation, and in another constructor body

lets say I have a class with 2 constructors like so:
class Foo
{
Foo(int x);
Foo();
...
}
I know that I can call one constructor from another like Foo() : Foo(42) {}, but why shouldn't (or should) I do the following:
Foo() {
Foo(42)
}
What is the difference in these cases?
Some suggest to use an "initializer" method, called from any constructor with their respective arguments, but I am puzzled as to what happens in the case above?
Let's put this example:
#include <iostream>
class B
{
private:
int number;
public:
B()
{
B(1);
}
B(int x) : number(x)
{
std::cout << "Constructor: " << x << std::endl;
}
void print(){
std::cout << "Msg: " << number << std::endl;
}
~B(){std::cout << "Destructor: " << number << std::endl;}
};
int main() {
B b;
b.print();
return 0;
}
Output:
Constructor: 1
Destructor: 1
Msg: 1
Destructor: 1
You are destroying a second object! This is strange, what happens if we use pointers...
#include <iostream>
class B
{
private:
int* arr;
public:
B()
{
B(1);
}
B(int x)
{
std::cout << "Constructor: " << x << std::endl;
arr = new int[x];
}
void print(int n){
std::cout << "Msg: " << arr[n] << std::endl;
}
void set(int n,int val){
arr[n] = val;
}
~B()
{
std::cout << "Destructor: " << arr << std::endl;
delete[] arr;
}
};
int main() {
B b;
b.set(0,14);
b.print(0);
return 0;
}
Constructor: 1
Destructor: 0xc45480
Msg: 14
Destructor: 0xc45480
Look up the pointer addr. They are the same, this means:
We are writing in deleted memory.
We are deleting the same memory twice.
These are two serious problems. You shouldn't do that.
Expression Foo(){Foo(42);} constructs anonymous temporary object that gets destroyed immediately without changing the object being constructed anyhow, while from Foo() : Foo(42){} will initialize the object being constructed.
You should not the following:
Foo() {
Foo(42)
}
When you are in the constructor body the member variables have been just constructed. That's why in C++ initialisation list exists.
The above code is semantically wrong! You are not absolutely using
delegating construction. Instead the statement Foo(42) in the body will just create another object without assigning it to any variable (anonymous variable).
You can imagine something like:
Foo() {
Foo another_obj = Foo(42);
}
In order to use delegating constructor, you must call constructor
in the initialisation list.
Foo() : Foo(42) { }

rvalue hello world missing constructor

I'm trying to know more about rvalue references but I got stuck on this simplest example:
#include <iostream>
using namespace std;
struct C {
C() { cout << "C()\n"; }
~C() { cout << "~C()\n"; }
C(const C&) { cout << "C(const C&)\n"; }
C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; }
C(C&&) { cout << "C(C&&)\n"; }
C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; }
};
C foo() { C c; return c; }
int main()
{
const C c = foo();
return 0;
}
I've compiled it with Clang 3.2 and -std=c++11 -fno-elide-constructors (to avoid (N)RVO) but the result is surprising to me:
C()
~C() // huh?
C(C&&)
~C()
~C()
I expected exactly that except for the first ~C(). Where did it came from and what am I missing because there are 2 constructions and 3 destructions? Is the && constructor called with a destroyed object reference??
This must be a bug. The destructor for the local object constructed in foo() is being invoked before the move constructor of the receiving object. In particular, it seems a temporary is allocated but not (move-)constructed when returning by value. The following program shows this:
#include <iostream>
using namespace std;
struct C
{
C(int z) { id = z; cout << "C():" << id << endl; }
~C() { cout << "~C():" << id << endl; }
C(const C& c) { id = c.id + 1; cout << "C(const C&):" << id << endl; }
C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; }
C(C&& c) { id = c.id + 1; cout << "C(C&&):" << id << endl;}
C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; }
int id;
};
C foo() { C c(10); return c; }
int main()
{
const C c = foo();
return 0;
}
Output:
C():10
// THE TEMPORARY OBJECT IS PROBABLY ALLOCATED BUT *NOT CONSTRUCTED* HERE...
~C():10 // DESTRUCTOR CALLED BEFORE ANY OTHER OBJECT IS CONSTRUCTED!
C(C&&):4198993
~C():4198992
~C():4198993
Creating two objects inside of foo() seems to shed some more light on the issue:
C foo() { C c(10); C d(14); return c; }
Output:
C():10
C():14
~C():14
// HERE, THE CONSTRUCTOR OF THE TEMPORARY SHOULD BE INVOKED!
~C():10
C(C&&):1 // THE OBJECT IN main() IS CONSTRUCTED FROM A NON-CONSTRUCTED TEMPORARY
~C():0 // THE NON-CONSTRUCTED TEMPORARY IS BEING DESTROYED HERE
~C():1
Interestingly, this seems to depend on how the object is constructed in foo(). If foo() is written this way:
C foo() { C c(10); return c; }
Then the error appears. If it is written this way it does not:
C foo() { return C(10); }
Output with this last definition of foo():
C():10 // CONSTRUCTION OF LOCAL OBJECT
C(C&&):11 // CONSTRUCTION OF TEMPORARY
~C():10 // DESTRUCTION OF LOCAL OBJECT
C(C&&):12 // CONSTRUCTION OF RECEIVING OBJECT
~C():11 // DESTRUCTION OF TEMPORARY
~C():12 // DESTRUCTION OF RECEIVING OBJECT