 Home Page   kurs "Filip"  Mail me [ Home ] [ Up ] [ What's new ] [ Downloads ] [ Interests ] [ Education ] [ Projects ] [ Programming ] [ Arts ] [ Journeys ] [ Photos ] [ Jokes ] [ Friends ] [ Links ]

Problem

Solution

# Inheriting only one time

## The problem

Let's suppose we have a class:

class vector {

public:

class itr;

int *array;

int get(int no);

itr getitr(int no);

};

It encapsulates some kind of data structure. This is just an example, so I put only the most important members here, and there is no privacy.

And it has another class as a member:

class vector::itr {

public:

int *pointer;

void next() { ++pointer; }

int operator*() { return *pointer; }

};

In this example, this is class which allows iterating through objects stored in data strucure.

And the vector class has a twin brother:

class list {

public:

class itr;

struct node {

int data;

int *next;

};

node *tail;

int getman(int no);

itr getitr(int no);

};

This is another implementation of data structure. It provides the same methods as previous class, so that there is no difference whether we work on any of that classes.

So, it same has a class as a member:

class list::itr {

public:

node *pointer;

void next() { pointer=pointer->next; }

int operator*() { return pointer->data; }

};

Now we create a template class, which gets as an argument one of either vector or list:

template<class C>

class adder : public C {

public:

int sum();

}

This is an example of use of data structure classes - it iterates through all data and calculates a sum.

Now we want to create a class that will need the itr class to provide greater functionality:

template<class C>

class divcalculator : public C {

public:

class itr;

double getvalue(int);

itr getitr(int no, double data);

}

For example, to be able to use some additional data:

template<class C>

class divcalculator<C>::itr : public C::itr {

public:

double *data;

divcalculator<C>::itr(double *p) : data(p) {}

double getdivvalue() { return operator*() / data[ operator*() ]; }

};

So far, so good. Everything works.

Now, we want to create one more class, similar to the previous, but providing different services:

template<class C>

class mulcalculator : public C {

public:

class itr;

double getvalue(int);

itr getitr(int no, double *data);

}

That alters itr in the same way:

template<class C>

class mulcalculator<C>::itr : public C::itr {

public:

double *data;

mulcalculator<C>::itr(double *p) : data(p) {}

double getdivvalue() { return operator*() * data[ operator*() ]; }

};

The problem is what happens if we try just to combine functionality of divcalculator and mulcalculator

We can use multiple inheritance: create new class that inherits both from divcalculator and mulcalculator, but this is not a good idea. We want to calculate on the same values, not on two copies. Multiple virtual inheritance is not good, either - it adds a pointer to each class, which consumes memory and time, that is resolved at run-time. We want our classes to be fast, that's why we have used templates.

The second approach is we can do something like this: mulcalculator<divcalculator<vector> >. This is better, we operate on the same data, we have no additional pointers. But the real problem, that I will try show you how to solve is that now the mulcalculator<divcalculator<vector> >::itr class has two pointers to data, that we suppose are the same.

## Solution

The solution is quite complicated. It bases on the fact that nested types can be redefined and made available to child classes. What we do is:

• Make two additional classes: one with pointer to double, and one empty, without any pointers, just fake one. Both should have default constructors (this can be modified, I will write about it later).
• There should be template helper class defined, that will inherit from template parameter, and the only thing it enhances is new nests with typedef new type - the one with real pointer to double. Let's suppose the name of the nested type is doublepointerclass.
• In each class that needs itr to have pointer to double, in our case both divcalculator and mulcalculator, the redefined itr class should inherit both from parent's itr class and the class denoted by doublepointerclass member. In the main class doublepointerclass member should be redefined to denoted the class with fake pointer to double. If class doesn't need the extended itr class, just do nothing with it.
• Now we can use data member in constructor of redefined itr, whatever happens, it will be there, and there will be only one copy of it.

Remark: make sure you have zero-length base classes option checked on. In other case, you will get many unused bytes of memory wasted.

## Example

So, we can modify our original class hierarchy in this way:

Two classes holding pointer to double:

class doublepointer {

public:

double *p;

};

class doublepointerfake {

};

Helper class - ancestor for other classes:

template<class C>

class strct2calc : public C {

public:

typedef doublepointer doublepointerclass;

};

The new divcalculator class:

template<class C>

class divcalculator : public C {

public:

class itr;

typedef doublepointerfake doublepointerclass;

double getvalue(int);

itr getitr(int no, double data);

}

And it's itr class:

template<class C>

class divcalculator<C>::itr : public C::itr, C::doublepointerclass {

public:

divcalculator<C>::itr(double *p) { data = p; }

double getdivvalue() { return operator*() / data[ operator*() ]; }

};

The class mulcalculator should be modified in similar way.

## Where it is used

I used it in my NoNameLib, in the implementation of iterators. It's because sometimes iterator has to have pointer to container it is iterating trough. And in my library there is inheritance similar to the shown here, that new classes inherit from it's template parameter and expand it's functionality. Sometimes more than one of such classes in a single branch of inheritance needs expanded iterators. And that's why I had to make this up.

Well, I know, this is quite complicated, but if you will look into it, I guess you understand, what I wanted to say. If not, email me.

 Home Page Top of page [ Back ] [ Up ] [ Next ] [ Up ] Page created 2001-03-17 17:02:36, last edited 2003-11-10 12:44:51   by Marcin Wudarczyk