C Board  

Go Back   C Board > General Programming Boards > C++ Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 11-02-2009, 04:26 PM   #1
Registered User
 
Join Date: Oct 2009
Posts: 38
Virtual functions

I have a Hallway class that is made up of a vector of Rooms. I have two types of Rooms - Bathrooms and Bedrooms. The toString() function in Room is a virtual function that I want to be overloaded by Bathroom and Bedroom. Whenever I call Hallway's toString() function, I want it to go through and call each Room's toString() function. The problem is that instead of calling Bathroom's or Bedroom's toString(), it calls Room's toString(). I'm assuming it's because the Hallway is a vector of Room's and not Bathroom's or Bedroom's. But I thought it would call the appropriate virtual function if a Room is a subclass. How can I check what kind of Room each Room is, and call the appropriate toString()? Here is some code if it's needed

Code:
string Hallway::toString() {
	ostringstream result;
	result<<"The hallway consists of "<<getRoomVec().size()<<" rooms."<<endl;
	result<<"---------------------------------------------"<<endl;
	for(int i=0;i<getRoomVec().size();i++) {
		result<<getRoomVec().at(i).toString()<<endl;
	}
	result<<"---------------------------------------------"<<endl;
	return result.str();
}
Code:
string Bedroom::toString() {
	ostringstream result;
	result<<"The bedroom consists of: "<<endl;
	result<<"---------------------------------------------"<<endl;
	for(int i=0;i < getItemVec().size();i++) {
		result<<getItemVec().at(i).toString()<<endl;
	}
	result<<"---------------------------------------------"<<endl;
	if(areLightsOn())
		result<<"The bedroom's lights are on."<<endl;
	else
		result<<"The bedroom's lights are off."<<endl;
	result<<"The remaining length to the bedroom is "<<getAvailableL()<<"."<<endl;
	result<<"The remaining width to the bedroom is "<<getAvailableW()<<"."<<endl;
	return result.str();
}
Code:
string Bathroom::toString() {
	ostringstream result;
	result<<"The bathroom consists of: "<<endl;
	result<<"---------------------------------------------"<<endl;
	if(getShowerVec().size() > 0) {
		for(int i=0;i<getShowerVec().size();i++) 
			result<<getShowerVec().at(i).toString()<<endl;
	}
	if(getStallVec().size() > 0) {
		for(int i=0;i<getStallVec().size();i++)
			result<<getStallVec().at(i).toString()<<endl;
	}
	if(getSinkVec().size() > 0) {
		for(int i=0;i<getSinkVec().size();i++)
			result<<getSinkVec().at(i).toString()<<endl;
	}
	if(getMirrorVec().size() > 0) {
		for(int i=0;i<getMirrorVec().size();i++)
			result<<getMirrorVec().at(i).toString()<<endl;
	}
	result<<"---------------------------------------------"<<endl;
	if(areLightsOn())
		result<<"The bathroom's lights are on."<<endl;
	else
		result<<"The bathroom's lights are off."<<endl;
	result<<"The remaining length to the bathroom is "<<getAvailableL()<<"."<<endl;
	result<<"The remaining width to the bathroom is "<<getAvailableW()<<"."<<endl;
	return result.str();
}
Code:
string Room::toString() {
	string nothing;
	return nothing;
}
Code:
int main() {
	Bathroom room;
	Shower one;
	Shower two;
	Stall three;
	Stall four;
	Stall five;
	Mirror six;
	Sink seven;
	room.addShower(one);
	room.addShower(two);
	room.addStall(three);
	room.addStall(four);
	room.addStall(five);
	room.addMirror(six);
	room.addSink(seven);
	Hallway hall;
	hall.addBathroom(room);
	cout<<hall.toString();

	return 0;
}
SterlingM is offline   Reply With Quote
Old 11-02-2009, 04:49 PM   #2
Registered User
 
Join Date: Sep 2004
Location: California
Posts: 2,845
Code:
I'm assuming it's because the Hallway is a vector of Room's and not Bathroom's or Bedroom's.
Sounds like you have an issue with object slicing when you add the Bedrooms to the vector. Make the container a vector of Room pointers instead of just a vector of Rooms. This will allow the appropriate virtual function to be called.
__________________
bit∙hub [bit-huhb] n. A source and destination for information.
bithub is offline   Reply With Quote
Old 11-02-2009, 04:49 PM   #3
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
Polymorphism only works on pointers or references.
Furthermore, when you add a non-pointer to an object, you slice it. Meaning that everything that is not a Room is lost in the conversion to a Room.
So in effect: you need to store pointers allocated with new or some such. You can use smart pointers to avoid memory management.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 11-02-2009, 04:50 PM   #4
Registered User
 
Join Date: Jan 2005
Posts: 7,137
The problem appears to be that your Hallway vector holds Room objects, not pointers. Since it holds Room objects, every time you add a Bathroom or Bedroom to the hallway, you slice off the derived-ness of the class and only the base class data is saved.

Dynamic polymorphism, which is what you're correctly trying to use, only works on pointers or references. You can't store references in vectors or arrays, so you have to store pointers. Make Hallway store a vector<Room*> instead.

Doing this brings up some other interesting challenges. The most important is, who "owns" the Rooms? It probably doesn't make sense for a Hallway to own the rooms it is connected to, so you'll have to identify something else that keeps the master pointer to each room and deletes it when it is all done.

