Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 243
tru7 Avatar asked Dec 04 '14 20:12

tru7


2 Answers

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.
like image 68
Josh Kelley Avatar answered Nov 14 '22 21:11

Josh Kelley


Sorry, you'll need to overload and copy by hand.

If you're really, really, really against this, you could use a hack where you create an abstract parent class with the static counter and overridden copy constructor, and a child class with your actual data members and implicit shallow copy constructor.

You can also take the slightly less hacky approach of an encapsulated class. Store the values you want shallow copied in the encapsulated class and then when implementing the outer class explicit copy constructor, make a shallow copy of the inner class using its implicit copy constructor.

like image 34
David Pfeffer Avatar answered Nov 14 '22 23:11

David Pfeffer