Intercept C++ implicit copy constructor, or invoke its functionality
Asked Answered
D

2

5

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.

Dent answered 4/12, 2014 at 20:18 Comment(2)
Same as handling a pointer (but just counting, here). Look up rule of three (five C++11)Burnedout
Overload the copy constructor.Scant
F
5

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.
Folkmoot answered 4/12, 2014 at 20:36 Comment(4)
I would suggest making Foo a child class of InstanceCounter<Foo>, for use of the CRTP idiom.Appreciation
I will admit, this is probably a much better solution for the OP's actual scenario. My answer attempts to more directly answer the specific question posed, which is how to get an explicit copy constructor with automatically implemented shallow-copy behavior. But if anyone comes here looking for an instance counting solution, this is the way I would do it too.Redbreast
@DavidPfeffer Always a tough decision when there's both an answer that answers the explicit question, as well as one that provides a different but better solution.Appreciation
I agree with @Serge.. I would also suggest making Count a private variable, or removing the accessorCadi
R
5

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.

Redbreast answered 4/12, 2014 at 20:29 Comment(0)
F
5

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.
Folkmoot answered 4/12, 2014 at 20:36 Comment(4)
I would suggest making Foo a child class of InstanceCounter<Foo>, for use of the CRTP idiom.Appreciation
I will admit, this is probably a much better solution for the OP's actual scenario. My answer attempts to more directly answer the specific question posed, which is how to get an explicit copy constructor with automatically implemented shallow-copy behavior. But if anyone comes here looking for an instance counting solution, this is the way I would do it too.Redbreast
@DavidPfeffer Always a tough decision when there's both an answer that answers the explicit question, as well as one that provides a different but better solution.Appreciation
I agree with @Serge.. I would also suggest making Count a private variable, or removing the accessorCadi

© 2022 - 2024 — McMap. All rights reserved.