Saturday, May 1, 2010

Shallow-Copy Semantics

I have read in too many places this sentence

"The default copy constructor and assignment operator make shallow copies"

This is not completely right. I would say, the generated copy constructor and assignment operator make default copies of the direct class members, and calls to the copy constructor or assignment operator of the base classes. If the member or base classes define the default copy constructor and assignment operator with deep copy semantics, the generated defaults will have a mix of both copy semantics.

This means that we can not let the compiler generate a shallow copy for us if the class has members or inherits in a general case.

Applications needing shallow copy semantics must follow a strict protocol. Here I present an explicit approach. Classes providing shallow copy semantics must define at least a default shallow copy constructor and a shallow assignment function.

The default copy constructor and assignment for the C++ fundamental and pointers types has shallow copy semantics. For the other compound types we need to make the difference between shallow copy and deep copy. We can use a tag for this purpose.

struct shallow_t {};
const shallow_t shallow = {};
C(shallow_t, C const&);
C& shallow_assign(C const&);

For base classes we need to take care just of members.

struct B {
  C c;
  B(shallow_t, B const& rhs) 
    : c(shallow, rhs.c) {}
  B& shallow_assign(B const& rhs){
    if (this!=&rhs) {
      c.shallow_assign(rhs.c);
    }
    return *this;
  }
};

For derived classes we do like:
struct D : B {
  D(shallow_t, D const& d) : B(shallow, d)
  // shallow copy construct the fields
  {
  }
  D& shallow_assign(D const& rhs){
    if (this!=&rhs) {
      this->B::shallow_assign(rhs);
      // shallow copy the fields
    }
    return *this;
  }
};

Now we are able to shallow copy objects as far as we follows the protocol.

D d1; 
D D2(shallow, D2);
D d3;
d3.shallow_assign(d1);

The main problem while trying to integrate shallow copy semantic in C++ is that we can not have function parameters or return types shallow copied as C++ call the copy constructor instead. For function parameters we should pass them by reference and call the shallow copy inside the function when needed.


No comments:

Post a Comment