samedi 19 avril 2014

c ++ - appelant identiquement nommé méthodes dans des classes de base - Stack Overflow


Base class A has a subclass B, and B has a subclass C. A implements a virtual method doStuff(), B does not, and C does. From C, I want to call A's implementation of doStuff() (I do this from within C's implementation of doStuff() but that shouldn't really matter.) Should I call:


A::doStuff();

Or:


B::doStuff();

The first seems more clear because it refers to the actual implementation. On the other hand, the second might be more useful if I later decide that B needs to doStuff() differently than A. Which is more standard? Which is more dangerous?


I was a little surprised that B::doStuff() didn't trigger any warnings, but I suppose that by definition B has an implementation even if it's from the base class. Can the chain of base classes and implementations be arbitrarily long and complicated? For example, if I had A through Z, each a subclass of the previous one, could I have implementations anywhere in the chain, and does calling any class's method just go 'up the chain' until it finds an implementation?




First, in regards to the last paragraph, yes, you're absolutely right.


Next up, for the main question:


It's a matter of logic which should be treated on a case-to-case basis. You're the only one who can decide whether you want to call the logic from B or from A, even if B::doStuff() isn't yet implemented. There's no right answer.


However, you should look into the template method pattern. My guess is that if you want C to call something from A and later you decide that you want D (sibling of C) to do the same thing, you're duplicating code. So I'd take a different approach:


class A
{
private:
void doAStuff(); //doesn't have to be here
public:
virtual void doStuff()
{
doAStuff();
}
void doAStuffAndStuff();
{
doAStuff();
doStuff();
}
}

class B : A
{
public:
virtual void doStuff(); //or not
}

class C : B
{
public:
virtual void doStuff();
}

IMO this is more intuitive. Say I have an A* a, I don't know the dynamic type of a:



  • if I want to call doStuff() with the logic in the most derived class, I call doStuff().

  • if I want to call doStuff() with the logic in the most derived class, but I also want the logic from A, I call doAStuffAndStuff().


I'm stating this approach as an alternative. I'm not saying it necessarily applies to your case, but it might.




Call B::doStuff. Even better, don't use designs which require to call methods of base classes explicitely, if you can. If you must, simulate base.doStuff by some macro or alias construct in order to prevent from exactly that kind of errors. You can define an alias (and ensure its presence at compile time) BaseType at class and use BaseType::doStuff syntax.



I was a little surprised that B::doStuff() didn't trigger any warnings, but I suppose that by definition B has an implementation even if it's from the base class



There is nothing to warn. It is in your case a legal scenario.



Can the chain of base classes and implementations be arbitrarily long and complicated



Yes it can, but it should not. Designs which require long inheritance chains plus contain some kind of hidden rules what methods of what base classes should be called are usually broken.



Base class A has a subclass B, and B has a subclass C. A implements a virtual method doStuff(), B does not, and C does. From C, I want to call A's implementation of doStuff() (I do this from within C's implementation of doStuff() but that shouldn't really matter.) Should I call:


A::doStuff();

Or:


B::doStuff();

The first seems more clear because it refers to the actual implementation. On the other hand, the second might be more useful if I later decide that B needs to doStuff() differently than A. Which is more standard? Which is more dangerous?


I was a little surprised that B::doStuff() didn't trigger any warnings, but I suppose that by definition B has an implementation even if it's from the base class. Can the chain of base classes and implementations be arbitrarily long and complicated? For example, if I had A through Z, each a subclass of the previous one, could I have implementations anywhere in the chain, and does calling any class's method just go 'up the chain' until it finds an implementation?



First, in regards to the last paragraph, yes, you're absolutely right.


Next up, for the main question:


It's a matter of logic which should be treated on a case-to-case basis. You're the only one who can decide whether you want to call the logic from B or from A, even if B::doStuff() isn't yet implemented. There's no right answer.


However, you should look into the template method pattern. My guess is that if you want C to call something from A and later you decide that you want D (sibling of C) to do the same thing, you're duplicating code. So I'd take a different approach:


class A
{
private:
void doAStuff(); //doesn't have to be here
public:
virtual void doStuff()
{
doAStuff();
}
void doAStuffAndStuff();
{
doAStuff();
doStuff();
}
}

class B : A
{
public:
virtual void doStuff(); //or not
}

class C : B
{
public:
virtual void doStuff();
}

IMO this is more intuitive. Say I have an A* a, I don't know the dynamic type of a:



  • if I want to call doStuff() with the logic in the most derived class, I call doStuff().

  • if I want to call doStuff() with the logic in the most derived class, but I also want the logic from A, I call doAStuffAndStuff().


I'm stating this approach as an alternative. I'm not saying it necessarily applies to your case, but it might.



Call B::doStuff. Even better, don't use designs which require to call methods of base classes explicitely, if you can. If you must, simulate base.doStuff by some macro or alias construct in order to prevent from exactly that kind of errors. You can define an alias (and ensure its presence at compile time) BaseType at class and use BaseType::doStuff syntax.



I was a little surprised that B::doStuff() didn't trigger any warnings, but I suppose that by definition B has an implementation even if it's from the base class



There is nothing to warn. It is in your case a legal scenario.



Can the chain of base classes and implementations be arbitrarily long and complicated



Yes it can, but it should not. Designs which require long inheritance chains plus contain some kind of hidden rules what methods of what base classes should be called are usually broken.


0 commentaires:

Enregistrer un commentaire