Generally it appears preferable in C++ that code that functions that take a parameter to a polymorphic object takes a pointer to it:
class Base {};
class DerivedA : public Base {};
class DerivedB : public Base {};
void OperateOnObject(Base* obj) {};
vector<Base*> objects;
objects.push_back(new DerivedA());
objects.push_back(new DerivedB());
for (size_t i=0; i<objects.size(); i++) {
OperateOnObject(objects[i]);
}
Why is it that OperateOnObject()
is usually written to take a pointer to Base
rather than a reference? Are there potential problems with slicing? (eg the vtable gets lost)
I've seen this response to a similar-sounding question, but it doesn't seem to address the same issue.
There's certainly no problem with slicing --- your classes don't need vtables since they have no virtual functions, but even if they did have them, pass-by-reference doesn't copy the object and hence doesn't slice anything.
You can make virtual calls via references exactly as you can via pointers. So as far as I know there's no practical reason, it's for style only.
Perhaps it's to do with the fact that polymorphic objects are generally created by pointer with new
, and of course must be stored in containers by pointer (or smart pointer) since you can't have a container of references. It might then seem more consistent to manipulate them by pointer always.
Also, if you want to use OperateOnObject
in a standard algorithm like for_each
, then either it has to accept the element type of the container, which is a pointer, or else you have to wrap it in a function adaptor that does the dereference. Standard C++ doesn't have that adaptor, so it's a world of hassle to base your style on it.
Related: look what happens if OperateOnObject
takes a reference, and you iterate your vector using iterators:
for (vector<Base*>::iterator i = objects.begin(); i != objects.end(); ++i) {
OperateOnObject(**i);
}
The 20th time that double-indirection annoys or confuses you, you will change the signature of OperateOnObject
;-)
As an aside, some style guides warn against passing non-const references regardless of whether the type is a polymorphic base class, on the grounds you can't immediately tell the difference between pass-by-reference and pass-by-value when you're reading code at the call site. So they'd prefer OperateOnObject
to take a pointer anyway.
Personally I think that argument is a little weak -- a function's name should tell you roughly what it does, and in particular a statement like ChangeTheObject(my_object);
is not-so-subtly hinting to me that since it's not using any return value, it must change its argument. But I accept that if you follow a style properly, there's some benefit to clearly and consistently distinguishing mutators from pure functions.
References do not present problems with slicing - in this respect, they are as good as pointers. There is no "hard" reason to prefer pointers, unless you want semantics of NULL
to be available to your code (i.e. an ability to pass "nothing", which is not there if you use references).
I think the pointer types are often used as parameters of polymorphic functions to match the semantic of collections: since you cannot make a vector
of references, your code will end up having to dereference pointers obtained from collections before passing them to functions. This may be annoying.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With