Deep copy with unique_ptr in map - c++

I would like to create a deep copy of a class that contains a unique_ptr.
How can this be achieved?
I have the following example:
#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>
class test;
typedef std::unordered_map<std::string, std::string> sMap;
typedef std::unordered_map<std::string, std::unique_ptr<test>> testMap;
class test {
public:
sMap map1;
testMap map2;
test(){}
// test(const test& t)
// :
// map1(t.map1),
// map2(t.map2) ??
// {}
};
int main() {
sMap myMap;
myMap["a"] = "b";
std::unique_ptr<test> tobj= std::make_unique<test>();
test& obj = *tobj;
obj.map1 = myMap;
test obj2;
obj2.map2.emplace("one", std::move(tobj));
// test obj3 (obj2); // error
return 0;
}
How can I copy the data in the map2 when calling the copy constructor?
Kind regards

If you want to copy the instances that the unique_ptrs point at, you need to do so manually because unique_ptrs are non-copyable. No two unique_ptrs should ever point at the same instance!
Example:
class test {
public:
sMap map1;
testMap map2;
test() = default;
test(const test& other) : map1(other.map1) { // copy constructor
for(auto&[str, ptr] : other.map2) {
if(ptr) // recursively copy construct:
map2.emplace(str, std::make_unique<test>(*ptr));
else // empty pointer, put `nullptr` in there:
map2.emplace(str, nullptr);
}
}
test(test&& other) noexcept = default; // move constructor
test& operator=(const test& other) { // copy assignment
// copy construct an instance and move assign it:
*this = test(other);
return *this;
}
test& operator=(test&& other) noexcept = default; // move assignment
};

Related

Writing a copyable unique_ptr for copyable classes with PIMPL idiom

