why dynamic_cast failed?

X

xgngli

Here I have four classes: RefBook and TextBook, which are inheritated
from base class Book; and a class Database, which has an array to store
the pointers to those two kinds of books. Now I am writing a member
function of Database, print(), which prints the information of all the
RefBooks in the array by using dynamic_cast. Below is my code(some
implemetations omitted). I don't know why the compiler complains that
"cannot dynamic_cast type Book* to type RefBook* (source type is not
polymophic)".

class Book
public:
Book(const string& = "");
const string& getName() const;
private:
string name_;
};

class RefBook : public Book {
public:
RefBook(const string&, char);
char getSubject() const;
void print() const;
private:
char subject_;
};

class TextBook : public Book {
public:
TextBook(const string&, int);
private:
int course_;
};

class Database {
public:
Database();
enum {SIZE = 100};
void addBook(Book*);
void printRef() const;
private:
Book* book_[SIZE];
int number_;
};

void Database::printRef() const {
for(int i = 0; i < number_; i++)
// compiler complains "source type is not polymophic", what's
wrong?
if(RefBook* rb = dynamic_cast<RefBook*> (book_))
cout << rb->print() << endl;
}


int main() {
Book* a = new RefBook("Relativity Theory", 'p');
Book* b = new TextBook("Database Management", 113);
Database db;
db.addBook(a);
db.addBook(b);
db.printRef();
delete a;
delete b;
return 0;
}
 
X

xgngli

I suspect there is another problem here. The addBook function takes the
type Book* as a parameter.
class Database {
public:
Database();
enum {SIZE = 100};
void addBook(Book*);
void printRef() const;
private:
Book* book_[SIZE];
int number_;
};

but when I use it in "db.addBook(a)", a type conversion from RefBook*
to Book* occured. Is this a "slicing problem"? If so, do I have to
define two addBook funtions(one for RefBook and one for TextBook) to
add books in the database?
 
T

Thomas J. Gritzan

I suspect there is another problem here. The addBook function takes the
type Book* as a parameter.
yes.
class Database {
public:
Database();
enum {SIZE = 100};
void addBook(Book*);
void printRef() const;
private:
Book* book_[SIZE];
int number_;
};

but when I use it in "db.addBook(a)", a type conversion from RefBook*
to Book* occured.

No, "a" already is a Book*. But it points to a RefBook object.
Is this a "slicing problem"?

No. Slicing occurs with values, not with pointers.
 
T

Thomas J. Gritzan

Here I have four classes: RefBook and TextBook, which are inheritated
from base class Book; and a class Database, which has an array to store
the pointers to those two kinds of books. Now I am writing a member
function of Database, print(), which prints the information of all the
RefBooks in the array by using dynamic_cast. Below is my code(some
implemetations omitted). I don't know why the compiler complains that
"cannot dynamic_cast type Book* to type RefBook* (source type is not
polymophic)".

The problem is: dynamic_cast works only with polymorphic types. That means,
the classes have to have virtual functions.
class Database {
public:
Database();
enum {SIZE = 100};
void addBook(Book*);
void printRef() const;
private:
Book* book_[SIZE];

Is there a reason for a fixed array size here? What if you have more than
100 books?

Make that a std::vector<Book*>.
Think about the ownership of the books. Should the database call "delete"
on the pointers in its destructor?
void Database::printRef() const {
for(int i = 0; i < number_; i++)
// compiler complains "source type is not polymophic", what's
wrong?
if(RefBook* rb = dynamic_cast<RefBook*> (book_))
cout << rb->print() << endl;
}


Bad idea. Maintainance hell.

In general, the base class (Book) should have a virtual function "print()"
which is overwritten in the subclasses. Then the database could simply call
print() on all books stored.
But it seems that you want to print out only the RefBook subclasses. Since
I don't know your design here, I can't suggest better alternatives.

Another thing:
cout << rb->print() << endl;

Since RefBook::print() has return type void, this won't compile.
 
X

xgngli

Thank you Thomas. I have to have at least one virtual function to make
dynamic_cast work.
 
B

Bo Persson

Thank you Thomas. I have to have at least one virtual function to
make
dynamic_cast work.

As you are deleting through a pointer to base class, you must have a
virtual destructor in the base class. That would satisfy the
requirement for dynamic_cast as well.


Bo Persson
 
E

Earl Purple

Thank you Thomas. I have to have at least one virtual function to make
dynamic_cast work.

You'll want a virtual destructor in Book and a virtual method that
Database calls. Then get rid of the dynamic_cast. dynamic_cast is
generally evil. That doesn't mean you'll never do it but you won't do
it most of the time. There are some exceptions but this isn't one of
them.

To handle the memory management, you'll probably either want to use
shared_ptr (probably simplest) or a type of collection like ptr_vector,
either of which can be found in boost. shared_ptr is going to be part
of the new standard and is already in tr1.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,888
Messages
2,569,964
Members
46,294
Latest member
HollieYork

Latest Threads

Top