Why instantiating a shared_ptr calling destructor? - c++

Can someone explain why is the destructor of class bar being called in the line where the object of the same type is being initialized?
#include <memory>
#include <iostream>
using namespace std;
class bar
{
public:
bar() {}
~bar() { std::cout << "destructor called " << std::endl; }
};
class foo
{
public:
foo(std::shared_ptr<bar> barP) {}
};
int main() {
std::shared_ptr<foo> f;
std::cout << "before init " << std::endl;
f = std::shared_ptr<foo>(new foo(std::shared_ptr<bar>(new bar())));
std::cout << "after init" << std::endl;
}
Output:
before init
destructor called
after init

This statement:
f = std::shared_ptr<foo>(new foo(std::shared_ptr<bar>(new bar())));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
constructs a temporary shared_ptr which goes out of scope at the end of the statement. At that point, the shared_ptr goes away, taking bar with it (since no copies of the shared_ptr remain alive).
But if you change foo to read like this:
class foo
{
public:
foo(std::shared_ptr<bar> barP) { m_bar = barP; }
std::shared_ptr<bar> m_bar;
};
Then the output you get is probably what you were expecting, because foo maintains a copy of the shared_ptr until it (foo) goes out of scope and that copy keeps bar alive:
before init
after init
destructor called
Live demo

It's because the bar instance only lives as long as the duration of the foo constructor. So it gets constructed, passed into the shared_ptr, which is then into the foo constructor. As soon as that constructor is done (even if on the same line) the expression itself is done, and so the shared_ptr is done, and destructs.
At the end of main right before the cout, you still have a shared_ptr to foo in f, but the unnamed shared_ptr to your bar object has already gone "out of scope".

First of all, you should not instantiate shared_ptr in this way, use make_shared to instantiate shared_ptr. Please check the modified code -
#include <memory>
#include <iostream>
using namespace std;
class bar
{
public:
bar() {}
~bar() { std::cout << "destructor called " << std::endl; }
};
class foo
{
public:
foo(std::shared_ptr<bar> barP) {}
};
int main() {
std::shared_ptr<foo> f;
std::cout << "before init " << std::endl;
std::shared_ptr<bar> b = std::make_shared<bar>();
f = std::make_shared<foo>(b);
std::cout << "after init" << std::endl;
}
The output of the above program -
before init
after init
destructor called
Generally, what is happening with your code is you are passing new bar to the constructor of foo. And no one is holding the ownership of bar shared pointer. So, the ref count is becoming 0 and it is being deleted, hence the destructor is being called.
Further Read: make_shared vs new

Related

How does a shared_ptr handle copy to a pure virtual base class?

Class B expects to receive an instance of shared_ptr<IError>.
Class A implements IError and is passed by value to the constructor of B.
I would like to understand how this scenario is handled. How does the shared_ptr as a template class handle the conversion to IError?
In a simple case where B receives shared_ptr<A> I assume the copy constructor is called and the reference counter is increased. However since IError is pure virtual a normal copy constructor invocation seems not to be case here?
// Example program
#include <iostream>
#include <string>
class IError
{
public:
virtual ~IError(){};
virtual void OnError() = 0;
};
class A : public IError
{
public:
A(){};
void OnError(){std::cout << "Error Occured" << std::endl;}
};
class B
{
public:
B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){}
void Action(){mErrorReporter->OnError();}
private:
std::shared_ptr<IError> mErrorReporter;
};
int main()
{
auto objA = std::make_shared<A>();
auto objB = std::make_shared<B>(objA);
objB->Action();
}
Debugging time! Let's find out what happens by using the tools we have available as developers.
The memory of the shared_ptr objA looks like this (type &objA in the memory window; it will be replaced by its address):
It has a pointer to the object (000002172badd8e0) and a pointer to the control block.
The control block looks like this (copy and paste the second value into a new memory window):
It has a pointer to the allocator (first 2 columns), the reference count (1) and the weak reference count (0 + offset 1).
After objB has been created, the control block of objA has changed to a reference count of 2:
And the shared_ptr objB looks like this:
It points to the a shared pointer and to the control block.
The shared pointer in objB points to the same object as before (000002172badd8e0), so no copy of the actual object has been made.
The control block of objB indicates that objB only has a reference count of 1:
a normal copy constructor invocation seems not to be case here?
Correct. No copy of the object is made, as we can confirm with a debugger. But a copy of the shared_ptr has been made.
It doesn't.
Copy a shared_ptr doesn't copy it's point-to object, just like normal pointer
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<IError> i = a;
A* a = new A;
IError* i = a; // no copy A
You are right in that the base class IError is abstract, hence it cannot be instantiated, never mind copied.
The code below has been modified from the original to show how each newly created shared_ptr just increments the reference count of the original shared_ptr. So, a shallow copy.
In your code, as well as in the code below, the underlying object to these shared_ptrs is concrete class A, derived from the abstract IError, so it is legal to shallow copy it.
// Example program
#include <iostream>
#include <string>
#include <memory>
class IError
{
public:
virtual ~IError(){};
virtual void OnError() = 0;
};
class A : public IError
{
public:
A(){std::cout << "A created.\n";};
void OnError(){std::cout << "Error Occured" << std::endl;}
};
class B
{
public:
B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){
std::cout << "B created from A.\n";
}
void Action(){mErrorReporter->OnError();}
private:
std::shared_ptr<IError> mErrorReporter;
};
int main()
{
auto objA = std::make_shared<A>();
std::cout << "I. Reference count for objA: " << objA.use_count() << '\n';
auto objB = std::make_shared<B>(objA);
std::cout << "II. Reference count for objA: " << objA.use_count() << '\n';
// objB->Action();
auto objBB = std::make_shared<B>(*objB);
std::cout << "Created objBB from objB\n";
std::cout << "III. Reference count for objA: " << objA.use_count() << '\n';
std::cout << "Reference count for objB: " << objB.use_count() << '\n';
std::cout << "Reference count for objBB: " << objBB.use_count() << '\n';
// auto objB_from_base = std::make_shared<B>(IError()); // ERROR: IError is an abstract class
}
with output:
A created.
I. Reference count for objA: 1
B created from A.
II. Reference count for objA: 2
Created objBB from objB
III. Reference count for objA: 3
Reference count for objB: 1
Reference count for objBB: 1

