Deleted copy constructor when member is an unique_ptr [duplicate] - c++

This question already has answers here:
Copy constructor for a class with unique_ptr
(6 answers)
Closed 4 years ago.
This code works fine:
class Test
{
int* ptr = new int(10);
};
int main()
{
Test o;
Test t = o;
}
But when we use unique_ptr instead raw ptr, we get an error:
error: use of deleted function 'Test::Test(const Test&)'
And sample code:
class Test
{
std::unique_ptr<int> ptr = std::make_unique<int>(1);
};
int main()
{
Test o;
Test t = o;
}
What is going on?

What is going on?
You cannot create a second instance of Test because this implies you need a copy of the unique_ptr, which is not possbile. A unique_ptr can only be moved. Try implementing a move asignment operator and move o like so:
class Test
{
public:
Test() = default;
Test(Test&& other) noexcept
: ptr(std::move(other.ptr))
{
}
private:
std::unique_ptr<int> ptr = std::make_unique<int>(1);
};
int main()
{
Test o;
Test t = std::move(o);
}
If you want to copy the int underlying the unique_ptr, you need to define a custom copy constructor like this:
class Test
{
public:
Test() = default;
Test(const Test& other)
:
ptr(new int(*other.ptr))
{
}
Test(Test&& other) noexcept
: ptr(std::move(other.ptr))
{
}
private:
std::unique_ptr<int> ptr = std::make_unique<int>(1);
};
int main()
{
Test o;
Test t = o;
}
However, NOTE, that the pointers point to two DIFFERENT ints.
If you want shared ownership, you have to (and should) use shared_ptr like this:
class Test
{
private:
std::shared_ptr<int> ptr = std::make_shared<int>(1);
};
int main()
{
Test o;
Test t = o;
}

What is going on?
You've swapped from a naked pointer, to one that enforces ownership semantics. One that's smart.
This is literally the purpose of unique_ptr.
It enforces unique ownership.
You can't copy it; only move it.
If you really need Test to be copyable, and to be able to share ints, you're probably looking for shared_ptr.

Related

How to return a class instance on the heap, when the relevant ctor is private?

Suppose I have this struct
struct MyStruct {
static MyStruct Create(int x) {
return { x*2, x>3 };
}
MyStruct(const MyStruct& c) = delete; // no copy c'tor
private:
MyStruct(int a_, bool b_) : a(a_), b(b_) {} // private c'tor -- can't use new
const int a;
const bool b;
};
Edit: I deleted the copy constructor. This is simplified example of some classes I have in my codebase where they don't have copy c'tors.
I can get an instance on the stack like so:
int main() {
auto foo = MyStruct::Create(2);
return 0;
}
But suppose I need a pointer instead (or unique_ptr is fine), and I can't change the implementation of MyStruct, how can I do that?
You could wrap MyStruct in another class, which has a MyStruct member. Here's a minimal version of that:
class Wrapper {
public:
MyStruct ms;
Wrapper(int x) : ms(MyStruct::Create(x)) { }
};
which you can use like so:
int main() {
MyStruct::Create(2);
std::make_unique<Wrapper>(2);
}
This code will not trigger any copies nor moves - because of copy elision (see: What are copy elision and return value optimization?).
You can then add any other constructors and methods you like to such a wrapper, possibly forwarding some of the method calls to the ms member. Some might choose to make ms protected or private.
Is this what you're looking for?
auto baz = std::make_unique<MyStruct>( MyStruct::Create(2) ); // unique pointer
A comment rather than an answer, to avoid confusion for future readers.
I can get an instance on the stack like so:
int main() {
auto foo = MyStruct::Create(2);
return 0;
}
Note that this is only true as of C++17 and guaranteed copy elision, whereas the program is ill-formed is C++14, as even if the copy may be elided, the initialization of foo is copy-initialization from a temporary (in C++17: the temporary is never materialized).
One more way to do it:
struct ChildStruct : public MyStruct {
ChildStruct(int x) : MyStruct(MyStruct::Create(x))
{}
};
int main() {
MyStruct *foo1 = new ChildStruct(2);
return 0;
}
C style solution. I am not sure that this is not UB, but for simple struct with 2 integer fields it should work.
int main() {
auto foo = MyStruct::Create(2);
MyStruct *p = (MyStruct*)malloc(sizeof(MyStruct));
memcpy(p, &foo, sizeof(MyStruct));
//...
free(p);
return 0;
}

