Copy constructor for class with shared_ptr data members? - c++

I know how to write a copy constructor when you have raw pointer data members in a class, but how do you write a copy constructor when you manage these with a shared_ptr?
Is there a copy() or clone() function which should be called? Interestingly I have never seen such an example.

The copy constructor for std::shared_ptr creates a second pointer which shared ownership with the first pointer. The pointee will be destroyed when all std::shared_ptr that point to it are destroyed.
Without a copy constructor explicitly declared, the language specification decrees that a suitable one will be implicitly provided, and that it will call the copy constructors of each member of your class.
So, in other words, the implicit copy constructor provided for your class will call shared_ptr<T>::shared_ptr(const shared_ptr<T> &), which will create a second share pointer pointing to the same object, and it doesn't look like that's what you want. If you want a deep copy of the pointee, you will have to declare your own copy constructor that creates one:
class Foo
{
Foo(const Foo &other)
: foobar(new Bar(*other.foobar.get()))
{}
shared_ptr<Bar> foobar;
}
References:
std::shared_ptr constructor documentation
Copy constructor semantics in C++98 (see section 12.8.8) and C++11 (see section 12.8.16)

std::shared_ptr has its own copy constructor that will handle the reference counting of the managed shared data.
struct Foo {
Foo(int i) : ptr{std::make_shared<int>(i)} {}
std::shared_ptr<int> ptr;
};
int main() {
Foo f1{0};
Foo f2{f1}; // Using the implicitly generated copy ctor.
*f1.ptr = 1;
std::cout << *f1.ptr << std::endl; // 1 (data is shared).
std::cout << *f2.ptr << std::endl; // 1 (data is shared).
}
If you want your class to have resource ownership semantics then simply declare the resource as an object with automatic storage duration.
struct Foo2 {
Foo2(int i) : i{i} {}
int i; // Automatic object.
};
int main() {
Foo2 f1{0};
Foo2 f2{f1};
f1.i = 1;
std::cout << f1.i << std::endl; // 1
std::cout << f2.i << std::endl; // 0
}

Related

placement new to circumvent assignment constructor

Is it a viable solution to use placement new to circumvent copy assignment?
I have a member object that contains const members.
The object itself is meant to be created at runtime, but it's members are meant to be const.
I was wondering whether I could use placement new to circumvent the copy assignment at runtime?
#include <new>
#include <iostream>
struct A {
const int a;
A() : a(0) {};
A(int a) : a(a){}
};
struct B {
A a;
void createA() {
new(&a) A(69);
}
void useA() {
std::cout << a.a << std::endl;
}
};
int main(void) {
B b;
b.createA();
b.useA();
return 0;
}
It compiles and runs but does it invoke UB?
You should not do this, though it will technically work in this case.
By circumventing the copy assignment you are not giving the object a chance to perform any required cleanup on the old data (e.g. freeing memory resources). In this example, you are calling the new placement operator before the lifetime of a ends.
The following will work as it does not violate a's lifetime:
a.~A(); // manually destruct a, end it's lifetime
new(&a) A(69); // construct a new instance and start a new lifetime

Proving assigments in constructor's body are inefficient

