This code won't compile (two compilers tried, gcc and VC++, both of recent versions, but I don't remember them exactly): - class C1
-
{
-
public:
-
void M1(int i) {}
-
};
-
-
-
class C2: public C1
-
{
-
public:
-
// using C1::M1;
-
void M1(int i, int j) {}
-
};
-
-
int main(int argc, char *argv[])
-
{
-
C2 c;
-
c.M1(14);
-
c.M1(1, 2);
-
return 0;
-
}
Decommenting the using ... line will make it compilable.
This happens because the C++ compiler assumes you want to redefine the base class'es methods completely, if you use a method with the same name, even if it has a different signature.
This, however, seems to be counter-intuitive to me. If I wanted to redefine a method, I'd do so. But otherwise, why hide a base class'es method away, if there's nothing exactly similar in a derived class?
OOP says that objects of a derived class are a kind of objects of the same class as the base class'es objects, only with additional and/or changed functionality - but I never heard anybody say that objects of publicky derived classes may actually have less functionality than the objects of the base class. It is always assumed that publicly derived classes expand the functionality of the base classe, and don't restrict it.
More than that: it should be always possible to substitute an object of a base class with an object of a derived class. This language feature effectively prevents this: - // something a cat can scratch
-
class Scratcheable
-
{
-
public:
-
virtual void expressPain();
-
};
-
-
// normally, a cat can scratch, itself, when scratching for flees, another cat,
-
// a dog, a human, or anything else that is scratcheable
-
class Cat: public Scratcheable
-
{
-
public:
-
bool scratch() {} // to scratch itself for flees
-
bool scratch(Scratcheable* s) {}
-
void expressPain()
-
{
-
// hiss or meow
-
};
-
};
-
-
// a person can always be scratched
-
class Person: public Scratcheable
-
{
-
public:
-
// cuddling a cat gets her into the mood for playing, cats scratch and bite while playing
-
void cuddle(Cat* c)
-
{
-
c->scratch(this);
-
}
-
void expressPain()
-
{
-
// say Ouch!
-
};
-
};
-
-
// a poor cat which has no paws, and as such cannot scratch
-
// we hide its scratch methods using the awkward C++ feature hide for this
-
// this way of hiding stuff is not uncommon - singletons use private
-
// constructors for making them non-callable from outside
-
// I use a private scratch method to make all scratch methods unavailable
-
// from outside - the language gives me no other idiom for
-
// making the methods unavailable, although I could override all to return
-
// false - but then, the stupidity the language enforces upon
-
// me is even greater: I have to override methods just to nop them, which
-
// is something clearly against sound OOP
-
class CrippledCat: public Cat
-
{
-
private:
-
bool scratch() {};
-
};
-
-
int main(int argc, char *argv[])
-
{
-
Person p;
-
CrippledCat c;
-
// a peson cuddling a crippled cat gets scratched, although a crippled
-
// cat is not supposed to be able to scratch!
-
p.cuddle(&c);
-
// a crippled cat being angry on a peson cannot scratch it, although
-
// it just did! -- compiler errs on next call
-
c.scratch(&p);
-
return 0;
-
}
A person who can cuddle a cat cannot cuddle a crippled cat. (I know that the example is stupid from a conceptual point of view, because it states that scratching _must_ happen when cuddling, so the wrong coupling is in the meaning encoded into the example code, but nevertheless, this example proves the point that an object of a derived class cannot be substituted anymore for an object of the base class due to this language feature.)
It seems reasonable to me to assume that the standards writers had something in mind when they introduced this counter-intuitive-ness. But I don't know what. Can anybody explain it to me?
11 3022
Let's take these in sections:
This happens because the C++ compiler assumes you want to redefine the base class'es methods completely, if you use a method with the same name, even if it has a different signature.
is not true.
C++ has function overloading but you cannot overload between a base class and a derived class. In a class hierarchy, a derived class method with the same name as a base class method is said ro DOMINATE the base class method.
That means a derived object calling the method will call the derived method and further, if the derived object tries to call the base method by having the base arguments, it won't compile becuse of the domination.
In this last case you have you make an explicit call to the base method:
deivedObject.base::themethod(etc..)
Next:
But otherwise, why hide a base class'es method away, if there's nothing exactly similar in a derived class?
Suppose you have Door class. You open the door by using the Open method. There are other methods about the size of the door, the grpahical image of the door, etc.
Now you have a requirement for a Door that opens only with a magic spell. There are various solurtions, but one solution is to derived MagicDoor from Door and redefine the Open method to take a char* that has the magic spell. Then you use Magic Door objects. All of the other Door methods will be inherited.
OOP says that objects of a derived class are a kind of objects of the same class as the base class'es objects, only with additional and/or changed functionality - but I never heard anybody say that objects of publicky derived classes may actually have less functionality than the objects of the base class. It is always assumed that publicly derived classes expand the functionality of the base classe, and don't restrict it.
This is just not true.
A derived class may have more or less functionality than the base class. If the derived class has more functionality than the base class and all you have is a base class pointer to the derived object, you use the Visitor design pattern to gain access to the derived object as a derived object pointer so you can call these additional derived class methods.
In fact, I would say the normal case is to operate with derived objects that have more funcitonality than the base class - and to do this polymorphically by using a base class pointer to these objects AND NO type-casting or RTTI or other silliness.
Then there's the question of interface vs implementation. The interface is the public methods in the base class. The implementation is how those methods are implemented. Normally, you see public virtual functions. This is a common design error. Virtual functions should always be private so as to separate the interface from the implementation. Often the base class has private virtual functions that are overriden by private derived class virtual methods. In effect you call derived methods from the base class. This is called the "Hollywood Principle" (don't call us we'll call you). Once you start doing this, your base class bears little resemblance to your derived class.
More than that: it should be always possible to substitute an object of a base class with an object of a derived class. This language feature effectively prevents this
You can NEVER substitiute an obect of a derived class for an object of the base class. When you do, the derived portion of the object is sliced off and you begin working with an amputee. Unfortunately, the VTBL is still in the remaining portion and when you call one of those methods that need data that has been chopped off the object, you crash.
What is true, is that you can always substitute a derived class POINTER or a REFERENCE for a base class pointer or reference. It must be a pointer or reference or the virtual function mechanism won't work. The underlying principle is the derived object can pose as a base object but this is done by address or reference only.
Suppose you have Door class. You open the door by using the Open method. There are other methods about the size of the door, the grpahical image of the door, etc.
Now you have a requirement for a Door that opens only with a magic spell. There are various solurtions, but one solution is to derived MagicDoor from Door and redefine the Open method to take a char* that has the magic spell. Then you use Magic Door objects. All of the other Door methods will be inherited.
Right. But more often I want to derive a WoodenDoor or an IronDoor, and besides inheriting the open() method, also add an open(int sqeakLevel). Instead of this happening automatically, I have to explicitly state this.
On the other hand, if I have written both the Door and the MagicDoor classes, and somebody else has written a Robot class, which has a method openDoor(Door* pDoor), and I pass it a MagicDoor*, it will open the MagicDoor without a spell - the compiler won't complain. What am I missing?
A derived class may have more or less functionality than the base class.
I agree. Still, it's not nice OOP to create derived classes with less functionality. I can think of no case right now when this would be desirable. The Door example you used is on one hand weak (see the stupid Robot above, which ignores the fact that you don't want the MagicDoor opened without a spell), on the other hand you usually want to complicate/enhance the functionality of a Door (see the Doors which squeak above), rather than cripple the base class'es functionality.
I don't mean at all that the case is impossible that you need to cripple the base class. I just think that it is a much less common case compared to the case where you need all that's there in the base class and then some.
You can NEVER substitiute an obect of a derived class for an object of the base class.
Agree. Since I'm used to garbage-collected languages, where you never manipulate objects or pointers to them, but always use references, I use object, pointer to object, reference of object interchangeably - which is of course a complete messup for a C++ programmer. However, I meant pointers.
To summarize:
C++ has function overloading but you cannot overload between a base class and a derived class.
That's exactly my problem: why? Why did the language designers decide this? IMO, you often need/want to overload between a base class and a derived class, instead of re-defining/hiding away all the base class'es methods by simply adding a new, similarly-named method.
However, the language designers were probably much smarter than I am, so they must have had a reason. Can anybody tell me the reason?
Right. But more often I want to derive a WoodenDoor or an IronDoor, and besides inheriting the open() method, also add an open(int sqeakLevel). Instead of this happening automatically, I have to explicitly state this.
On the other hand, if I have written both the Door and the MagicDoor classes, and somebody else has written a Robot class, which has a method openDoor(Door* pDoor), and I pass it a MagicDoor*, it will open the MagicDoor without a spell - the compiler won't complain. What am I missing?
This is a problem with failing to separate the implementation from the interface. You get into this pickle by using public virtual functions. The virtual functions define the implementation, the "how I did it". The interface is the set of public member functions in the base class. None of these member functions should ever be virtual.
You are addressing that "all doors open". It's just that some doors open differently from others. Therefore, in your base class, you just have an Open() method: -
class Door
-
{
-
public:
-
void Open();
-
private:
-
virtual void DoOpen() = 0;
-
-
protected:
-
Door* theDoor;
-
-
};
-
-
-
void Door::Open()
-
{
-
theDoor->DoOpen();
-
}
-
When you create a specific kind of Door, you store the derived object pointer in the base object for later use. In the case of the MagicDoor, it will be the "this" pointer from the derived object. -
class MagicDoor : public Door
-
{
-
private:
-
void DoOpen(); //base class override
-
string theSpell;
-
public:
-
MagicDoor(string Spell);
-
};
-
-
MagicDoor::MagicDoor(string Spell) : theSpell(Spell)
-
{
-
theDoor = this;
-
}
-
-
void MagicDoor::DoOpen()
-
{
-
string data;
-
char buffer[256];
-
cin.getline(buffer, 245);
-
data = buffer;
-
-
if (data == theSpell)
-
{
-
cout << "The MagicDoor is open" << endl;
-
}
-
else
-
{
-
cout << "The MagicDoor is not open" << endl;
-
}
-
}
-
The main(): -
int main()
-
{
-
Door* d = new MagicDoor("Open Sesame");
-
d->Open();
-
}
-
and the door will open only when you enter the magic spell. Check out the Design Patterns book and look up the Template Method pattern.
Which leads to:
That's exactly my problem: why? Why did the language designers decide this? IMO, you often need/want to overload between a base class and a derived class, instead of re-defining/hiding away all the base class'es methods by simply adding a new, similarly-named method.
However, the language designers were probably much smarter than I am, so they must have had a reason. Can anybody tell me the reason?
The reason is function overloading vs domination. Overloading requires the same function name but with different function arguments. This is an overload: -
//the functions are at the same scope level
-
void function();
-
void function(int);
-
This is not an overload: -
//functions are at different scope levels
-
void Door::Open();
-
void MagicDoor::Open(string spell);
-
because it's the same as: -
float data;
-
-
void function()
-
{
-
string data;
-
-
//No way to get to the float here.
-
}
-
-
where the local variable name dominates the global variable name. You need scope resolution to get around this: -
float data;
-
-
void function()
-
{
-
string data;
-
-
::data = 2.5f; //the global float
-
}
-
-
The same rule applies to your member functions: The derived class method name dominates the base class method name. To use the base method in the derived class you use the scope resolution operator on the call.
This is a problem with failing to separate the implementation from the interface. You get into this pickle by using public virtual functions. The virtual functions define the implementation, the "how I did it". The interface is the set of public member functions in the base class. None of these member functions should ever be virtual.
You are addressing that "all doors open". It's just that some doors open differently from others. Therefore, in your base class, you just have an Open() method: -
class Door
-
{
-
public:
-
void Open();
-
private:
-
virtual void DoOpen() = 0;
-
-
protected:
-
Door* theDoor;
-
-
};
-
-
-
void Door::Open()
-
{
-
theDoor->DoOpen();
-
}
-
When you create a specific kind of Door, you store the derived object pointer in the base object for later use. In the case of the MagicDoor, it will be the "this" pointer from the derived object. -
class MagicDoor : public Door
-
{
-
private:
-
void DoOpen(); //base class override
-
string theSpell;
-
public:
-
MagicDoor(string Spell);
-
};
-
-
MagicDoor::MagicDoor(string Spell) : theSpell(Spell)
-
{
-
theDoor = this;
-
}
-
-
void MagicDoor::DoOpen()
-
{
-
string data;
-
char buffer[256];
-
cin.getline(buffer, 245);
-
data = buffer;
-
-
if (data == theSpell)
-
{
-
cout << "The MagicDoor is open" << endl;
-
}
-
else
-
{
-
cout << "The MagicDoor is not open" << endl;
-
}
-
}
-
The main(): -
int main()
-
{
-
Door* d = new MagicDoor("Open Sesame");
-
d->Open();
-
}
-
and the door will open only when you enter the magic spell. Check out the Design Patterns book and look up the Template Method pattern.
Interesting, I probably would have put the open command on the operator. I’ve not heard of the “Hollywood” principal. Sort of like this: -
// door
-
class Door {
-
public:
-
// Used by anyone
-
virtual bool OpenDoor();
-
// Used by magicians
-
virtual bool CastSpell(int magicPoints, string const & magicWords);
-
// Used by thieves
-
virtual bool PickLock(int dexterityPoints);
-
};
-
-
-
// user
-
class User {
-
public:
-
virtual OpenDoor(Door& doorToOpen) = 0;
-
};
-
-
// robot
-
class Robot : public user {
-
public:
-
virtual OpenDoor(Door& doorToOpen) {
-
if (OpenDoor()) {
-
cout << “Door opened.” << endl;
-
}
-
else {
-
cout << “Door locked.” << endl;
-
}
-
}
-
};
-
-
// magician
-
class Magician {
-
enum spells_e {
-
//...
-
openMagicDoorSpell,
-
//...
-
};
-
std::map<spells_e, string> spells;
-
-
int magicPoints; // could be part of user class
-
public:
-
virtual OpenDoor(Door& doorToOpen) {
-
if (doorToOpen.OpenDoor()) {
-
cout << “Door opened.” << endl;
-
}
-
else if(doorToOpen.CastSpell(magicPoints, spells[openMagicDoorSpell])
-
&& doorToOpen.OpenDoor()) {
-
cout << “Door opened.” << endl;
-
}
-
else {
-
cout << “Door locked.” << endl;
-
}
-
}
-
};
-
This is of course missing a lot, and I’m not sure if I would have the magician automagicly (if you forgive the pun ;)) open the door if it can’t be open by regular means.
A minor issue with this is that the CastSpell() function is available to all. The programmer must restrain themselves from calling it. A failing in the language IMHO; should restrict some functionality based on the calling object. Eiffel has this.
Adrian
-
// door
-
class Door {
-
public:
-
// Used by anyone
-
virtual bool OpenDoor();
-
// Used by magicians
-
virtual bool CastSpell(int magicPoints, string const & magicWords);
-
// Used by thieves
-
virtual bool PickLock(int dexterityPoints);
-
};
-
The problem here is that the interface of the Door is not usable by all derived classes. What you have are various methods of opening a Door. The weakness is you have to change this class every time a new way to open a door appears. This is not good when you have a large installed base.
The Door can only Open() because that is the published interface and it must be independent from all derived classes and usable by all derived classes.
I added code for your LockingDoor that has a lock picked by dexterity points. -
/////////////////////////////////////////////////////////
-
-
//Adrian's Door needs a thief to pick the lock
-
class Thief
-
{
-
public:
-
void SetDexterityPoints(int arg);
-
int GetDexterityPoints();
-
private:
-
int DexterityPoints;
-
};
-
void Thief::SetDexterityPoints(int arg)
-
{
-
this->DexterityPoints = arg;
-
}
-
int Thief::GetDexterityPoints()
-
{
-
return this->DexterityPoints;
-
}
-
-
class LockingDoor : public Door
-
{
-
private:
-
void DoOpen(); //base class override
-
Thief* t;
-
public:
-
LockingDoor(Thief* t); //the lockpicker
-
-
};
-
-
LockingDoor::LockingDoor(Thief* t) : t(t)
-
{
-
theDoor = this;
-
}
-
void LockingDoor::DoOpen()
-
{
-
int rval = this->t->GetDexterityPoints();
-
if ( rval < 10)
-
{
-
cout << "Not enough to open the door" << endl;
-
}
-
else
-
{
-
cout << "Door lock has been picked" << endl;
-
}
-
}
-
-
int main()
-
{
-
Door* d = new MagicDoor("Open Sesame");
-
d->Open();
-
Thief Barabbas;
-
Barabbas.SetDexterityPoints(5);
-
Door* d1 = new LockingDoor(&Barabbas);
-
d1->Open();
-
}
-
If you need to actually use d1 as a LockingDoor* to call added methods on LockingDoor that are not on Door, you need to design for the Visitor design pattern.
Visitor allows you to use a derived object as a derived object when all you have is a base class pointer to the object. And of course, in C++, there is no casting allowed.
Hmmm...Visitor...this may be the signal for my next article. What do you think??
Oops, forgot to make the Magician derived from User.
I still don’t think that I agree that virtual functions should not be made public, especially when you may want to use a set of objects like a common base type. What reference do you quote from? I’m just taking from my design course from university and how I’ve applied it since, but I dunno, maybe things have changed in the last 7 years.
The problem here is that the interface of the Door is not usable by all derived classes. What you have are various methods of opening a Door. The weakness is you have to change this class every time a new way to open a door appears. This is not good when you have a large installed base.
The Door can only Open() because that is the published interface and it must be independent from all derived classes and usable by all derived classes.
Not exactly true, it doesn’t. Casting a spell doesn’t open the door, it unlocks it. To open the door, you open the door.
And contrary to your comment about it being bad on a large installation base, at least the methods are centralised with the door. Operations on an object should be done by the object. I don’t think I would want to have a User unlock a door in any way at all (though I think I might consider having a lock class). In a game, this may not be critical, but in an OS, you defiantly don’t want things unlocked by just anyone. ;) But like I said, I didn’t put a lot of thought in to this, so there could definitely be holes in it.
I added code for your LockingDoor that has a lock picked by dexterity points. -
/////////////////////////////////////////////////////////
-
-
//Adrian's Door needs a thief to pick the lock
-
class Thief
-
{
-
public:
-
void SetDexterityPoints(int arg);
-
int GetDexterityPoints();
-
private:
-
int DexterityPoints;
-
};
-
void Thief::SetDexterityPoints(int arg)
-
{
-
this->DexterityPoints = arg;
-
}
-
int Thief::GetDexterityPoints()
-
{
-
return this->DexterityPoints;
-
}
-
-
class LockingDoor : public Door
-
{
-
private:
-
void DoOpen(); //base class override
-
Thief* t;
-
public:
-
LockingDoor(Thief* t); //the lockpicker
-
-
};
-
-
LockingDoor::LockingDoor(Thief* t) : t(t)
-
{
-
theDoor = this;
-
}
-
void LockingDoor::DoOpen()
-
{
-
int rval = this->t->GetDexterityPoints();
-
if ( rval < 10)
-
{
-
cout << "Not enough to open the door" << endl;
-
}
-
else
-
{
-
cout << "Door lock has been picked" << endl;
-
}
-
}
-
-
int main()
-
{
-
Door* d = new MagicDoor("Open Sesame");
-
d->Open();
-
Thief Barabbas;
-
Barabbas.SetDexterityPoints(5);
-
Door* d1 = new LockingDoor(&Barabbas);
-
d1->Open();
-
}
-
I don’t agree with this design because - A door doesn’t have a thief. It knows how to open and perhaps how difficult it is to lock/unlock itself, but...
- A User does operate on the door.
The User can do different operations on it too. Maybe try and break it. If the door is weakened enough, perhaps it will collapse. Because there is a finite number of general things you can do to the door (open, unlock, break, cast spell, hmmm, not sure what else), I think (IMHO) that it is better that the user does it to the door, not the door opens based on the Thief.
Adding to that, it would require that you have an open for each User, thus complicating the door, requiring you to add more pointers to your class as you have defined it. I think that is along similar lines you had against my suggestion (which I don’t exactly agree with) but it is the reason for why I setup the class structure as I did.
I don’t necessarily like the design you suggested, but there are always compromises in design. You just try and get the best that you can. It is usually good to seek second opinions.
If you need to actually use d1 as a LockingDoor* to call added methods on LockingDoor that are not on Door, you need to design for the Visitor design pattern.
Visitor allows you to use a derived object as a derived object when all you have is a base class pointer to the object. And of course, in C++, there is no casting allowed.
Hmmm...Visitor...this may be the signal for my next article. What do you think??
Actually, if I understand it correctly, the Visitor Pattern is what I was suggesting. Also, doesn’t it require that virtual functions are made public (at least in the base class, though if it isn’t public in the derived class, it doesn’t make much sense unless you inherit privately)?
I would be interested in reading your article.
Adrian
:o(
Still no answer to my initial question, or I am stupid.
To repeat myself, if you have: - class C1
-
{
-
public:
-
void f1()
-
}
-
-
class C2: public C1
-
{
-
public:
-
void f1(int);
-
}
then:
won't compile. This is normal, and happens because in C++ each class defines its own visibility range, hence same-named methods in derived classes hide away methods in the base class. You need to either unhide the base class methods using the "using <base class>::<base class method name>" syntax, or call them explicitly, using the scope resolution operator - c.C1::f1() in the above example.
However, overloading inside a class happens based on the requirement that no two methods have the same signature. I.e. if I have: - class C1
-
{
-
public:
-
void f1()
-
void f1(int);
-
}
-
-
class C2: public C1
-
{
-
public:
-
// nothing
-
}
then:
will compile happily. This is so because for the compiler void f1(int) and void f1() are different signatures. What you're trying to convince me is that the compiler suddenly becomes stupid when you place a similarly named method with a different signature in a derived class, and is not able to distinguish void f1(int) and void f1() anymore, and therefore hides the base class'es f1 from derived classes.
Evidently, this is what happens. But my curiosity is not what happens, how to name or describe what happens, or how to work around what happens. My curiosity is why the designers of the language decided that it should happen the way it does. I.e. why do I get to inherit without complicacies only methods which I don't want to overload in the derived class.
The more I think of it, the more upside down it seems to me.
:o(
Still no answer to my initial question, or I am stupid.
To repeat myself, if you have: - class C1
-
{
-
public:
-
void f1()
-
}
-
-
class C2: public C1
-
{
-
public:
-
void f1(int);
-
}
then:
won't compile. This is normal, and happens because in C++ each class defines its own visibility range, hence same-named methods in derived classes hide away methods in the base class. You need to either unhide the base class methods using the "using <base class>::<base class method name>" syntax, or call them explicitly, using the scope resolution operator - c.C1::f1() in the above example.
However, overloading inside a class happens based on the requirement that no two methods have the same signature. I.e. if I have: - class C1
-
{
-
public:
-
void f1()
-
void f1(int);
-
}
-
-
class C2: public C1
-
{
-
public:
-
// nothing
-
}
then:
will compile happily. This is so because for the compiler void f1(int) and void f1() are different signatures. What you're trying to convince me is that the compiler suddenly becomes stupid when you place a similarly named method with a different signature in a derived class, and is not able to distinguish void f1(int) and void f1() anymore, and therefore hides the base class'es f1 from derived classes.
Evidently, this is what happens. But my curiosity is not what happens, how to name or describe what happens, or how to work around what happens. My curiosity is why the designers of the language decided that it should happen the way it does. I.e. why do I get to inherit without complicacies only methods which I don't want to overload in the derived class.
The more I think of it, the more upside down it seems to me.
See this thread.
Adrian
See this thread.
Adrian
Finally! Thanks a lot.
To summarize my understanding of the problem: due to implicit conversions provided by C++, if this hiding mechanism wasn't in place, small changes in a base class, of which the compiler won't even warn, can determine changes in the behavior of derived classes. Am I right?
Finally! Thanks a lot.
To summarize my understanding of the problem: due to implicit conversions provided by C++, if this hiding mechanism wasn't in place, small changes in a base class, of which the compiler won't even warn, can determine changes in the behavior of derived classes. Am I right?
Yes. Happy coding.
Adrian
This quote:
I still don’t think that I agree that virtual functions should not be made public, especially when you may want to use a set of objects like a common base type. What reference do you quote from? I’m just taking from my design course from university and how I’ve applied it since, but I dunno, maybe things have changed in the last 7 years.
The interface in the base class is public. But none of these functions are virtual. The base class public interface functions call private virtual base class functions. It is these private virtual functions that the derived class overrides.
The problem with public virtual functions in the base class is that these are overriden by the derived class and this also overrides the base class functionality.
Thaat forces you in the derived class to code: -
void Derived::Method()
-
{
-
Base::Method(); //recover base class code
-
-
//put derived class behavior here.
-
}
-
Not all base class functions are pure virtual functions. If they are not, and you override, you lose the code on the base class.
Worse, you can't tell of the call above to Base::Method() should be done before or after your derived class code. Now there is ambiguity. Not good.
Often, base class virtual functions contain code that is common to the derived classes or is used as default behavior if the derived class chooses not to override.
That said, almost all textbooks use public virtual base class methods. The reason: Well, that's how you are supposed to do it. However, after 15 years of using these things, it turns out the public virtual is a bad deal.
Look at it this way: The base class provides the inreface. By overriding the base class funcitons, you are overridieng the published interface and along with it the published interface behavior. Remember, once published, an interface cannot be changed.
I googled "separate implementation from interface" and got 210 hits. There's some reading.
And this:
Adding to that, it would require that you have an open for each User, thus complicating the door, requiring you to add more pointers to your class as you have defined it. I think that is along similar lines you had against my suggestion (which I don’t exactly agree with) but it is the reason for why I setup the class structure as I did.
Not true. If the base class has an Open() that calls a private base DoOpen(). There is no reason why every user needs to override this method: -
class Base
-
{
-
public:
-
enum Rcodes {NOT_SUPPORTED, OK, etc...}
-
void Open();
-
private:
-
virtual Rcodes DoOpen();
-
};
-
-
Rcodes Base::DoOpen()
-
{
-
return NOT_SUPPORTED;
-
}
-
If the derived class has no DoOpen(), then the base class behavior prevails and the user is returned NOT_SUPPORTED.
If you overide a base public virtual function tyou would need to put this code in every class where Open() is not supported. Now you have bloat and you require that all derived classes have the same method names.
In real life, rarely do derived classes have the same methods as their base classes. They usually have more methods but those methods are outside the hierarchy interface. To get at them using a base pointer to a derived object requires a Visitor. A Visitor allows you to access these methods and even to appear to add methods to a class without having to chnage the class.
The above example is called a hook. Check out the book Design Patterns by Erich Fromm, et al Addison-Wesley 1994 and look up the Template Method pattern for a discussion of hooks. Also, look up Visitor.
Now to the student:
class C1
{
public:
void f1()
};
class C2: public C1
{
public:
void f1(int);
};
C2 c;
c.f1() <---won't compile
To Aflj: You did not read (or did not understand ) my earlier posts. c.f1() cannot compile becuse there is no method in the class C2 that is f1(). The C2 method is f1(int).
The C2::f1(int) hides the base class function. You do inherit C1::f1() but to call it you have to get around the C2::f2(int). So just use an explicit call: Sign in to post your reply or Sign up for a free account.
Similar topics
by: Fahd Khan |
last post by:
Hi team! While troubleshooting a crash I had while using BitTorrent
where the torrent's target file names didn't fall into the ascii range
I was playing around in the interpreter and noticed this...
|
by: Amit |
last post by:
I am facing a problem while using SQL Server with VB application.
Implicit conversion from datatype text to nvarchar is not allowed.
Use the convert function to run this query.
When i see the...
|
by: StressPuppy |
last post by:
(posted this in VB group but then realized I probably should have posted here)
I have a TabControl with several TabPages. Upon startup, I only want to
show the first TabPage, hiding the rest....
|
by: Jochen Luebbers |
last post by:
Hi,
I've a problem:
I compile modules of embedded SQL (for a dynamic lib) on multiple
platforms (AIX,Linux,Windows). They are compiled from the same
source code. To be able to provide all...
|
by: Chris Dunaway |
last post by:
The C# 3.0 spec (http://msdn.microsoft.com/vcsharp/future/) contains a
feature called "Implicitly typed local variables".
The type of the variable is determined at compile time based on the...
|
by: Alex |
last post by:
Hello all,
I have a main form(say "form1") .i want to display another form(say
"form2") on occuring of an event (say a button click) and want to hide
it after some time so that it will again...
|
by: jaime |
last post by:
Hi all.
Apologies, since this is more a tool question, than strictly a language
question, but hey, it seemed like an appropriate place to ask...
I'm a c newbie (and have been now for about 6...
|
by: utab |
last post by:
Dear all,
I was trying to write a more complex program, and while searching for
sth in my reference C++ primer, by Lippman. I had a question in the
mind, see the code below
#include <string>...
|
by: Bill Cunningham |
last post by:
I am stumped on this one. I tried two methods and something just doesn't
seem right. I'll try my new syle.
#include <stdio.h>
#include <stdlib.h>
main() {
srand(2000); /*seed unsigned */...
|
by: Sh4wn |
last post by:
Hi,
first, python is one of my fav languages, and i'll definitely keep
developing with it. But, there's 1 one thing what I -really- miss:
data hiding. I know member vars are private when you...
|
by: taylorcarr |
last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
|
by: aa123db |
last post by:
Variable and constants
Use var or let for variables and const fror constants.
Var foo ='bar';
Let foo ='bar';const baz ='bar';
Functions
function $name$ ($parameters$) {
}
...
|
by: ryjfgjl |
last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
|
by: emmanuelkatto |
last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud.
Please let me know.
Thanks!
Emmanuel
|
by: BarryA |
last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
|
by: Sonnysonu |
last post by:
This is the data of csv file
1 2 3
1 2 3
1 2 3
1 2 3
2 3
2 3
3
the lengths should be different i have to store the data by column-wise with in the specific length.
suppose the i have to...
|
by: Hystou |
last post by:
There are some requirements for setting up RAID:
1. The motherboard and BIOS support RAID configuration.
2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
|
by: Hystou |
last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers,...
| |