Object Oriented Programming

Started by
16 comments, last by Paradigm Shift 2000 22 years, 6 months ago
Abstract base classes can also be known as "Interface classes"; specifically, a base class with only public members, no implementations (all pure-virtual functions) and no data members can be called an "interface". This is extremely useful when you want to separate implementation from interface, and greatly helps decoupling code.

Example: an IQueue interface might give you functions like open, get, put, waitForData, etc. IQueue derivatives are forced to implement these functions--say a SocketQueue, FileQueue, etc. An application would see these only as IQueues, and would have no need to know about whether they were dealing with Sockets or Files, and would not have to recompile every time SocketQueue or FileQueue changed.

Game Example: IPlayer could be used as a base class for LocalHumanPlayer, RemoteHumanPlayer (e.g. network), and ComputerPlayer. The game engine only sees IPlayer--it does not need to recompile every time any of those derived classes change.

In other words, a class like this describes a protocol or contract that clients of derived classes will stick to. Derived classes must implement the functions given, guaranteeing they''ll be there.
Advertisement
Oh, I see now, I think. Just as an example because this would be completely useless in practice but basically, what you''re saying is that if I made a pure virtual base class like CFile, and gave it a virtual void Load(const char * filename) function, any derivation i make like CBmp : public CFile *must* define a void Load(const char * filename) function otherwise the compile will error out. Then, I could use in my code CFile * bmpfile = new CBmp; bmpfile->Load("foo.bmp") and it would always use the proper Load() function without recompiling even if I modified the code for void CBmp::Load(const char * filename)? Would I then need to implement this type of class in a DLL(which is what I''m currently doing with my file loaders so I don''t need to recompile all my projects whenever I need to fix a bug in the loader code)?

Paradigm Shift 2000
"I am Locutus of Borg. Resistance is Futile." -- Locutus of Borg
quote:Original post by Paradigm Shift 2000
what you're saying is that if I made a pure virtual base class like CFile, and gave it a virtual void Load(const char * filename) function, any derivation i make like CBmp : public CFile *must* define a void Load(const char * filename) function otherwise the compile will error out.

That is correct.
quote:
Then, I could use in my code CFile * bmpfile = new CBmp; bmpfile->Load("foo.bmp") and it would always use the proper Load() function without recompiling even if I modified the code for void CBmp::Load(const char * filename)?

Any call to bmpfile->Load would call CBmp::Load, because bmpfile is-a CBmp (though it looks like a CFile). However, this function _would_ need to recompile because of the "new CBmp". Since you'd be calling the CBmp::CBmp constructor, this module would be sensitive to the changes in CBmp implementation file. However, anything else that calls through bmpfile only relies on CFile, not on CBmp. This is why you usually physically separate the part of the code that allocates objects from the code that uses those objects if you're using the interface pattern. Usually, there's a "factory" class that generates the objects (has all the calls to new) and returns CFile, making clients of that class completely oblivious to CFile derivatives. It's just that every time a derived class changes, the factory class must recompile; but that's better than having your entire engine recompile.

quote:
Would I then need to implement this type of class in a DLL(which is what I'm currently doing with my file loaders so I don't need to recompile all my projects whenever I need to fix a bug in the loader code)?


You could do it either way--in a DLL or in your main executable. One doesn't dictate the other. But if you wanted to patch the CBmp implementation without having to patch the executable, doing it in a DLL would solve that problem.

Edited by - Stoffel on September 24, 2001 11:40:40 PM
Ah, I see. Thank you so much for that clarification. :-)

Paradigm Shift 2000
"I am Locutus of Borg. Resistance is Futile." -- Locutus of Borg
You''ll want to use a pure virtual class to represent abstract concepts. For a silly example, Food is an abstract concept, we know that we can do certain things with food (like eating it) but there''s not too many other information we can put into our food class & still retain generality. So we can make Food have a virtual Eat() function, then all food objects can inherit from Food & we know how to Eat() any object. You use pure virtual functions when you want to define interface without defining any kind of implementation. As a side note I try to write C++ in a Java style, only inherit from one base class that has defined functions, all other inheritance must come from Abstract Base Classes (ie all pure virtual functions). OO design is cleaner & there is usually no need for multiple inheritance and the evil that results from it.

Brad
I think he finally got it!

Yeah. the biggest reason I use an abstract base class is so I can do stuff like this:

bool Road::add(Vehicle * vehicle);

This way, no matter what type of Vehicle I send it, when it comes time to draw the vehicles moving on the road, all I have to do is this:

void Road::draw()
{
for (j=0; j < vehicleList.length(); j++)
{
vehicleList[j]->draw();
}
}

And it would know exactly which version of draw() to call--be it truck, car, van, or motorcycle--because draw() is virtual . And because add() takes a pointer to an abstract class, I can send it any of its derived classes, instead of creating multiple overloaded versions of add().

And of course, sometimes I do it just for the heirarchy.

Jinushaun

Nation Leprechaun

Edited by - jinushaun on September 25, 2001 12:34:30 PM
Oh! I don''t know why I didn''t see it before, because now it seems so obvious. I could say, define in a DirectDrawObject class a void LoadResource(CFile * file) function, and then use file->Load(filename) and it would always use the right Load() function, without me having to overload the LoadResource() function for each different type of file. That would be *so* useful!

Paradigm Shift 2000
"I am Locutus of Borg. Resistance is Futile." -- Locutus of Borg
Just wanted to say that the base class doesn''t have to be entirely pure. You can mix non-pure member functions with pure except you can''t create an object out of that base class. But if you derive from the base and create a base pointer and assign a derived object''s addy to it then the base member function code still gets executed but parts of derived object''s code also is executed within the base. So your base could crunch code and be like a high level platform while under it is specific code the platform uses i.e. derived object''s code. Don''t forget to make your destructors virtual. When I started c++ I used to think in terms of having an interface without any code and then implement that code in derived objects but try to put majority of common code in the base and see what you get

This topic is closed to new replies.

Advertisement