Will containing a `std::unique_ptr` make the default copy constructor a deleted function? [duplicate]

This question already has answers here:
Copy constructor for a class with unique_ptr
(6 answers)
Closed 4 years ago.
This code works fine:
class Test
{
int* ptr = new int(10);
};
int main()
{
Test o;
Test t = o;
}
But when we use unique_ptr instead raw ptr, we get an error:
error: use of deleted function 'Test::Test(const Test&)'
And sample code:
class Test
{
std::unique_ptr<int> ptr = std::make_unique<int>(1);
};
int main()
{
Test o;
Test t = o;
}
What is going on?
What is going on?
You cannot create a second instance of Test because this implies you need a copy of the unique_ptr, which is not possbile. A unique_ptr can only be moved. Try implementing a move asignment operator and move o like so:
class Test
{
public:
Test() = default;
Test(Test&& other) noexcept
: ptr(std::move(other.ptr))
{
}
private:
std::unique_ptr<int> ptr = std::make_unique<int>(1);
};
int main()
{
Test o;
Test t = std::move(o);
}
If you want to copy the int underlying the unique_ptr, you need to define a custom copy constructor like this:
class Test
{
public:
Test() = default;
Test(const Test& other)
:
ptr(new int(*other.ptr))
{
}
Test(Test&& other) noexcept
: ptr(std::move(other.ptr))
{
}
private:
std::unique_ptr<int> ptr = std::make_unique<int>(1);
};
int main()
{
Test o;
Test t = o;
}
However, NOTE, that the pointers point to two DIFFERENT ints.
If you want shared ownership, you have to (and should) use shared_ptr like this:
class Test
{
private:
std::shared_ptr<int> ptr = std::make_shared<int>(1);
};
int main()
{
Test o;
Test t = o;
}
What is going on?
You've swapped from a naked pointer, to one that enforces ownership semantics. One that's smart.
This is literally the purpose of unique_ptr.
It enforces unique ownership.
You can't copy it; only move it.
If you really need Test to be copyable, and to be able to share ints, you're probably looking for shared_ptr.

Assignment operator as copy constructor

Assignment operator can be used to copy the value of one object to another
instead of using copy constructor,then why we required a copy constructor?
class example
{
int data;
public:
example()
{
}
example(int x)
{
data = x;
}
};
int main()
{
example a(50);
example a(b);
//same can be done with the assignment operator
//b = a;
return 0;
}
Because at the point of calling a copy constructor, the object being copied to doesn't yet exist.
An assignment operator assigns the value of another object to one that does exist.
Devices such as member initialisation can be used with a copy constructor, but are not available on assignment. Furthermore it's possible to create a const object using a copy constructor.
Furthermore, the assignment operator typically returns a reference to self.
So a copy constructor and the assignment operator probably will leave the mutated object in an identical state, but it doesn't necessarily have to be the case.
As Bathsheba already said: A copy constructor creates a new object, an assignment operator assigns values to an already existing object. One needs to construct a new object, the other needs to handle whatever happens if you assign the values from one object to another. Take this example:
class Foo
{
public:
Foo(int x) { someValue = x; };
int getValue() const { return someValue; };
private:
int someValue;
}
class Bar
{
public:
Bar(int y)
{
myFoo = new Foo(y);
myValue = y + 1;
myInitDone = true;
};
Bar(const Bar& other)
{
//myFoo was not yet initalized, so no need to clean it up
myFoo = new Foo(other.myFoo->getValue());
myValue = other.myValue;
myInitDone = true;
}
Bar& operator=(const Bar& other)
{
delete myFoo; // If we don't clean up myFoo here we leak memory
myFoo = new Foo(other.myFoo->getValue());
myValue = other.myValue;
// myInitDone is only set during construction due to [reason]
}
private:
Foo* myFoo;
int myValue;
bool myInitDone;
}
The copy constructor needs to set myInitDone (which is only done during constuction because [insert reason here]), while the assigment operator needs to clean up myFoo or it will leak memory.

