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 &d_ref = d;
d_ref.vocalize();
}
but this is not
int main() {
Dog d1;
Dog d2;
Dog &d_ref; // Error, can't define a reference not pointing to anything
Dog &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 &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
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
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