Two classes derived from virtual base class; one of which needs an extra member function

Hi,

I have a simple class hierarchy comprising two derived classes and a pure virtual base class. Each derived class provides an alternative way to carry out essentially the same calculation. There are also some static member variables and functions related to initializing the supporting data, which are unique to each derived class. My intent is to allocate a base class pointer to whichever form of the calculation I need.

For the most part that's all working well.

My problem is that one of the derived classes (let's call it "Two") needs a supporting member variable and functions that is not shared with "One" (or, clearly, "Base"). Unfortunately I can no longer use a base class pointer to switch between the two classes once I add the supporting members. If I do my compiler (correctly) tells me the base class has no member matching the name of the supporting members. I tried adding a corresponding virtual function to the base class, but a) it seemed wrong to do so and b) my compiler then complained that it could not allocate an object of type "One" to the pointer.

Here is the basic structure:

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
34
35
36
37
38
//Header
class Base
{
    virtual void set() = 0;
private:
    Point2D coordinate;
};

class One : public Base
{
    void set();
}

class Two : public Base
{
    void set();

    // This is the offending function...
    bool applyCorrections();

private:

    // ... and associated variable
    bool doCorrections;
};

// Calling code
Base* pBase = new One;

// Do something with One

delete pBase;
pBase = new Two;

// Do the alternative calculation using Two

delete pBase;

Everything works perfectly without the applyCorrections() member in "Two". Can anyone point me to a method that allows me to still work with a base class pointer, but still allows me to include the supporting members? Have I made an obvious mistake? Do I need to change the hierarchy somehow? Or is it simply impossible?

Cheers,

Frank
1
2
3
4
if(typeid(pBase) == typeid(Two))
{
    ((Two*)pBase)->applyCorrections();
}

Is this what you want?
http://www.cplusplus.com/reference/std/typeinfo/
Rather than check typeid and use C-style casting (blech), I would favor dynamic_cast, since this is exactly what it's for:

1
2
3
Two* t = dynamic_cast<Two*>(pBase);
if(t) // t will be null if the cast failed
  t->applyCorrections();


Notes:
1) 'Base' needs a virtual destructor if you are using it like this.
2) Downcasting is a sign of a design flaw. Practical use of it is extremely rare. Why can't you use a Two pointer here instead of a Base pointer?
Last edited on
Your example seems a bit constructed and it's not clear what the semantics of the real problem are, particularly the purpose of applyCorrections(). If you really need to have access to the full interface of Two at all times, then why have a base class at all?

Without understand the desired behaviour of the system it's hard to suggest a technique. However, stabbing completely in the dark I would suggest:

o Call applyCorrections() from Two::set()
o Redesign the interface of Base appropriately
o Don't use dynamic polymorphism as it's not appropriate to your problem.

If you can explain the real problem, we may be able to suggest a more appropriate design.

I would say, that the solutions suggested by Disch and L B may work, but to me they seem to be heading down the road of bad design. I think the circumstances where it's a good idea to cast at every call site are quite rare.
Last edited on
Thanks for the replies, everyone, particularly on New Years Day.

kev82, you make very good points. Sorry if my example is a bit "constructed" -- I was just trying to show the essence of the problem. I hope the following explanation clears things up:

My application needs to evaluate a fairly lengthy series of time-based polynomial coefficients for a given date. There are two ways to do so: the modern, more precise method (embodied by "One") and the older, less precise method ("Two"). The trade-offs are that the new method has a very limited range of validity, whereas the old method is valid for a *much* longer period of time but needs to be 'tweaked' to make it more precise (the corrections).

Their interfaces are essentially identical -- you pass in a date (via set(), although I didn't show the argument in my code example) and get a two-dimensional coordinate value back -- which is why I've designed them using a pure virtual base class. Internally the polynomial series are very different, plus they each load a different supporting file (via lazy initialization) containing the polynomial terms.

(The functions associated with loading the polynomials are declared static, as are the vectors that contain them. The structures held in the static vector for each class are also different.)

Both methods are considered to be canonical standards, but in the case of the older method the standard only applies to the *uncorrected* version. So, applyCorrections() simply sets the doCorrections flag, which enables the corrections at the appropriate time (called indirectly via set()).

I am currently developing these classes in isolation and eventually plan to declare a pointer to Base as a member of a higher-level class. I will then allocate either a new One or Two object to the pointer depending on whether the chosen date falls within One's period of validity or not. (applyCorrections() will be an optional extra, but only for an instance of Two).

Hopefully this clarifies rather than clouds the issue. Perhaps a class hierarchy wasn't the best way to design this, so I'm happy to see any other suggestions.

Cheers,

Frank
What code have you written uptill now ?
I'm still not sure which piece of code has the responsibility to decide whether the corrections need to be applied or not. What pieces of information are required to decide if you want to apply the corrections?

If it can be decided by using only the date, and things you can calculate from it, then it is internal to algorithm 2, and the code should be in Two::set()

If it needs more information than that, then you ought to think about having 3 algorithms instead of two. I.e. One, TwoWithoutCorrections, and TwoWithCorrections. That way you decide at the point of instantiation whether you want corrections applied or not. E.g.

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

#include <iostream>

class Base
{
public:
	virtual void set() =0;
	virtual ~Base() {}
};

class One : public Base
{
public:
	virtual void set() {
		std::cout << "Called One::set()" << std::endl;
	}
	virtual ~One() {}
};

template<bool applyCorrections_>
class Two : public Base
{
public:
	virtual void set() {
		if(applyCorrections_) {
			std::cout << "Called TwoWithCorrections::set()" << std::endl;
		} else {
			std::cout << "Called TwoWithoutCorrections::set()" << std::endl;
		}
	}
	virtual ~Two() {}
};

typedef Two<false> TwoWithoutCorrections;
typedef Two<true> TwoWithCorrections;

void f(Base &b) {
	b.set();
}

int main() {
	One o;
	TwoWithCorrections twc;
	TwoWithoutCorrections twoc;

	f(o);
	f(twc);
	f(twoc);

	return 0;
}

Both methods are considered to be canonical standards, but in the case of the older method the standard only applies to the *uncorrected* version. So, applyCorrections() simply sets the doCorrections flag, which enables the corrections at the appropriate time (called indirectly via set()). 


This part is fairly unclear can you elobrate .
bluecoder and kev82, thanks again for your comments.

The decision to apply the corrections is entirely up to the user. Without them the coordinate value is calculated by a single function, which is called by set(). With them, set calls an additional two functions.

I've written up all of the code for all forms of calculation but am not averse to tearing it apart after redesigning the interface. Clearly it isn't working properly as it is.

Cheers,

Frank
In that case, then I would just think about it as 3 separate algorithms, and use code similar to what I do above.

If you don't like the template solution, then pass a boolean constructor argument to Two.
Can the client set or unset the correction flag at any time?
If yes, I think Base class should have a virtual method applyCorrection(bool) which throw an NotSupported(or whatever) exception by default, but this will clutter Base interface.
If not, do like kev82 said, which is prefferable.
If you don't like the template solution, then pass a boolean constructor argument to Two.


That should do nicely (and is kinda obvious once it's pointed out). Thanks.
Topic archived. No new replies allowed.