How to destroy a smart pointer prematurely

I have a class that has a setter method which takes a unique_ptr as an argument. That unique_ptr is saved as a class member.
class TestClass {
std::unique_ptr<Tester> sp;
void setTester_Way1(std::unique_ptr<Tester> te) {
auto deleter=std::move(sp);
sp=std::move(te);
}
void setTester_Way2(std::unique_ptr<Tester> te) {
sp=std::move(te);
}
};
Which way is the correct way to set the smart pointer? Does Way2 leak the original pointer of sp?
Way2 is fine, when you assign to a unique_ptr any existing owned pointer will be safely deleted.
As Chris Drew said Way2 is fine. One thing though is that unique_ptr is not copyable/assignable so the only ways to pass a unique_ptr is by reference, r-value reference or by value with move(). Trying to do:
int main()
{
TestClass t;
auto p = std::make_unique<int>(10);
t.setTester_Way2(p);
}
Will fail to compile. Although you can move() p into the function(example).
If you change setTester_Way2() to void setTester_Way2(std::unique_ptr<int>& te) then it will compile. If you change the function to take an rvalue reference and std::move() the pointer into the funcnction:
class TestClass {
std::unique_ptr<int> sp;
public:
void setTester_Way1(std::unique_ptr<int> te) {
auto deleter=std::move(sp);
sp=std::move(te);
}
void setTester_Way2(std::unique_ptr<int>&& te) {
sp=std::move(te);
}
};
int main()
{
TestClass t;
auto p = std::make_unique<int>(10);
t.setTester_Way2(std::move(p));
}
Then it will also compile.

Is this C++ reassignment valid?

