2

Let's say there is a base class called Dog with the following attributes and methods.

#pragma once
#include <string>

class CDog
{
protected:
string name;
string breed;
int age;

public:
CDog();
virtual ~CDog();
CDog(string name, string breed, int age);
string getName();
string getBreed();
int getAge();
};

And two derived classes called Puppy and Old;

#pragma once
#include "CDog.h"
class CPuppy: public CDog
{
private:
bool playful;

public:
CPuppy();
~CPuppy();
CPuppy(string name, string breed, int age, bool playful);
bool getPlayful;
};

 #pragma once
#include "CDog.h"
class COld: public CDog
{
private:
bool disease;

public:
COld();
~COld();
COld(string name, string breed, int age, bool disease);
bool getDisease;
};

Then, I create a dynamic vector of objects based on the Dog class, so that the user can register as many dogs as he wants. Later on, the user wants to know the average age of all the puppies and all the old dogs. And my question raises here, ¿ How can I get access to the attributes of the Puppey and Old class in the vector in order to generate those reports? I think that there must be an if statement, but I don't know the condition. I hope guys you can help me. I solved this problem using two different dynamic vectors, one for the Puppey class and other for the Old class, but I dont' know if it's possible only with one dynamic vector.

Drako
  • 73
  • 8
  • 6
    If you ever feel the need to access a derived class from a base class pointer/reference, odds are *very good* that you have done something terribly wrong in your class hierarchy. Maybe you're not providing an appropriate `virtual` interface, or you shouldn't be using a class hierarchy to model this concept at all. Why do you feel that you *need* to have `CPuppy` derive from `CDog`? Why are you modelling the stages of a dog's lifecycle with inheritance? – Nicol Bolas Feb 02 '16 at 03:03
  • It's just an example. I dont' hate dogs or anything. – Drako Feb 02 '16 at 03:11
  • My point is how i can calculate the average of all puppies and all the old dogs . I'm pretty sure I should use a for loop, but if I do that, I would be mixing all the ages. – Drako Feb 02 '16 at 03:14
  • You have two choices: 1)) keep two lists and loop through them separately, or 2) use `dynamic_cast` to check whether any given `CDog` is really a ` CPuppy` or `COld`. – Remy Lebeau Feb 02 '16 at 03:16
  • 2
    @Drako: And *my* point is that this wouldn't be a question if you had a well-designed class. Your class hierarchy's design is *actively inhibiting* the particular things you want to do with it. If you need to do X, and you design a system where X is hard, then you have a bad design for doing X. – Nicol Bolas Feb 02 '16 at 03:22
  • 1
    "I've been solving some problems related to POO" - maybe a plastic bag? – M.M Feb 02 '16 at 03:24

2 Answers2

1

Generally this is possible through dynamic cast. One would make a vector of pointers to Base class and then try to downcast Base to a Derved object, and if succeeded work like it's a Derived (which it is). Like in the examples, or here: https://stackoverflow.com/a/6471659/1133179.

However, in c++ this is almost always a bad design and a rarely used pattern. It's probably useful only to implement dynamic languages. c++ naturally is a statically typed language and designs avoid such issues.

For your case, you should have common features in a base class, like age in Dog, then for a container of Dogs you can compute average age. But if you need to perform actions specific only on one type of a SpecialDog you should put it into another container, so you don't mix two types, and don't have to do any filtering and type checking.

Probably best design for you is to have two containers of shared_ptr. One for CDog (all dogs) and one for COld for old dogs. If you create a COld you can put it into both containers, if you create a CPuppy or CDog you can put it only into let's say vector< shared_ptr<CDog> >. The idea is to have "views" of objects of the same class. And since one object (a dog) can belong to two classes, you need to express the information that the object is actually shared between the views, thus shared_ptr.

Well it might not be the easiest for starters, but it's probably most proper way to do it.

Community
  • 1
  • 1
luk32
  • 15,812
  • 38
  • 62
  • Yea, well I think referred sources and examples cover this. I didn't want to get into all technicalities. I also avoided code examples and just wanted to focus and express design ideas. I included dynamic_cast mention, because it's the answer to the question, though the idea is not good in the 1st place. That's what I wanted it to be about. – luk32 Feb 02 '16 at 03:28
0

To do what you are asking for, you need to either:

  1. keep two separate lists, one for CPuppy objects and one for COld objects, and then loop through them separately:

    template<typename T>
    int getAverageAge(const std::vector<T*> &dogs)
    {
        if (dogs.empty()) return 0;
        int sum_ages = 0;
        for(std::vector<T*>::iterator iter = dogs.begin(), end = dogs.end(); iter != end; ++iter)
        {
            sum_ages += (*iter)->getAge();
        }
        return sum_ages / dogs.size();
    }
    

    std::vector<CPuppy*> puppies;
    std::vector<COld*> olds;
    ...
    int avg_puppy_age = getAveragAge(puppies);
    int avg_old_age = getAveragAge(olds);
    
  2. use dynamic_cast to check whether any given CDog is really a CPuppy or COld object.

    std::vector<CDog*> dogs;
    ...
    
    int avg_puppy_age = 0;
    int num_puppies = 0;
    int avg_old_age = 0;
    int num_olds = 0;
    
    for(std::vector<CDog*>::iterator iter = dogs.begin(), end = dogs.end(); iter != end; ++iter)
    {
        if (dynamic_cast<CPuppy*>(*iter) != NULL)
        {
            avg_puppy_age += (*iter)->getAge();
            ++num_puppies;
        }
        else if (dynamic_cast<COld*>(*iter) != NULL)
        {
            avg_old_age += (*iter)->getAge();
            ++num_olds;
        }
    }
    
    if (num_puppies)
        avg_puppy_age /= num_puppies;
    if (num_olds)
       avg_old_age /= num_olds;
    

Then , you can (and should) take the two techniques above and combine them together:

std::vector<CDog*> dogs;
std::vector<CPuppy*> puppies;
std::vector<COld*> olds;

...

for(std::vector<CDog*>::iterator iter = dogs.begin(), end = dogs.end(); iter != end; ++iter)
{
    if (CPuppy *puppy = dynamic_cast<CPuppy*>(*iter))
        puppies.push_back(puppy);

    else if (COld *old = dynamic_cast<COld*>(*iter))
        olds.push_back(old);
}

// alternatively, any time you add a new Puppy or Old to the
// main dogs list, add it to the appropriate sub-list as well...

int avg_puppy_age = getAveragAge(puppies);
int avg_old_age = getAveragAge(olds);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770