I read in http://www.cs.technion.ac.il/users/yechiel/c++-faq/init-lists.html
that using initializer lists is more efficient than doing assigment in the body of the constructor, because for example in Fred::Fred() { x_ = whatever; }
1) the expression whatever causes a separate, temporary object to be created, and this temporary object is passed into the x_ object's assignment operator. Then that temporary object is destructed at the ;
2)the member object will get fully constructed by its default constructor, and this might, for example, allocate some default amount of memory or open some default file
How can assigment cause the creation of some temporary object inside the constructor? That means that the Construtor would call itself: an infinite recursive call
I made the following piece of code to verify a copy of the object is created in the assigment process,hoping to see the additional creation and destruction of the temporary object but all I can see is the creating and destruction of the object I am creating in the main an of course no infinite recursive call.
How do I make sense of it and how can I modify the code to see the creation and destruction of the temporary object?
#include<iostream>
using namespace std;
class Base
{
private:
int c_var;
public:
Base( )
{ c_var=10;
cout <<"Constructor called"<<endl;
cout << "Value is " << c_var<<endl;
}
~Base()
{
cout <<"Destructor called"<<endl;
}
};
int main()
{
Base il;
}
How can assigment cause the creation of some temporary object inside the constructor? That means that the Construtor would call itself: an infinite recursive call
No. The text talks about calling the constructor of the member. With an int it doesn't matter too much, but consider:
struct foo {
foo() {
/* construct a foo, with expensive instructions */
std::cout << "default constructor";
}
foo(int x) {
/* also construct a foo */
std::cout << "constructor";
}
};
struct bar_wrong {
foo f;
bar_wrong() {
f = foo(42);
}
};
Members are initialized before the body of the constructor is executed. Hence the foo member of bar_wrong will first be default constructed (which is potentially expensive) just to be overwritten with the right instance later and creating a bar_wrong will print both outputs from foos constructors.
The correct way is
struct bar_correct {
foo f;
bar_correct() : f(42) {}
};
Because here foo is only initialized. Alternatively you can use in-class initializers:
struct bar_alternative {
foo f{42};
};
Here the compiler generated constructor is sufficient. It will use the in-class initializer to initialize f with 42.

C++ Assignment operator for class that contains a unique pointer member variable

I understand that if I have a class with a smart unique pointer, it is not possible to assign that class to another instance, as a unique pointer cannot be copied. I understand that I could make the unique pointer a shared pointer and this would solve the problem. But what if I did not want to share ownership of the pointer? Is it possible to create an assignment operator that moves the unique pointer and copies the other variables?
I have read that you can use std::move to pass ownership.
#include <iostream>
#include <memory>
struct GraphStructure { };
class test {
int a;
std::vector<int> vector;
std::unique_ptr<GraphStructure> Graph_;
};
int main () {
test t1;
auto t2 = t1;
}
The default copy constructor of class test is deleted because of a member (graph_) not being copiable (if you still could copy in any meaningful way, e. g. by creating a deep copy of the graph member, you'd have to implement on your own copy constructor). In contrast, the default move constructor still exists (std::unique_ptr is movable). So what you can do is the following:
test t1;
auto t2 = std::move(t1);
Be aware, though, that t1 then won't hold any object any more (you moved the object, so you moved its contents to another one) and the object previously held by t2 is destroyed. If this is a meaningful state is up to you to decide...
Side note: What I wrote about copy and move constructors applies for copy and move assignment as well...
Fixing this the easy way
If GraphStructure is a class or struct without any virtual member functions, this is easy to do. We can write a function to duplicate the data inside a unique_ptr to create a new GraphStructure:
std::unique_ptr<GraphStructure> duplicate(std::unique_ptr<GraphStructure> const& ptr)
{
return std::make_unique<GraphStructure>(*ptr);
}
Once we have duplicate, we can use this class to write a copy constructor for test:
class test {
std::unique_ptr<GraphStructure> ptr;
std::vector<int> values;
public:
// this can be defaulted
test() = default;
// we use duplicate to create a copy constructor
test(const test& source)
: ptr(duplicate(source.ptr)))
, values(source.values)
{}
// we can use the default move constructor
test(test&&) = default;
test& operator=(test const& source) {
ptr = duplicate(source.ptr);
values = source.values;
return *this;
}
// we can use the default move assignment operator
test& operator=(test&&) = default;
};
What if GraphStructure has virtual methods?
In this case, add a virtual clone method to GraphStructure that returns a new std::unique_ptr<GraphStructure>:
class GraphStructure {
public:
// override this method in child classes
virtual std::unique_ptr<GraphStructure> clone() {
return std::make_unique<GraphStructure>(*this);
}
virtual ~GraphStructure() {}
};
Then, use .clone() in place of duplicate

Override delete operator with empty implementation