Sorry for the basic question, but I'm having trouble finding the right thing to google.
#include <iostream>
#include <string>
using namespace std;
class C {
public:
C(int n) {
x = new int(n);
}
~C( ) {
delete x;
}
int getX() {return *x;}
private:
int* x;
};
void main( ) {
C obj1 = C(3);
obj1 = C(4);
cout << obj1.getX() << endl;
}
It looks like it does the assignment correctly, then calls the destructor on obj1 leaving x with a garbage value rather than a value of 4. If this is valid, why does it do this?
If there is a class C that has a constructor that takes an int, is this code valid?
C obj1(3);
obj1=C(4);
Assuming C has an operator=(C) (which it will by default), the code is valid. What will happen is that in the first line obj1 is constructed with 3 as a the parameter to the constructor. Then on the second line, a temporary C object is constructed with 4 as a parameter and then operator= is invoked on obj1 with that temporary object as a parameter. After that the temporary object will be destructed.
If obj1 is in an invalid state after the assignment (but not before), there likely is a problem with C's operator=.
Update: If x really needs to be a pointer you have three options:
Let the user instead of the destructor decide when the value of x should be deleted by defining a destruction method that the user needs to call explicitly. This will cause memory leaks if the user forgets to do so.
Define operator= so that it will create a copy of the integer instead of a copy of the value. If in your real code you use a pointer to something that's much bigger than an int, this might be too expensive.
Use reference counting to keep track how many instances of C hold a pointer to the same object and delete the object when its count reaches 0.
If C contains a pointer to something, you pretty much always need to implement operator=. In your case it would have this signature
class C
{
public:
void operator=(const C& rhs)
{
// For each member in rhs, copy it to ourselves
}
// Your other member variables and methods go here...
};
I do not know enough deep, subtle C++ to explain the problem you are encountering. I do know, however, that it's a lot easier to make sure a class behaves the way you expect if you follow the Rule of Three, which the code you posted violates. Basically, it states that if you define any of the following you should define all three:
Destructor
Copy constructor
Assignment operator
Note as well that the assignment operator implementation needs to correctly handle the case where an object is assigned to itself (so-called "self assignment"). The following should work correctly (untested):
#include <iostream>
#include <string>
using namespace std;
class C {
public:
C(int n) {
x = new int(n);
}
C(const C &other): C(other.getX()) { }
~C( ) {
delete x;
}
void operator=(const C &other) {
// Just assign over x. You could reallocate if you first test
// that x != other.x (the pointers, not contents). The test is
// needed to make sure the code is self-assignment-safe.
*x = *(other.x);
}
int getX() {return *x;}
private:
int* x;
};
void main( ) {
C obj1 = C(3);
obj1 = C(4);
cout << obj1.getX() << endl;
}
Basically you are trying to re-implement a smart pointer.
This is not trivial to get correct for all situations.
Please look at the available smart pointers in the standard first.
A basic implementation (Which will fail under certain situations (copy one of the standard ones to get a better one)). But this should cover the basics:
class X
{
int* data;
public:
// Destructor obvious
~X()
{
delete data;
}
// Easy constructor.
X(int x)
:data(new int(x))
{}
// Copy constructor.
// Relatively obvious just do the same as the normal construcor.
// Get the value from the rhs (copy). Note A class is a friend of
// itself and thus you can access the private members of copy without
// having to use any accessor functions like getX()
X(X const& copy)
:data(new int(copy.x))
{}
// Assignment operator
// This is an example of the copy and swap idiom. This is probably overkill
// for this trivial example but provided here to show how it is used.
X& operator=(X const& copy)
{
X tmp(copy);
this->swap(tmp);
return this;
}
// Write a swap() operator.
// Mark it is as no-throw.
void swap(X& rhs) throws()
{
std::swap(data,rhs.data);
}
};
NEW:
What's happening is that your destructor has deallocated the memory allocated by the constructor of C(4). So the pointer you have copied over from C(4) is a dangling pointer i.e. it still points to the memory location of the deallocated memory
class C {
public:
C(int n) {
x = new int(n);
}
~C( ) {
//delete x; //Don't deallocate
}
void DeallocateX()
{
delete x;
}
int getX() {return *x;}
private:
int* x;
};
int main(int argc, char* argv[])
{
// Init with C(3)
C obj1 = C(3);
// Deallocate C(3)
obj1.DeallocateX();
// Allocate memory and store 4 with C(4) and pass the pointer over to obj1
obj1 = C(4);
// Use the value
cout << obj1.getX() << endl;
// Cleanup
obj1.DeallocateX();
return 0;
}
Be explicit about ownership of pointers! auto_ptr is great for this. Also, when creating a local don't do C obj1 = C(3) that creates two instances of C and initializes the first with the copy constructor of the second.
Heed The Guru.
class C {
public:
C(int n) : x(new int(n)) { }
int getX(){ return *x; }
C(const C& other) : x(new int(*other.x)){}
C& operator=(const C& other) { *x = *other.x; return *this; }
private:
std::auto_ptr<int> x;
};
int main() {
C obj1(3);
obj1 = C(4);
std::cout << obj1.getX() << std::endl;
}
When are you testing the value of obj1? Is it after you leave the scope?
In your example, obj1 is a stack object. That means as soon as you leave the function in which it defined, it gets cleaned up (the destructor is called). Try allocating the object on the heap:
C *obj1 = new C(3);
delete obj1;
obj1 = new C(4);