snippetcppMinor
How to design interface of deep-copy behaving pointer containers?
Viewed 0 times
designbehavinginterfacepointerdeephowcontainerscopy
Problem
I want to make a container which manages big objects. which performs deep copies on copy construction and copy assignment. I also like the interface of the std containers, so I wanted to implement it using public inheritance:
I heard of the "Thou shalt not inherit from std containers" maxim and the reasons behind it. So I decided to make an alternative:
This looks more expressive, but using the code feels strange and verbose:
instead of:
I am a beginner programmer and I don't want to make errors early on. I would like to ask your advice on which choice to make. Or even better, if there is a third option.
template
class Container : public std::vector >
{
public:
Container(int nToAllocate){ /* fill with default constructed TBigObjects */}
Container(const Container& other){ /* deep copy */ }
Container(Container&&) = default;
Container& operator = (const Container& population){ /* deep copy */ }
Container& operator = (Container&&) = default;
};I heard of the "Thou shalt not inherit from std containers" maxim and the reasons behind it. So I decided to make an alternative:
template
using Container = std::vector >;
template
Container defaultAllocate(int nItems);
template
Container deepCopy(const Container& other);This looks more expressive, but using the code feels strange and verbose:
class Big;
Container someContainer = defaultAllocate(12);
Container copy = deepCopy(someContainer);instead of:
class Big;
Container someContainer(12);
Container copy = someContainer;I am a beginner programmer and I don't want to make errors early on. I would like to ask your advice on which choice to make. Or even better, if there is a third option.
Solution
I think you are going about it the wrong way.
Containers already perform copy on copy construction/assignment because they are designed for value objects not pointers. What you really want is a wrapper object for pointers that performs a deep copy when that object is copied.
This has the added advantage of working with all containers.
If you define the move operators then it also works well with re-size operations as the move semantics will be applied to the wrapper when the the container is re-sized.
The other advantage here is you can make the wrapper behave like the underlying type. So you get easy access to all the algorithms.
Containers already perform copy on copy construction/assignment because they are designed for value objects not pointers. What you really want is a wrapper object for pointers that performs a deep copy when that object is copied.
This has the added advantage of working with all containers.
If you define the move operators then it also works well with re-size operations as the move semantics will be applied to the wrapper when the the container is re-sized.
The other advantage here is you can make the wrapper behave like the underlying type. So you get easy access to all the algorithms.
template
class Deep
{
T* value;
public:
Deep(): value(NULL) {}
// Take ownership in this case.
Deep(T const* value): value(value) {}
Deep(T const& value): value(new T(value)) {}
~Deep() {delete value;}
Deep(Deep&& move) {std::swap(value, move.value);}
Deep(Deep const& copy) {T* tmp = new T(*(copy.value));swap(tmp,value);delete tmp;}
// Note this does do a deep copy via copy and swap
Deep& operator=(Deep copy) {std::swap(value, copy.value);return *this;}
Deep& operator=(Deep&& move) {std::swap(value, move.value);return *this;}
// Note: undefined behavior if used when value is NULL.
// You may want to add logic here depending on use case.
operator T&() {return *value;}
operator T const&() const {return *value;}
T* release() {T* tmp = value;value = NULL;return tmp;}
};
int main()
{
std::vector> deep(5,7);
}Code Snippets
template<typename T>
class Deep
{
T* value;
public:
Deep(): value(NULL) {}
// Take ownership in this case.
Deep(T const* value): value(value) {}
Deep(T const& value): value(new T(value)) {}
~Deep() {delete value;}
Deep(Deep&& move) {std::swap(value, move.value);}
Deep(Deep const& copy) {T* tmp = new T(*(copy.value));swap(tmp,value);delete tmp;}
// Note this does do a deep copy via copy and swap
Deep& operator=(Deep copy) {std::swap(value, copy.value);return *this;}
Deep& operator=(Deep&& move) {std::swap(value, move.value);return *this;}
// Note: undefined behavior if used when value is NULL.
// You may want to add logic here depending on use case.
operator T&() {return *value;}
operator T const&() const {return *value;}
T* release() {T* tmp = value;value = NULL;return tmp;}
};
int main()
{
std::vector<Deep<int>> deep(5,7);
}Context
StackExchange Code Review Q#26111, answer score: 2
Revisions (0)
No revisions yet.