Search code examples
c++copy-constructorshallow-copydefault-copy-constructor

Intercept C++ implicit copy constructor, or invoke its functionality


Given:

class Foo {

private:
    static int cntFoos;

    //... stuff...

public:
     Foo() { cntFoos++; }
     ~Foo() { cntFoos--; }
};

... where "stuff" may be any set of properties. (The idea is to have a counter of instances of that class)

Then:

Foo aFoo;
Foo twoFoo=aFoo;

Will invoke the automatic copy constructor, and thus I'd miss counting this one.

Is there a way to keep that counter reflecting the new instances created automatically? If I implement the explicit copy constructor, I will have to assign all the properties one by one. However, I want a shallow, memberwise copy. I don't need to perform a deep copy, so it seems like a lot of unnecessary work to implement an explicit copy constructor.


Solution

  • Since you want the default behavior for most members and only need special handling for one (static) member, why not encapsulate that special handling in its own class and make a member variable of that class? Like this:

    template<typename T>
    class InstanceCounter
    {
    public:
      static int Count;
    
      // Automatically invoked when a class containing it is created.
      InstanceCounter() { Count++; }
    
      // Automatically invoked when a class containing it is destroyed.
      ~InstanceCounter() { Count--; }
    
      // Automatically invoked when a class containing it is copy-constructed.
      InstanceCounter(const InstanceCounter& rhs) { Count++; }
    
      // No need to override operator=
    
      // Allow this counter to be used as an int.    
      operator int() const { return Count; }
    };
    
    template<typename T>
    int InstanceCounter<T>::Count;
    
    class Foo
    {
    public:
      InstanceCounter<Foo> count;
    };
    

    Implementation notes:

    • I made InstanceCounter a template so that different classes can easily have their own instance counts.
    • For C++11, you'll also want to provide a move constructor and a move assignment operator for InstanceCounter.

    Alternatively, and probably better, using the CRTP idiom:

    template<typename T>
    class InstanceCounted
    {
    public:
      static int InstanceCount;
    
      // Automatically invoked when a class containing it is created.
      InstanceCounted() { InstanceCount++; }
    
      // Automatically invoked when a class containing it is destroyed.
      ~InstanceCounted() { InstanceCount--; }
    
      // Automatically invoked when a class containing it is copy-constructed.
      InstanceCounted(const InstanceCounted& rhs) { InstanceCount++; }
    
      // No need to override operator=
    };
    
    template<typename T>
    int InstanceCounted<T>::InstanceCount;
    
    class Foo : public InstanceCounted<Foo>
    {
      // insert class contents here
    };
    // Now we can access Foo::InstanceCount.