Using the delete operator on an object normally leads to two things: calling the object's destructor (and its virtual base destructors, if present) and freeing the memory afterwards.
If override the delete operator on a class giving it an empty implementation {}, the destructor will still be called, but the memory does not get freed.
Assuming the destructor is also empty, will the delete then have any effect or would it be safe to continue using the "deleted" object (i.e. is there undefined behaviour)?
struct Foo {
static void operator delete(void* ptr) {}
Foo() {}
~Foo() {}
void doSomething() { ... }
}
int main() {
Foo* foo = new Foo();
delete foo;
foo->doSomething(); // safe?
}
Not that this would make much sense as it is, but I'm investigating in a "deferred delete" (gc) mechanism where objects won't get deleted instantly when delete gets called but shortly afterwards.
Update
Referring to some answers that mention memory leaks: let's assume the overloaded delete operator is not empty, but does store its ptr argument in a (let's say static, for the sake of simplicity) set:
struct Foo {
static std::unordered_set<void*> deletedFoos;
static void operator delete(void* ptr) {
deletedFoos.insert(ptr);
}
Foo() {}
~Foo() {}
}
And this set gets cleaned up periodically:
for (void* ptr : Foo::deletedFoos) {
::operator delete(ptr);
}
Foo::deletedFoos.clear();
From n4296:
A destructor is invoked implicitly
(11.1) — for a constructed object with static storage duration (3.7.1)
at program termination (3.6.3),
(11.2) — for a constructed object with thread storage duration (3.7.2)
at thread exit,
(11.3) — for a constructed object with automatic storage duration
(3.7.3) when the block in which an object is created exits (6.7),
(11.4) — for a constructed temporary object when its lifetime ends
(12.2).
In each case, the context of the invocation is the context of the
construction of the object. A destructor is also invoked implicitly
through use of a delete-expression (5.3.5) for a constructed object
allocated by a new-expression (5.3.4); the context of the invocation
is the delete-expression. [ Note: An array of class type contains
several subobjects for each of which the destructor is invoked. —end
note ] A destructor can also be invoked explicitly.
Thus, the very use of delete expression that calls delete operator, you implicitly call destructor as well. Object's life ended, it's an undefined behavior what happens if you will call a method for that object.
#include <iostream>
struct Foo {
static void operator delete(void* ptr) {}
Foo() {}
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << " called\n"; }
};
int main() {
Foo* foo = new Foo();
delete foo;
foo->doSomething();
// safe? No, an UB. Object's life is ended by delete expression.
}
Output:
Destructor called
void Foo::doSomething() called
used: gcc HEAD 8.0.0 20170809 with -O2
The question starts with assumption that redefining delete operator and behaviour of object would omit destruction of object. Redefining destructor of object itself will not redefine destructors of its fields.
In fact it won't exist anymore from semantics point of view. It will not deallocate memory, which might be a thing if object is stored in memory pool. But it would delete abstract 'soul' of object, so to say. Calling methods or accessing fields of object after that is UB.
In particular case, depending on operation system, that memory may stay forever allocated. Which is an unsafe behavior. It also unsafe to assume that compiler would generate sensible code. It may omit actions altogether.
Let me add some data to object:
struct Foo {
int a;
static void operator delete(void* ptr) {}
Foo(): a(5) {}
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << "a = " << a << " called\n"; }
};
int main() {
Foo* foo = new Foo();
delete foo;
foo->doSomething(); // safe?
}
Output:
Destructor called
void Foo::doSomething() a= 566406056 called
Hm? We didn't initialized memory? Let's add same call before destruction.
int main() {
Foo* foo = new Foo();
foo->doSomething(); // safe!
delete foo;
foo->doSomething(); // safe?
}
Output here:
void Foo::doSomething() a= 5 called
Destructor called
void Foo::doSomething() a= 5 called
What? Of course, compiler just omitted initialization of a in first case. Could it be because class doesn't do anything else? In this case it is possible. But this:
struct Foo {
int a, b;
static void operator delete(void* ptr) {}
Foo(): a(5), b(10) {}
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
};
int main() {
Foo* foo = new Foo();
std::cout << __PRETTY_FUNCTION__ << " b= " << foo->b << "\n";
delete foo;
foo->doSomething(); // safe?
}
will generate similar undefined value:
int main() b= 10
Destructor called
void Foo::doSomething() a= 2017741736 called
Compiler had considered field a unused by the time of death of foo and thus "dead" without impact on further code. foo went down with all "hands" and none of them formally do exist anymore. Not to mention that on Windows, using MS compiler those programs would likely crash when Foo::doSomething() would try to revive the dead member. Placement new would allow us to play Dr.Frankenstein role:
#include <iostream>
#include <new>
struct Foo {
int a;
static void operator delete(void* ptr) {}
Foo() {std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
Foo(int _a): a(_a) {std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
};
int main() {
Foo* foo = new Foo(5);
foo->~Foo();
Foo *revenant = new(foo) Foo();
revenant->doSomething();
}
Output:
Foo::Foo(int) a= 5 called
Destructor called
Foo::Foo() a= 1873730472 called
void Foo::doSomething() a= 1873730472 called
Irregardless to wit if we call destructor or not, compilers are allowed to decide that revenant isn't same thing as original object, so we can't reuse old data, only allocated memory.
Curiously enough, while still performing UB, if we remove delete operator from Foo, that operation seem to work as expected with GCC. We do not call delete in this case, yet removal and addition of it changes compiler behavior, which, I believe, is an artifact of implementation.
From N4296 (~C++14):
3.8 Object lifetime [basic.life]
...
The lifetime of an object of type T ends when:
(1.3) — if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
(1.4) — the storage which the object occupies is reused or released.
Then:
12.4 Destructors [class.dtor]
...
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial destructors, and
(5.6) — for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Otherwise, the destructor is non-trivial.
So basically, for simple enough classes, this is safe, but if you have any kind of resource-owning classes involved it is not going to be legal.
Take care, though, that in your example your destructor is user-provided, hence non-trivial.
Whether your hack can work depends on the members of the class. The destructor will always call the destructors of the class members. If you have any members that are strings, vectors, or other objects with an 'active' destructor, these objects will be destroyed, even tough the memory allocated for the containing object is still allocated.
The main problem is that even if have empty dtor's, other background things, like the release of virtual method tables, will happen. My personal approach in such cases is to make a private dtor and use a member method (name it destroy if you want) for filtering the delete operator. For example
class A
{
bool _may_be_deleted;
public:
A(bool may_be_deleted)
: _may_be_deleted(may_be_deleted){;}
void allow_delete()
{
_prevent_delete = false;
}
static bool destroy(A*);
private:
virtual ~A(){;}
};
bool A::destroy(A *pA)
{
if(pA->_may_be_deleted)
{
delete pA;
return true;
}
return false;
}
int main(int argc, char* argv[])
{
A* pA = new A(false);
A::destroy(pA); //returns false and A is not deleted
pA->allow_delete();
A::destroy(pA); //Ok, now A is destroyed and returns true;
}
Hope that helps.

Will a class recall its constructor when pushed in a std::vector?

if for example I do:
FOO foo;
foovect.push_back(foo);
where FOO is a class with a default constructor.
Will the constructor be called only when foo is put on the stack, or is it called again when pushed into the std::vector?
Thanks
I'm doing:
OGLSHAPE::OGLSHAPE(void)
{
glGenBuffersARB(GL_ARRAY_BUFFER_ARB,&ObjectVBOInt);
}
FOO foo; would call the constructor.
foovect.push_back(foo); would call the copy constructor.
#include <iostream>
#include <vector>
class FOO
{
public:
FOO()
{
std::cout << "Constructor" << std::endl;
}
FOO(const FOO& _f)
{
std::cout << "Copy Constructor" << std::endl;
}
};
int main()
{
FOO foo;
std::vector<FOO> foovect;
foovect.push_back(foo);
}
Output for this:
Constructor
Copy Constructor
No, the copy constructor is used, i.e. the one that looks like this:
FOO( const FOO & f );
A default copy constructor is provided by the compiler, if you don't provide one yourself.
When you do a push_back, your object is copied into the vector. That means the copy constructor for your object gets called. All the standard library containers deal with copies of objects, not the object themselves. If you want that behavior, you'll need to resort to using pointers.