I want to write a class that uses the PIMPL idiom but makes the following possible:
int main() {
MyClass myclass {12};
std::string val = myclass.get_value();
std::cout << val << "\n";
MyClass copy = myclass;
val = copy.get_value();
return 0;
}
That is, provide copy functionality while using PIMPL. Using std::unique_ptr to implement PIMPL leads to the expected compile error:
error C2280: 'MyClass::MyClass(const MyClass &)': attempting to reference a deleted function
This is my attempt:
class.hpp
#pragma once
#include <string>
#include "copyable_unique_ptr.hpp"
class MyClass {
private:
struct MyClassPrivate;
copyable_unique_ptr<MyClassPrivate> _impl;
public:
MyClass(int value);
~MyClass();
std::string get_value();
};
class.cpp
#include "class.hpp"
#include <string>
struct MyClass::MyClassPrivate {
int value;
};
MyClass::MyClass(int value)
: _impl(std::make_unique<MyClassPrivate>()) {
_impl->value = value;
}
MyClass::~MyClass() = default;
std::string MyClass::get_value() {
return std::to_string(_impl->value);
}
copyable_unique_ptr.hpp
#pragma once
#include <memory>
template <typename T>
class copyable_unique_ptr {
private:
std::unique_ptr<T> _ptr;
public:
copyable_unique_ptr(std::unique_ptr<T> &&ptr)
: _ptr(std::move(ptr)) {}
~copyable_unique_ptr() = default;
copyable_unique_ptr(const copyable_unique_ptr<T> &other) {
_ptr = std::make_unique<T>(*other._ptr);
}
copyable_unique_ptr(copyable_unique_ptr<T> &&other) {
_ptr = std::move(other._ptr);
}
copyable_unique_ptr &operator=(const copyable_unique_ptr<T> &other) {
_ptr = std::make_unique<T>(*other._ptr);
}
copyable_unique_ptr &operator=(copyable_unique_ptr<T> &&other) {
_ptr = std::move(other._ptr);
}
T *operator->() {
return _ptr.get();
}
};
which leads to the following error:
warning C4150: deletion of pointer to incomplete type 'MyClass::MyClassPrivate'; no destructor called
I also had this error when first trying to use unique_ptr, and the solution was to move the destructor into class.cpp: MyClass::~MyClass() = default;.
I don't really know how to solve this problem while using the approach with copyable_unique_ptr.
I saw this question on SO. They are trying to do the same as me, but the last comment to the answer suggests that they are having exactly the same problem.
Although you can write a custom smart pointer here, I always like to reuse as much code as possible. It seems you want a dynamically created object with copy semantics rather than pointer semantics. For that reason I would hesitate to call your copyable_unique_ptr a pointer at all. I did write a class for this once and settled on something like dynamic_object<T>.
Instead of a pointer I would do this using a std::vector that contains just a single element. Something a bit like this:
class MyClass {
private:
struct MyClassPrivate;
std::vector<MyClassPrivate> _impl;
public:
MyClass(int value);
~MyClass();
std::string get_value();
};
struct MyClass::MyClassPrivate {
int value;
};
MyClass::MyClass(int value)
: _impl(1) {
_impl.front().value = value;
}
MyClass::~MyClass() = default;
std::string MyClass::get_value() {
return std::to_string(_impl.front().value);
}
However if you really want to create your copyable_unique_ptr I woud still use a std::vector here to simplify its codeing and to reuse high quality Standard Library code:
Perhaps a bit like this:
template<typename T>
class copyable_unique_ptr
: private std::vector<T>
{
using base_type = std::vector<T>;
public:
copyable_unique_ptr(std::unique_ptr<T>&& ptr)
: base_type(std::move(*ptr), 1) {}
copyable_unique_ptr(std::unique_ptr<T> const& ptr)
: base_type(*ptr, 1) {}
T& operator*() { return base_type::back(); }
T* operator->() { return base_type::data(); }
T const& operator*() const { return base_type::back(); }
T const* operator->() const { return base_type::data(); }
};
Sam Varshavchik gave a hint in the comment that also moving copy constructor and assignment operator to the implementation fixes the code that I was trying to write, that means:
class.hpp
#pragma once
#include <string>
#include "copyable_unique_ptr.hpp"
class MyClass {
private:
struct MyClassPrivate;
copyable_unique_ptr<MyClassPrivate> _impl;
public:
MyClass(int value);
MyClass(const MyClass &);
MyClass(MyClass &&);
MyClass &operator=(const MyClass &);
MyClass &operator=(MyClass &&);
~MyClass();
std::string get_value();
};
class.cpp
#include "class.hpp"
#include <string>
#include <iostream>
struct MyClass::MyClassPrivate {
int value;
};
MyClass::MyClass(int value)
: _impl(std::make_unique<MyClassPrivate>()) {
_impl->value = value;
}
MyClass::MyClass(const MyClass &) = default;
MyClass::MyClass(MyClass &&) = default;
MyClass &MyClass::operator=(const MyClass &) = default;
MyClass &MyClass::operator=(MyClass &&) = default;
MyClass::~MyClass() = default;
std::string MyClass::get_value() {
std::cout << &_impl << "\n";
return std::to_string(_impl->value);
}
And it works as expected.
On the other hand, Galik gave an answer that gives a much better and less complex alternative to copyable_unique_ptr. I will prefer using his way.
Its probably simpler to stick to a normal unique_ptr and just implement copying of MyClass in your cpp file:
class MyClass {
private:
struct MyClassPrivate;
std::unique_ptr<MyClassPrivate> _impl;
public:
MyClass(int value);
MyClass(MyClass&&);
MyClass(const MyClass&);
~MyClass();
MyClass& operator = (const MyClass&);
MyClass& operator = (MyClass&&);
std::string get_value();
};
MyClass::MyClass(const MyClass& other)
: _impl(std::make_unique<MyClassPrivate>(*other._impl)) {
}
MyClass& MyClass::operator = (const MyClass& other) {
_impl = std::make_unique<MyClassPrivate>(*other._impl);
return *this;
}
MyClass::~MyClass() = default;
MyClass::MyClass(MyClass&&) = default;
MyClass& MyClass::operator = (MyClass&&) = default;
There is a slight issue with this code (and your original code) in that the moved from objects will have a null _impl you might want to add checks for null, alternatively you could replace with a default constructed object instead of a null pointer but that might be a waste in most cases?

Deep Copy Pointer and Delete the Memory