Why unique_ptr doesn't destroy object immediately when goes out-of-scope?

I have the following piece of code:
struct C
{
C() {std::cout << ">>> constructor\n"; }
~C() {std::cout << ">>> destructor\n"; }
};
void bar(std::unique_ptr<C>&& p)
{
std::cout << "bar\n";
}
int main()
{
auto instance = std::make_unique<C>();
std::cout << "after construction\n";
bar(std::move(instance)); // #1
std::cout << "after bar\n";
return 0;
}
On line #1, I'm moving the pointer to the function bar. I was thinking that in this call the pointer-to-C is moved-out from unique_ptr instance and passed to the argument unique_ptr p. If so, then the C destructor should be called at the end of bar()'s execution when argument p is destructed. But it turned out that the instance of C is alive until the end of main()' s execution. The output looks as follows:
>>> constructor
after construction
bar
after bar
>>> destructor
Why does it happen? How to interpret this behavior?
std::move doesn't actually move. It just casts it into a an rvalue reference.
Change
void bar(std::unique_ptr<C>&& p)
to
void bar(std::unique_ptr<C> p)
To actually do the move in the move constructor.

Why passing shared_from_this() will cause segment fault?

Say we have a class Foo defined as follows:
// foo.hpp
class Foo;
using FooCallback = std::function<void(std::shared_ptr<Foo> ins)>;
class Foo : public std::enable_shared_from_this<Foo>{
public:
Foo(int b, const FooCallback& callback):m_bar(b),
m_callback(callback){}
int
getBar();
void
doSth();
private:
int m_bar;
const FooCallback& m_callback;
};
Why will the following code cause segment fault?
// foo.cpp
#include "foo.hpp"
int
Foo::getBar(){
return m_bar;
}
void
Foo::doSth(){
std::cout << "Start ... " << std::endl;
this->m_callback(shared_from_this());
std::cout << "End ... " << std::endl;
}
int main()
{
auto f = std::make_shared<Foo>(100,
[](std::shared_ptr<Foo> ins){
std::cout << "bar: " << ins->getBar() << std::endl;
});
f->doSth();
return 0;
}
The output is:
Start ...
segmentation fault
To my understanding, this is what is going on:
In main(), f is a shared_ptr pointing to an instance of Foo, say it's ins.
When f->doSth() is called, ins.doSth() is actually called.
In ins.doSth, this is a pointer to ins. And shared_from_this() is a shared_ptr to ins.
So why is step 3 causing the segment fault?
This has nothing to do with shared_from_this. If you look in the debugger, it shows that this segfaults at the location where the std::function's internal pointer points to.
And this happens because m_callback is a reference, and the function object that it refers doesn't exist anymore when you call doSth (because it was a temporary object).
To fix this you can save m_callback by value:
const FooCallback m_callback;
Or even better, since the lambda doesn't capture anything, you could make m_callback a plain function reference (or pointer):
using FooCallback = void(std::shared_ptr<Foo> ins);
…
FooCallback& m_callback;
…
auto f = std::make_shared<Foo>(100,
*[](std::shared_ptr<Foo> ins){
std::cout << "bar: " << ins->getBar() << std::endl;
});

Would this restrict the class to be have a lifetime in the current frame only?

