Re: Downcasting base-class objects to a derived-class
There are a lot of issues, see below. I'm leaving a few things out,
you will want to read the C++ FAQ linked to below thoroughly, as well
as all of the other information:
On Dec 6, 12:27 pm, vsk <vmi...@gmail.com> wrote:
In my AP Comp. class, we wrote a Symbolic Algebra program in Java that
is completely based on one interface: IExpression.
I want to port my Java code to C++, for experience, and I'm having a
few issues.
C++ doesn't (to my knowledge) have an equivalent of an Interface, so
I;
class IExpression {
public:
IExpression() {};
virtual bool hasVar() ;
virtual double eval(double);
virtual string getStr();
virtual string getSmart();
virtual bool equals(IExpression&);
virtual IExpression simplify();
virtual IExpression derivative();
};
You'll want to make sure you declare and define a virtual destructor
as well:
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7
Also, unless your IExpression virtual functions are pure virtual
functions, you'll need to define a base implementation for the
functions (one major cause of your vtable error). Plus, pure virtual
functions will bring you closer to a Java interface. Put a "= 0" after
the declaration:
class IExpression {
public:
IExpression() {}
virtual ~IExpression() {}
virtual bool hasVar() = 0;
virtual double eval(double) = 0;
virtual string getStr() = 0;
virtual string getSmart() = 0;
virtual bool equals(IExpression&) = 0;
virtual IExpression simplify() = 0;
virtual IExpression derivative() = 0;
};
This requires a derived class to implement those functions. However,
this breaks your code. You have simplify() returning an actual
IExpression instance. That won't work for two reasons:
1. The abstract IExpression can't be instantiated on it's own.
2. This won't work anyways as an "IExpression" can only be an
IExpression, it can't be something derived from an IExpression. Your
declarations return an actual IExpression, not a reference/pointer to
something derived from an IExpression. There are other solutions, the
"simplest" way is to return a pointer to an IExpression instead, e.g.:
virtual IExpression *simplify () = 0;
virtual IExpression *derivative () = 0;
I quote "simplest" because there, you'll have to allocate new
IExpressions with new and remember to delete them:
class SomeExpression : public IExpression { ... }
IExpression * SomeExpression::simplify () {
IExpression *simplified = new SomeExpression(...); // e.g.
return simplified;
}
And then:
void f () {
SomeExpression ex;
IExpression *simplified = ex.simplify();
// use simplified, then...
delete simplified; // <-- must clean up
}
Smart pointer implementations can help you clean up automatically.
Other interface designs could remove the need to use new entirely.
Use regular virtual functions if you want to provide a default base
implementation and make it optional for derived classes to implement
the functions, e.g. with no-op base implementations:
class IExpression {
public:
IExpression() {}
virtual ~IExpression() {}
virtual bool hasVar() { }
virtual double eval(double) { }
virtual string getStr() { }
virtual string getSmart() { }
virtual bool equals(IExpression&) { }
virtual IExpression simplify() { }
virtual IExpression derivative() { }
};
Once the "interface" or base-class was done, I wanted to implement it
with a simple class from my project: Number;
class Number : public virtual IExpression {
Virtual inheritance changes the rules a lot. C++ supports multiple
inheritance which can lead to a problem known as the "dreaded
diamond", virtual inheritance is intended to fix that. For
simplicity's sake, you don't really want to use virtual inheritance
here; your IExpression has no data members. Read this:
http://www.parashift.com/c++-faq-lite/multiple-inheritance.html
Specifically:
http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.9
So consider this instead:
class Number : public IExpression {
...
};
private:
double value;
void init();
public:
Number(double);
bool equals(Number &that);
bool hasVar();
double eval(double);
string getStr();
string getSmart();
bool equals(IExpression&);
IExpression simplify();
IExpression derivative();
};
I wrote the implementation of Number's methods in the header, and I
wont bother posting (most of) them.
The one that's giving me hell is;
bool Number::equals(IExpression &that) {
if (typeid(this) == typeid(that)) {
return this->equals(reinterpret_cast<Number&> (that));
} else {
return false;
}
Your syntax looks correct, unless I'm missing something subtle the
compiler should be accepting this. Make sure you've #included
<typeinfo>.
However, "this" is a pointer type (it's a Number *) and "that" is a
reference type. Therefore "typeid(this)" will never equal "typeid
(that)". Even if "that" is a number, it's type is "Number &", not
"Number *". You'd want this:
if (typeid(*this) == typeid(that)) ...
But even then, it's probably safer just to use dynamic_cast. Read
this:
http://www.cplusplus.com/doc/tutorial/typecasting.html
You could do something like this:
bool Number::equals(IExpression &that) {
try {
Number &other = dynamic_cast<Number &>(that);
// do stuff with other as a Number
} catch (std::bad_cast&) {
return false;
}
}
}
bool Number::equals(Number &that) {
return this->value == that.value;
}
C++ has given me arcane error messages, and I don't know what I'm
doing that's so horribly incorrect.
I think it's a down-casting problem in equals(), but it's also telling
me that I have an "undefined reference to vtable".
How can I fix this?
The major problem with the vtable is because you did not provide a
base implementation of any of your IExpression virtual functions.
By the way, you want to read this section on const-correctness:
http://www.parashift.com/c++-faq-lite/const-correctness.html
HTH,
Sorry if I left anything out,
Jason