There are more advanced tools to help with this, including shared_ptr. If you can use that, I'd recommend it, but often that is not allowed for assignments since you'd have to get another library like boost. If you can't, then you just have to pay extra attention to who maintains the pointer and when it gets deleted.
Daved is offline   Reply With Quote
Old 11-02-2009, 04:54 PM   #5
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
As a side note, shared_ptr is also available in TR1: std::tr1::shared_ptr and in C++0x: std::shared_ptr.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 11-02-2009, 07:17 PM   #6
Registered User
 
Join Date: Oct 2009
Posts: 38
Okay I got it working how I wanted it. Another question though, is there any way I could check whether a Room was a Bathroom or Bedroom? Mainly to have a Hallway toString() looks something like this

Room 1 is a //Bedroom or Bathroom
It consists of ...

Then continue along
SterlingM is offline   Reply With Quote
Old 11-02-2009, 07:23 PM   #7
Registered User
 
Join Date: Jan 2005
Posts: 7,137
That should not be part of the Hallway code, that should be part of the Bedroom or Bathroom toString methods (or part of the Room toString method if you wanted to get a little fancier).

If you don't want that part of the toString() for Bedroom and Bathroom, you can have the Hallway do something like this:
Code:
cout << "Room " << room_index+1 << " is a " << rooms[room_index]->getRoomTypeName() << '\n';
// ...
But putting it in the rooms' toString methods is probably better.

Last edited by Daved; 11-02-2009 at 07:25 PM.
Daved is offline   Reply With Quote
Old 11-02-2009, 11:39 PM   #8
Registered User
 
Join Date: Oct 2006
Posts: 263
Quote:
Originally Posted by SterlingM View Post
Okay I got it working how I wanted it. Another question though, is there any way I could check whether a Room was a Bathroom or Bedroom? Mainly to have a Hallway toString() looks something like this

Room 1 is a //Bedroom or Bathroom
It consists of ...

Then continue along
you can use dynamic_cast:

Code:
  Room *room = new Room;
  if (dynamic_cast<Bathroom*>(room))
  {
    std::cout << "room is a Bathroom." << std::endl;
  }
  else if (dynamic_cast<Bedroom*>(room))
  {
    std::cout << "room is a Bedroom." << std::endl;
  }
  else
  {
    std::cout << "room is neither." << std::endl;
  }
Elkvis is offline   Reply With Quote
Old 11-02-2009, 11:39 PM   #9
Registered User
 
Join Date: Oct 2009
Posts: 38
Yeah I had planned on putting it into Room's toString(), but how can a Room pointer check what type of Room it is? Is there a function for situations like this?
SterlingM is offline   Reply With Quote
Old 11-02-2009, 11:40 PM   #10
Registered User
 
Join Date: Oct 2009
Posts: 38
Ah okay, the above code got posted before I posted.
SterlingM is offline   Reply With Quote
Old 11-03-2009, 02:10 AM   #11
Registered User
 
Join Date: Nov 2009
Posts: 2
---"Polymorphism only works on pointers or references."
This is the answer.
For more, please refer to STL allocator.
YuJunheng is offline   Reply With Quote
Old 11-03-2009, 10:03 AM   #12
Mysterious C++ User
 
Join Date: Oct 2007
Posts: 14,099
You should not rely on deducing the type of the pointer or object. You should rely on the proper functions being called by the compiler.
Derive multiple classes from Room, each with your different type of room and overload your virtual function for each of them. When called, the proper function in the proper class will be called due to polymorphism. You do not need to know the type, unlike Java.
__________________
Using: Microsoft Windows™ 7 Professional (x64), Microsoft Visual Studio™ 2008 Team System
I dedicated my life to helping others. This is only a small sample of what they said:
"Thanks Elysia. You're a programming master! How the hell do you know every thing?"
Quoted... at least once.
Quote:
Originally Posted by cpjust
If C++ is 2 steps forward from C, then I'd say Java is 1 step forward and 2 steps back.
Elysia is offline   Reply With Quote
Old 11-03-2009, 11:51 AM   #13
Registered User
 
Join Date: Jan 2005
Posts: 7,137
>> you can use dynamic_cast
You can, but that's a bad idea here and a bad habit to get into. Don't do it.

>> how can a Room pointer check what type of Room it is?
It doesn't have to. If you use the Room's toString() method, you could do something like this:
Code:
cout << "Room " << room_id << " is a " << getRoomTypeName() << '\n';
Like in my previous example, getRoomTypeName would be a virtual function that is implemented in the derived classes to return the room type as a string.

If you find yourself trying to identify which derived type your Room pointer points to, you need to rethink what you're doing. Usually, whatever information you are trying to get (in this case, the type of room as a string) can be moved into a separate virtual function that can then be called on the Room pointer. That way you don't need to know what derived class it is, the virtual function will handle that for you.

Does that make sense?
Daved is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
i need a good example of virtual functions Anddos C++ Programming 10 02-15-2006 11:48 AM
Virtual Functions guda C++ Programming 3 11-16-2004 04:13 PM
Thread creation on virtual functions gustavosserra C++ Programming 13 10-14-2004 08:03 AM
recursive virtual functions ygfperson C++ Programming 0 05-25-2003 08:00 PM
Exporting Object Hierarchies from a DLL andy668 C++ Programming 0 10-20-2001 01:26 PM


All times are GMT -6. The time now is 02:34 AM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.0 RC2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22