-2

I am writing a library in C++, and was wondering about the use of references and/or pointers in place of the interfaces (i.e., use of (abstract) base classes as a placeholder for derived classes).

The question is, which one of the two should I go for? Should I prefer one to another, at all? Is there any difference to using references to (abstract) base classes instead of pointers?

Please have a look at the below code excerpt, and comment on any issues:

#include <iostream>

class Base {
    protected:
    public:
        virtual void Print() const {
            std::cout << "Base" << std::endl;
        }
};

class Derived : public Base {
    protected:
    public:
        void Print() const override {
            std::cout << "Derived" << std::endl;
        }
};

class AnotherDerived : public Base {
    protected:
    public:
        void Print() const override {
            std::cout << "Another Derived" << std::endl;
        }
};

void someFunc( const Base& obj ) {
    obj.Print();
}

void anotherFunc( const Base* obj ) {
    obj->Print();
}

int main( int argc, char* argv[] ) {
    Base baseObj, *basePtr;
    Derived derivedObj;
    AnotherDerived anotherDerivedObj;

    someFunc( derivedObj );
    anotherFunc( &derivedObj );
    someFunc( anotherDerivedObj );

    /* slicing ??? */
    baseObj = derivedObj;
    /* another slicing ??? */
    baseObj = anotherDerivedObj;

    /* proper use */
    basePtr = &anotherDerivedObj;

    someFunc( baseObj );
    anotherFunc( basePtr );

    return 0;
}

I guess, in the above code, I am doing an object-slicing when copy-assigning a child object to a parent. However, assuming I am not doing any object-slicing (as in the first two calls to someFunc), will the reference approach do what I am intending to do? Do both the reference and the pointer approaches internally use dynamic_casting when deciding on which polymorphic function to call? Or, am I totally missing the point here?

Thanks in advance for your time!

Arda Aytekin
  • 1,231
  • 14
  • 24
  • Totally depends on your actual requirements and use cases. – πάντα ῥεῖ Aug 31 '15 at 09:46
  • Calls to virtual functions through both pointers and references behave polymorphically. – Alan Stokes Aug 31 '15 at 09:49
  • @πάνταῥεῖ What do you mean by that? Assume I have different datasets on which I need to run some sort of optimization problem. There are dense datasets and sparse datasets; yet, after all, they are all datasets. Is there any difference to providing `const Dataset& input` to the optimization algorithm instead of the `const Dataset* input` style? @AlanStokes So I can use either without any problems, differences? – Arda Aytekin Aug 31 '15 at 09:49
  • @ArdaAytekin There's no single _best recipe_ of using references or pointers. Nothing to do with optimization ( they finally have the same cost), it's about semantics. – πάντα ῥεῖ Aug 31 '15 at 09:53
  • @πάνταῥεῖ I'd appreciate if you told me in simple words the difference, or else, the reason why you have said so. I mean, in the pointer example, I can check against `nullptr` to verify the validity of the object I am working with. In the reference example, I do not need to check it, as the program would have thrown an exception otherwise, right? Are there any other (big) differences I should be careful with? – Arda Aytekin Aug 31 '15 at 09:57
  • @πάνταῥεῖ References should be slightly easier to optimize, but I agree. – Šimon Tóth Aug 31 '15 at 10:00
  • @ArdaAytekin You can't (well, you can, but not normally) have an empty reference, so there is no need to check. – Šimon Tóth Aug 31 '15 at 10:00
  • @πάνταῥεῖ I am sorry for the confusion, but I was referring to a mathematical optimization algorithm I am trying to implement on a given dataset. So, the word optimization was not referring to the compiler optimization --- sorry! :( – Arda Aytekin Aug 31 '15 at 10:02
  • @ArdaAytekin The common caveat applies. Do not ignore compiler warnings and do not use casts unless you know EXACTLY what you are doing (i.e. if you use a cast to avoid a warning, your code is wrong). – Šimon Tóth Aug 31 '15 at 10:02
  • @Let_Me_Be Thanks for your comments and suggestions! – Arda Aytekin Aug 31 '15 at 10:06

1 Answers1

1

My rule of thumb for functions and method parameters is to use a constant reference (const &) for input only parameters that are mandatory. Use a const * for input parameters that also can also be NULL and to prefere pointers over references for out or inout parameters. That way the caller has to use & for the parameters that could potentially be modified by the function/method and it is more explicit. This applies when passing instances of classes and structs. For simple types pass by value is prefered.

trenki
  • 7,133
  • 7
  • 49
  • 61
  • Passing parameters by reference or pointers hinders the optimizer because now it has to consider aliasing and memory model requirements. Prefer passing by value if possible. See https://www.youtube.com/watch?v=eR34r7HOU14 for more details. – Maxim Egorushkin Aug 31 '15 at 12:51
  • Sure, I only pass instances of classes and structs by reference, not simple types. – trenki Aug 31 '15 at 12:53