Virtual functions

This is a discussion on Virtual functions within the C++ Programming forums, part of the General Programming Boards category; I have a Hallway class that is made up of a vector of Rooms. I have two types of Rooms ...

  1. #1
    Registered User
    Join Date
    Oct 2009
    Posts
    117

    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;
    }

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,264
    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.

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    23,012
    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.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,344
    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.

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    23,012
    As a side note, shared_ptr is also available in TR1: std::tr1::shared_ptr and in C++0x: std::shared_ptr.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    Registered User
    Join Date
    Oct 2009
    Posts
    117
    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

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,344
    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.

  8. #8
    Registered User
    Join Date
    Oct 2006
    Posts
    2,588
    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;
      }

  9. #9
    Registered User
    Join Date
    Oct 2009
    Posts
    117
    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?

  10. #10
    Registered User
    Join Date
    Oct 2009
    Posts
    117
    Ah okay, the above code got posted before I posted.

  11. #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.

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    23,012
    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.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Registered User
    Join Date
    Jan 2005
    Posts
    7,344
    >> 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?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. i need a good example of virtual functions
    By Anddos in forum C++ Programming
    Replies: 10
    Last Post: 02-15-2006, 11:48 AM
  2. Virtual Functions
    By guda in forum C++ Programming
    Replies: 3
    Last Post: 11-16-2004, 04:13 PM
  3. Thread creation on virtual functions
    By gustavosserra in forum C++ Programming
    Replies: 13
    Last Post: 10-14-2004, 09:03 AM
  4. recursive virtual functions
    By ygfperson in forum C++ Programming
    Replies: 0
    Last Post: 05-25-2003, 09:00 PM
  5. Exporting Object Hierarchies from a DLL
    By andy668 in forum C++ Programming
    Replies: 0
    Last Post: 10-20-2001, 02:26 PM

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