0

I am working on a simple C++ (under Linux) project, which will have pointers to objects. I have class A, class B which extends A, and class C which extends B. Class C has a method (test) that does not exist in A or B.

Is is possible to have a single pointer 'p' that can point to an object of type A, B, and C ? How would I define that pointer?

Secondly, since a.test() doesn't exist, but c.test() does, can my generic pointer 'p' call p->test() ? Will this compile? What if at run time p points to an object of class A and I call p->test()? Is that a runtime error?

TSG
  • 4,242
  • 9
  • 61
  • 121
  • 4
    Look up polymorphism – NathanOliver Sep 20 '15 at 14:26
  • I've looked up polymorphism and most sites talk about overloading. When it comes to inheritence and pointers the info is not as clear. This is close: http://stackoverflow.com/questions/15188894/why-doesnt-polymorphism-work-without-pointers-references but still unsure. I've mixed something up that's making this harder to grasp than it should be. – TSG Sep 20 '15 at 14:31
  • Have you seen [this](https://isocpp.org/wiki/faq/virtual-functions)? – NathanOliver Sep 20 '15 at 14:38
  • 1
    You may have looked up polymorphism, but you clearly haven't understood the implications well enough. Given your description, a pointer to `A` (i.e. of type `A *`) can point at a `B` or a `C`. If `A` is a polymorphic base then, in C++, it provides a set of virtual functions that can be specialised by `B` or `C`. If you design such a class hierarchy (in particular the base class `A`) well enough, no casting should be needed. In fact, a need to cast usually indicates a design error. – Peter Sep 20 '15 at 14:39

1 Answers1

4

Is is possible to have a single pointer that can point to an object of type A, B, and C ?

I assume you mean "that can either point to an A object or to B object or to a C object", right?

Yes, you can have such a pointer.

How would I define that pointer?

A*

A base-class pointer can point to objects of derived classes.

Secondly, how would I call methods of the object if the pointer can point to A/B/C classes?

You define a virtual function in A and override it in B and C. Then, when you call the method through your A*, the language will perform dynamic dispatch, i.e. it will automatically call the right method depending on whether your A* points to an A, to a B or to a C.

Do I need to cast them before calling the methods?

No. That would pretty much defeat the purpose of virtual functions.

Here is a complete example:

#include <iostream>

class A
{
public:
    virtual ~A() {} // not really needed in this program,
                    // but almost always needed in real code
                    // when a class has a virtual function
    virtual void method() { std::cout << "A::method\n"; }
};

class B : public A
{
public:
    virtual void method() override { std::cout << "B::method\n"; }
};

class C : public A
{
public:
    virtual void method() override { std::cout << "C::method\n"; }
};

int main()
{
    A a;
    B b;
    C c;
    A* ptr = &a;
    ptr->method();
    ptr = &b;
    ptr->method();
    ptr = &c;
    ptr->method();
}

Output:

A::method
B::method
C::method
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • 1
    A virtual destructor is needed to avoid undefined behaviour if a dynamically allocated instance (created with operator `new`) of a derived class is destroyed (operator `delete`) via a pointer to the base. This does not require existence of other virtual functions (although, in practice, such classes will usually have virtual functions). – Peter Sep 20 '15 at 14:46
  • @Peter: Exactly. I don't think I've ever written or maintained a base class in which *only* the destructor was virtual. I cannot even think of a good use case for that. – Christian Hackl Sep 20 '15 at 14:50
  • Thanks! Getting clearer. Do I *HAVE* to define the virtual functions? If my base class is ANIMAL and derived classes are DOG and CAT, does the base class really need a bark() and meow() method? That makes my code less neat and clean. Any way around that? – TSG Sep 20 '15 at 15:38
  • 1
    @Telium: Barking and meowing are not abstract, general things that belong to *any* animal. So you would **not** put them into an `Animal` base class, no. A base class might instead have a method like `makeNoise`, and you would implement it *in terms of* barking or meowing in derived classes. Which will lead you to the discovery of *pure* virtual functions, because an abstract animal makes no defined noise. You would thus declare the base method like this: `virtual void makeNoise() = 0;`. But you should really read more teaching material on this. – Christian Hackl Sep 20 '15 at 15:45
  • Ok almost there! I've read that I can add methods to the derived class (that don't exist in the base class). And that's where I'm getting stuck! So if class BIRD derives from ANIMAL, then BIRD can have a fly method (which will not exist in the base class). But now if I have a pointer to the base class, what happens if I call the fly method? Will this compile? What if the instance is a DOG and I call fly? **THIS IS THE HEART OF WHERE I'M CONFUSED** – TSG Sep 20 '15 at 17:22
  • @Telium: It won't compile. – Christian Hackl Sep 20 '15 at 17:30
  • I think I'm ready to answer my own question (hope this is right). A pointer to the base class CANNOT call the fly() method. So if I find the type is BIRD then I must cast the point to BIRD and then call fly() ?? – TSG Sep 20 '15 at 17:33
  • @Telium: You can use `dynamic_cast` and `typeid` to find out if your `Animal*` actually points to a `Bird`, treat it as a `Bird*` and then call `fly()` on it. But this should be considered an **advanced** technique for a beginner, and it's more of a **workaround for existing code**, not (typically) something you design from the beginning when you try to create a clean architecture. – Christian Hackl Sep 20 '15 at 17:41
  • I think we've hit the answer with your comment above. I'm now sure why this approach isn't a 'clean architecture'...from my (beginners) perspective, that's one thing that makes C++ so powerful, that I can add methods in derived classes and then call them using a base class pointer. There is a risk that as I get better at c++ I will realize this conclusion is wrong...but it's probably outside the original question. Thanks – TSG Sep 20 '15 at 23:11