I wanted to restrict a specific class to be creatable on the stack only (not via allocation). The reason for this is that on the stack, the object which lifetime has begun last, will be the first to be destroyed, and I can create a hierarchy. I did it like this:
#include <cstddef>
#include <iostream>
class Foo {
public:
static Foo createOnStack() {
return {};
}
~Foo () {
std::cout << "Destructed " << --i << std::endl;
}
protected:
static int i;
Foo () {
std::cout << "Created " << i++ << std::endl;
}
Foo (const Foo &) = delete;
};
int Foo::i = 0;
The constructor normally should push the hierarchy stack, and the destructor pops it. I replaced it here for proof of concept. Now, the only way you can use such an object is by storing it in a temporary reference like this:
int main() {
Foo && a = Foo::createOnStack();
const Foo& b = Foo::createOnStack();
return 0;
}
My question now is, how safe is this with the C++ standard? Is there still a way to legally create a Foo on the heap or hand it down from your function into another frame (aka return it from your function) without running into undefined behaviour?
EDIT: link to example https://ideone.com/M0I1NI
Leaving aside the protected backdoor, C++17 copy elision breaks this in two ways:
#include<iostream>
#include<memory>
struct S {
static S make() {return {};}
S(const S&)=delete;
~S() {std::cout << '-' << this << std::endl;}
private:
S() {std::cout << '+' << this << std::endl;}
};
S reorder() {
S &&local=S::make();
return S::make();
}
int main() {
auto p=new S(S::make()),q=new S(S::make()); // #1
delete p; delete q;
reorder(); // #2
}
The use of new is obvious and has been discussed.
C++17 also allows prvalues to propagate through stack frames, which means that a local can get created before a return value and get destroyed while that return value is alive.
Note that the second case already existed (formally in C++14 and informally long before) in the case where local is of type S but the return value is some other (movable) type. You can't assume in general that even automatic object lifetimes nest properly.

std::thread initialization with class argument results with class object being copied multiple times

It seems that if you create an object of a class, and pass it to the std::thread initialization constructor, then the class object is constructed and destroyed as much as 4 times overall. My question is: could you explain, step by step, the output of this program? Why is the class being constructed, copy-constructed and destructed so many times in the process?
sample program:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <thread>
class sampleClass {
public:
int x = rand() % 100;
sampleClass() {std::cout << "constructor called, x=" << x << std::endl;}
sampleClass(const sampleClass &SC) {std::cout << "copy constructor called, x=" << x << std::endl;}
~sampleClass() {std::cout << "destructor called, x=" << x << std::endl;}
void add_to_x() {x += rand() % 3;}
};
void sampleThread(sampleClass SC) {
for (int i = 0; i < 1e8; ++i) { //give the thread something to do
SC.add_to_x();
}
std::cout << "thread finished, x=" << SC.x << std::endl;
}
int main(int argc, char *argv[]) {
srand (time(NULL));
sampleClass SC;
std::thread t1 (sampleThread, SC);
std::cout << "thread spawned" << std::endl;
t1.join();
std::cout << "thread joined" << std::endl;
return 0;
}
The output is:
constructor called, x=92
copy constructor called, x=36
copy constructor called, x=61
destructor called, x=36
thread spawned
copy constructor called, x=62
thread finished, x=100009889
destructor called, x=100009889
destructor called, x=61
thread joined
destructor called, x=92
compiled with gcc 4.9.2, no optimization.
There are a lot of copying/moving going on in the background. Note however, that neither the copy constructor nor the move constructor is called when the thread constructor is called.
Consider a function like this:
template<typename T> void foo(T&& arg);
When you have r-value references to template arguments C++ treats this a bit special. I will just outline the rules here. When you call foo with an argument, the argument type will be
&& - when the argument is an r-value
& - all other cases
That is, either the argument will be passed as an r-value reference or a standard reference. Either way, no constructor will be invoked.
Now look at the constructor of the thread object:
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
This constructor applies the same syntax, so arguments will never be copied/moved into the constructor arguments.
The below code contains an example.
#include <iostream>
#include <thread>
class Foo{
public:
int id;
Foo()
{
id = 1;
std::cout << "Default constructor, id = " << id << std::endl;
}
Foo(const Foo& f)
{
id = f.id + 1;
std::cout << "Copy constructor, id = " << id << std::endl;
}
Foo(Foo&& f)
{
id = f.id;
std::cout << "Move constructor, id = " << id << std::endl;
}
};
void doNothing(Foo f)
{
std::cout << "doNothing\n";
}
template<typename T>
void test(T&& arg)
{
}
int main()
{
Foo f; // Default constructor is called
test(f); // Note here that we see no prints from copy/move constructors
std::cout << "About to create thread object\n";
std::thread t{doNothing, f};
t.join();
return 0;
}
The output from this code is
Default constructor, iCount = 1
About to create thread object
Copy constructor, id = 2
Move constructor, id = 2
Move constructor, id = 2
doNothing
First, the object is created.
We call our test function just to see that nothing happens, no constructor calls.
Because we pass in an l-value to the thread constructor the argument has type l-value reference, hence the object is copied (with the copy constructor) into the thread object.
The object is moved into the underlying thread (managed by the thread object)
Object is finally moved into the thread-function doNothing's argument
int main(int argc, char *argv[]) {
sampleClass SC; // default constructor
std::thread t1 (sampleThread, SC); // Two copies inside thread constructor,
//use std::ref(SC) to avoit it
//..
}
void sampleThread(sampleClass SC) { // copy SC: pass by ref to avoid it
// but then modifications are for original and not the copy
// ...
}
Fixed version Demo