Why I am a C++ Pope (Pointer Person) (Incomplete)

I am relatively new to the world of practical C++, having experience in it but not as much as I consider worth of notice, so I might say some inaccuracies in this post. If you are an expert C++ programmer, your opinion is extremely welcome. That said, what does it mean I am a “Pope”, or pointer person, in C++? My colleagues gave me this very appropriate surname, due to the fact that I prefer my C++ like my C: with pointers.

In C++ you have two apparently similar concepts to refer to an instance: pointers and references. Pointers point to an object, references refer (or alias) to an object. Let’s define some easy classes to begin with, so we have something concrete to talk about:

#include <iostream>

class Animal {
public:
    Animal() {}
    virtual ~Animal() {}

    virtual void vocalize() = 0;
};

class Dog : public Animal {
public:
    Dog() {}
    virtual ~Dog() {}

    virtual void vocalize() { std::cout << "woff" << std::endl; }
};

class Cat : public Animal {
public:
    Cat() {}
    virtual ~Cat() {}

    virtual void vocalize() { std::cout << "meow" << std::endl; }
};

Pretty standard I guess. Now let’s see a way of using these classes for something productive

int main() {
    Dog d;
    Cat c;

    d.vocalize();
    c.vocalize();
}

Again, rather typical. Now it’s time to bring in references. A reference is an alias to an object. It can be used as the original object by the very fact that it is the original object. References however have the following constraints

- They cannot be dangling, that is, you can't define a reference and set to
  point to something later on. Definition and assignment must be done at
  the same time
- They cannot be changed. Once you bind a reference to an instance, that
  reference cannot be set to refer to another instance of the same type.

These two requirements imply that this is valid code

int main() {
    Dog d;
    Dog &amp;d_ref = d;

    d_ref.vocalize();
}

but this is not

int main() {
    Dog d1;
    Dog d2;

    Dog &amp;d_ref; // Error, can't define a reference not pointing to anything
    Dog &amp;d2_ref = d2;
    d2_ref = d1 // valid, but this does not rebind d2_ref to refer to d1. It copies d1 on d2 (potentially invoking operator=()). 

}

What about inheritance? Sure enough, this code is absolutely valid, and works

int main() {
    Dog d;
    Animal &amp;animal = d;

    animal.vocalize();
}

but as you can’t rebind a reference, you can’t rebind animal to a cat object later on, and call animal.vocalize() to get a meow. Additionally, since Animal is an abstract class, you can’t make a list of it, and then iterate over the list. This code works

int main() {
    std::vector<dog> dogs;
    dogs.push_back(Dog());
    dogs.push_back(Dog());

    int i;
    for (i=0; i < dogs.size(); ++i) {
        dogs[i].vocalize();
    }
}

but this doesn’t. It would not even work with Animal being non pure virtual, because in that case you would risk slicing your object.

int main() {
    std::vector<Animal> animals;
    animals.push_back(Dog());
    animals.push_back(Cat());

    int i;
    for (i=0; i < animals.size(); ++i) {
        animals[i].vocalize();
    }
}

and of course, you can’t make a list of references, meaning that std::vector<animal &> animals is not an option. A pointer solves easily the whole conundrum, but it gives you an additional burden: handling the memory allocation and deallocation (especially deallocation, unless you use some form of smart pointer, of course)

int main() {
    std::vector<Animal *> animals;
    animals.push_back(new Dog());
    animals.push_back(new Cat());

    int i;
    for (i=0; i < animals.size(); ++i) {
        animals[i]->vocalize();
    }
    // XXX: Leaking dogs and cats!    
}

This is a trivial demonstration of why I tend to prefer pointers. In the upcoming posts, I will show more examples, but please be advised: I am not advocating pointers as superior to references. Both have their objective, and you can solve some of the situations I will show with either pointers and references. What changes is the style of the solution. When you use references, it normally involves rather aggressive templating, as we will see in later posts, and one drawback of templates is that they might “bubble up” in your dependency network: class A needs class B that is templated and used with class C as B? then you might also have to template A and use it as A. When you use pointers, you might end up having to wrangle memory allocation (something that carries a discipline burden that might not be acceptable), using dynamic_cast(), and having to transfer ownership.

Unfinished from here.

You end up having to define the routine in the header. Soon you will have most of your code in headers.

you can’t rely on class forward declaration for return type (you will have to include the full class in the header)

You don’t hide complexity. It bubbles up.

suppose that you have to template on T, and the function needs an object that depends on T. bam, now you have to template on both T and V.

template template <template class V></template>

two classes related. one deep down routine binds them, bubbles up.

cannot make a virtual templated function.

http://www.artima.com/cppsource/type_erasure.html