Search code examples
c++copy-constructordeep-copy

Is it ok to use C++ copy constructor for other purpose?


By other purpose, I mean Clone. Here is a simplified example: I have objects, some are squares, some are boxes, which are both Object2D. Each one has a unique id.

In this system, I don't need a typical meaning of copy of an object. A typical copy constructor is used when object is passed by value, returned by value, and ... Here in this system every object is unique, so we don't need the typical copy constructor. Two squares will differ for at least the id. Therefore, I am using the copy constructor to make clones. During a clone, I will deep copy the tree structure except the id.

#include <iostream>
#include <string>
#include <memory>
#include <cstdlib>
#include <vector>
int offset(){
    return rand()%100-50;
}
int location(){
    return rand()%10000-5000;
}
class Object2D{
public:
    int x_, y_;
    int id;
    static int count;
    Object2D(int x, int y):x_(x), y_(y){
        id=count++;
    }
};
int Object2D::count=0;
class Square: public Object2D{
public:
    int getParent(){return containedBy_->id;}
    Square(Object2D* containedBy, int size):
            Object2D(containedBy->x_+offset(), containedBy->y_+offset()),
            containedBy_(containedBy),size_(size){
        std::cout<<"Square constructor"<<std::endl;
    }
    Square(const Square& other):
            Object2D(other.x_-other.containedBy_->x_, other.y_-other.containedBy_->y_), 
            containedBy_(other.containedBy_), size_(other.size_){
        std::cout<<"Square clone"<<std::endl;
    }
private:    
    Object2D* containedBy_;
    size_t size_;
};
class Box:public Object2D{
private:
    size_t l_;size_t h_;
    std::vector<std::shared_ptr<Square>> all;
public:
    void addSquare(int size){
        auto temp = std::make_shared<Square>(this, size);
        all.push_back(std::move(temp));
    }
    const std::vector<std::shared_ptr<Square>>& getAll() const{
        return all;
    }
    Box(int x, int y, size_t l, size_t h):
            Object2D(x,y), l_(l), h_(h){}
    Box(const Box& other):Object2D(location(), location()){// clone the other box, put cloned squares in
        for(const auto& item:other.getAll()){
            all.push_back(std::make_shared<Square>(*item));
        }
    }
    void showOffsets(){
        std::cout<<"show offsets of all components for container"<<id<<":";
        for(const auto& item: all){
            std::cout<<item->id<<"("<<item->x_-x_<<","<<item->y_-y_<<")"<<" ";
        }
        std::cout<<std::endl;
    }
};

int main()
{
    Box b(100,100,100,100);
    std::cout<<"before do it"<<std::endl;

    b.addSquare(10);
    Box c(b);
    std::cout<<b.id<<c.id<<std::endl;

    b.showOffsets();
    c.showOffsets();
    return 0;
}

Here I clone a box, I need to also clone the contained squares, and keep their offsets. The cloned objects will have new ids, which are all unique.

Question

Is it a good idea to use copy constructor for clone? Note that, in this case, the copy constructor will behave differently from a typical copy constructor, so I need to forbid any way to make a call to the typically expected copy constructor, such as I can't use vector to contain squares, since a simple vector erase will call copy constructor which will not preserve the id. So I used smart pointers.

Second, how come the offsets are not copied during clone? That is the thing I would like to preserve.

Results:

before do it
Square constructor
Square clone
02
show offsets of all components for container0:1(36,33) 
show offsets of all components for container2:3(-1879,2256) 

Solution

  • yes, you can define your own copy constructor.

    however, as you already noted, this way you'll find it hard to "forbid" copies where you do not want them explicitly.

    my suggestion: give your object a clone() function and delete the copy constructor (maybe also the copy operator).

    this way you only clone when you explicitly intend to.