Vector of base and derived classes

I asked a similar question to this previously, so apologies in advance, but the example I gave was quite convoluted and my understanding of classes and inheritance was lacking. I'm a bit more comfortable with classes now, and would like to ask for help with a simpler example.

I want to create a vector of objects, mixing instances of a base class and any derived classes.

In the following example, I have a Dog class, with a string m_name variable.

From this Dog class I have a derived Labrador class, with a string m_colour variable.

In main, I create a vector of pointers of type Dog. I push_back a new instance of Dog, and push_back a new instance of Labrador. I can still only access the Dog's m_name variable in the Labrador object.

Previously a user suggested I use dynamic casting, but I just can't wrap my head around how this works, or what the code would be.

So, I know it's bad form, but I would really appreciate if somebody would show me the correct code for this. I've been stuck on this problem for a while and it's a little frustrating.


DOG.H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef DOG_H
#define DOG_H

class Dog
{
private:
	std::string m_name;

public:
	Dog(std::string name) : m_name(name) {}
	std::string getName() { return m_name; }
};



#endif DOG_H 


LABRADOR.H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef LABRADOR_H
#define LABRADOR_H

class Labrador : public Dog
{
private:
	std::string m_colour;

public:
	Labrador(std::string name, std::string colour)
		: Dog(name), m_colour(colour) {}

	std::string getColour() { return m_colour; }
};



#endif LABRADOR_H 


MAIN.CPP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "Dog.h"
#include "Labrador.h"

int main()
{
	std::vector<std::unique_ptr<Dog>> vec;

	vec.push_back(std::unique_ptr<Dog>(new Labrador("Holly", "brown")));
	vec.push_back(std::unique_ptr<Dog>(new Dog("Doggy")));

	std::cout << vec[0]->getColour(); // Does not complile, can only access Base member variable m_name

}
Last edited on

std::cout << vec[0]->getColour(); // Does not complile, can only access Base member variable m_name

That comment is exactly right. Think about it... not all Dogs have a color. Only Labradors do.


Downcasting is a solution, but it is a bad solution, and kind of defeats the entire point of making this class polymorphic.

The whole point of putting these in the same container is so you can treat them all the same way. If you can't treat all of your dogs like they are 'Dogs' (ie, you have to treat the Labradors differently than you treat your other Dogs) -- then they shouldn't be clumped together.

Ask yourself what the purpose of making this class hierarchy is if you can't treat all the classes the same.





That said.... to downcast, you can do this:

1
2
3
4
5
6
7
8
9
10
Labrador* p = dynamic_cast<Labrador*>(vec[0].get());

if(p)
{
    std::cout << p->getColour();
}
else
{
    // this dog was not a labrador, can't get its color
}



But again. Downcasting is usually a sign of a design flaw. Reconsider what you're doing here.
Interesting, thanks Disch. I'll try that code when I get home.

For some reason I was under the impression that it would be a very common thing to have a container of mixed base and derived classes. Is this not the case?

This may be a vague question, but would professional coders in general avoid this and keep vectors/containers to a single type?
For some reason I was under the impression that it would be a very common thing to have a container of mixed base and derived classes. Is this not the case?
It is. YOu just limited to base class interface. Usually you use virtual functions to get needed functionality:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Dog
{
private:
	std::string m_name;
public:
	Dog(std::string name) : m_name(name) {}
	std::string getName() { return m_name; }
        virtual std::string getColour() { return "All dogs are black"; }
};


class Labrador : public Dog
{
private:
	std::string m_colour;

public:
	Labrador(std::string name, std::string colour)
		: Dog(name), m_colour(colour) {}

	virtual std::string getColour() { return m_colour; }
};

int main()
{
	std::vector<std::unique_ptr<Dog>> vec;

	vec.push_back(std::unique_ptr<Dog>(new Labrador("Holly", "brown")));
	vec.push_back(std::unique_ptr<Dog>(new Dog("Doggy")));

	std::cout << vec[0]->getColour() << '\n';
	std::cout << vec[1]->getColour() << '\n';
}
brown
All dogs are black
Excellent, thanks MiiNiPaa. Using virtual functions was the missing link for me.
Topic archived. No new replies allowed.