I'm trying to create Copy Constructor.
For example, There is a class like :
#include <string>
#include <vector>
using namespace std;
class Test
{
vector<string*> name;
public:
~Test();
Test(const string& name);
Test(const Test& t);
};
Test::Test(const Test& t)
{
for (auto it = t.name.begin(); it != t.name.end(); it++) {
this->name.push_back((*it));
}
}
Test::Test(const string& name)
{
this->name.emplace_back(name);
}
Test::~Test() {
for (auto it = name.begin(); it != name.end(); it++) {
delete (*it);
(*it) = nullptr;
}
}
int main() {
Test t("Hello World!");
Test t2(t);
}
When program is done, then error occured:
Access violation reading location 0xDDDDDDDD
I know that, it is because of t's name has deleted when t2 is called, however I don't know how to deep-copy of the name vector.
Thank you for your help.
In order to make a deep copy, you need to copy what any pointer points to.
Like this:
name.push_back(new string(**it));
You also need to implement the copy assignment operator.

inserting object in a map without copying the object

Is it possible to insert objects in a map, if the class of the object has disabled copy constructor and disabled copy operator? Is move semantics useful here?
#include <map>
class T {
public:
T(int v): x(v) {};
private:
T(const T &other); // disabled!
T &operator=(const T &other); // disabled!
int x;
};
int main() {
std::map<int, T> m;
m[42] = T(24); // compilation error here!
}
edit I was not completely clear. The object is huge, so I don't want to make unnecessary copies of it. But I can modify the code of the class (maybe I need to implement move semantics?) and not the user code (the main function in the example).
Use emplacement syntax:
m.emplace(std::piecewise_construct,
std::forward_as_tuple(42), std::forward_as_tuple(24));
// ^^ ^^
// int(42) T(24)
Or, in C++17, use try_emplace:
m.try_emplace(42, 24);
This might be what you are looking for:
class T {
public:
T(){};
T(int v): x(v) {};
T(const T &other) = delete;
T(T&& other) {x = other.x; std::cout << "move ctor\n";}
T &operator=(const T &other) = delete;
T& operator=(T&& other) {x = other.x; std::cout << "move assigment\n";}
private:
int x;
};
int main() {
std::map<int, T> m;
m.insert(std::make_pair(42, T(24)));
m[44] = T(25);
}
You might insert as pointer:
public:
T(int v) : x(v) {};
int getX(){ return this->x; }
private:
T(const T &other); // disabled!
T &operator=(const T &other); // disabled!
int x;
};
int main()
{
std::map<int, T*> m;
m[42] = new T(24); // no compilation error here!
std::cout << m[42]->getX() << std::endl; // prints out 24
return 0;
}

Move constructor c++

What's the right way to do a move constructor ?
class A{
...some stuff...
private:
int i;
std::string str;
};
A::A(A &&a)
{
*this = std::move(a);
};
or
A::A(A &&a)
{
this->str = std::move(a.str);
};
In the second case, is it useful to std::move() the int value ?
It should be
A::A(A&& other)
: i{other.i},
str{std::move(other.str)} {
// nop
}
This is the default implementation for a move constructor.

Ensure move in std vector with trivial type

Say I have a vector of ints
std::vector<int16_t> samples;
Is there a nice way to disable copying into this vector so that only moving is allowed? I know about std::move, but I'd like something like a compile error (like unique_ptr) if copying is attempted, rather than just relying on the programmer to "do the right thing"(tm)
Make an uncopyable wrapper:
#include <vector>
template<typename T>
class uncopyable
{
public:
uncopyable(const uncopyable&) = delete;
uncopyable(uncopyable&&) = default;
uncopyable(T&& data)
:
data_(std::move(data))
{
}
public:
uncopyable& operator=(const uncopyable&) = delete;
uncopyable& operator=(uncopyable&&) = default;
uncopyable& operator=(T&& data)
{
data_ = std::move(data);
return *this;
}
private:
T data_;
};
int main()
{
std::vector<int> big(10000);
uncopyable<std::vector<int>> uncopyable_big(std::move(big));
std::vector<int> other_big(10000);
uncopyable_big = std::move(other_big);
}
And use this type instead of your vectorif you want to guarantee no copies are made.
If it's a class member, then just make it private and only allow access in the ways you want:
std::vector<int16_t> const & get_samples() {return samples;}
void set_samples(std::vector<int16_t> && s) {samples = std::move(s);}
Otherwise, there's not much you can do to enforce particular access patterns.