Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it ever a good idea to put virtual methods on a copyable type?

Have seen some related questions, but not this exact one...

I've treated classes as fitting into a few major categories, let's say these four for simplicity:

  • Value Classes which have some data and a bunch of operations. They can be copied and meaningfully compared for equality (with copies expected to be equal via ==). These pretty much always lack virtual methods.

  • Unique Classes whose instances have identity that you disable assignment and copying on. There's usually not an operator== on these because you compare them as pointers, not as objects. These quite often have a lot of virtual methods, as there isn't risk of object-slicing since you're being forced to pass them by pointer or reference.

  • Unique-but-Clonable Classes which disable copying, but are pre-designed to support cloning if that's what you really want. These have virtual methods, most importantly those following the virtual construction / cloning idiom

  • Container Classes which inherit the properties of whatever they're holding. These tend not to have virtual methods...see for instance "Why don't STL containers have virtual destructors?".

Regardless of holding this informal belief system, a couple times I've tried adding a virtual method to something copyable. While I may have thought it would "be really cool if that worked", inevitably it breaks.

This led me to wonder if anyone has an actual good example of a type which has virtual methods and doesn't disable copying?

like image 729
HostileFork says dont trust SE Avatar asked Nov 04 '13 17:11

HostileFork says dont trust SE


2 Answers

The only counter-example that I have are classes that are meant to be stack-allocated and not heap-allocated. One scheme I use it for is Dependency Injection:

class LoggerInterface { public: virtual void log() = 0; };

class FileLogger final: public LoggerInterface { ... };

int main() {
    FileLogger logger("log.txt");

    callMethod(logger, ...);
}

The key point here is the final keyword though, it means that copying a FileLogger cannot lead to object-slicing.

However, it might just be that being final turned FileLogger into a Value class.

Note: I know, copying a logger seems weird...

like image 91
Matthieu M. Avatar answered Sep 22 '22 16:09

Matthieu M.


There's nothing inherently wrong in being able to copy a polymorphic class. The problem is being able to copy a non-leaf class. Object slicing will get you.

A good rule of thumb to follow is never derive from a concrete class. This way, non-leaf classes are automatically non-instantiable and thus non-copyable. It won't hurt to disable assignment in them though, just to be on the safe side.

Of course nothing is wrong with copying an object via a virtual function. This kind of copying is safe.

Polymorphic classes are normally not "value-classes" but it does happen. std::stringstream comes to mind. It'not copyable, but it is movable (in C++11) and moving is no different from copying with regard to slicing.

like image 23
n. 1.8e9-where's-my-share m. Avatar answered Sep 21 '22 16:09

n. 1.8e9-where's-my-share m.