Overloading method in base class, with member vari

2019-05-06 15:25发布

I have a Class structure as follows:

class Base {
    public:
        void setDefault( uint8_t my_default ) { m_default = my_default; }

        void method( uint8_t * subject ) { method( subject, m_default ); }
        virtual void method( uint8_t * subject, uint8_t parameter ) =0;

    protected:
        uint8_t m_default;
};

class Derived1 : public Base {
    public:
        void method ( uint8_t * subject, uint8_t parameter ) { /* do something to subject */ }
};

class Derived2 : public Base {
    public:  
        void method ( uint8_t * subject, uint8_t parameter ) { /* do something different to subject */ }
};

I want to be able to call method( ... ) on any instance of a class derived from Base, which will call the method defined in the derived class using the member variable as the default parameter.

From what I've read elsewhere on Stack Overflow here, the override must have the same signature, which is why it won't compile.

Are there any potential ambiguities with this approach?

Two ways I can think to get around this:

  1. declare a default method( void ) for each derived class, but this doesn't seem very DRY
  2. use a different name for the default method (e.g. defaultMethod( uint8_t * subject )) but I feel this makes my class less intuitive

Is there a better way around this?


Here is a complete example, which won't compile (Arduino IDE 1.7.9):

class Base {
    public:
        void setDefault( uint8_t my_default ) { m_default = my_default; }

        void method( uint8_t * subject ) { method( subject, m_default ); }
        virtual void method( uint8_t * subject, uint8_t parameter ) =0;

    protected:
        uint8_t m_default;
};

class Derived1 : public Base {
    public:
      void method ( uint8_t * subject, uint8_t parameter ) { *subject += parameter; }
};

class Derived2 : public Base {
    public:  
      void method ( uint8_t * subject, uint8_t parameter ) { *subject *= parameter; }
};

Derived1 derived1;
Derived2 derived2;

uint8_t subject = 0;

void setup() {
  // put your setup code here, to run once:
  derived1.setDefault( 3 );
  derived2.setDefault( 5 );
}

void loop() {
  // put your main code here, to run repeatedly:
  derived1.method( &subject, 1 );  // subject == 1  
  derived2.method( &subject, 2 );  // subject == 2  

  // won't compile with this line:
  derived1.method( &subject );  // subject == 5
}

Error produced is:

over-ride-load.ino: In function 'void loop()':
over-ride-load.ino:39:29: error: no matching function for call to 'Derived1::method(uint8_t*)'
over-ride-load.ino:39:29: note: candidate is:
over-ride-load.ino:14:12: note: virtual void Derived1::method(uint8_t*, uint8_t)
over-ride-load.ino:14:12: note:   candidate expects 2 arguments, 1 provided
Error compiling.

2条回答
老娘就宠你
2楼-- · 2019-05-06 15:48

What I believe that you're looking for is the using directive.

You've done everything correctly in Base. While using a default parameter is (often) better than defining a second function, given your requirements (using a member) that's not possible here: so you defined a second overloaded function to fix it (and defined it inline - kudos!).

But the problem comes with the derived classes. If you didn't override the virtual function, everything would have been fine: both versions of method would be available to you. But you have to override it, so you effectively "mask" the base version of method(subject); with method(subject,parameter);.

What you want to do is "promote" all of Base's methods into the various Deriveds, to give them equal weighting. How? With the using directive.

In each Derived definition, put the following code:

using Base::method;

That "promotes" all of Base's method into the Derived - while still allowing you to override individual ones. I suggest that you put that line directly above each Derived's method() override.

查看更多
趁早两清
3楼-- · 2019-05-06 15:52

From what I've read elsewhere on Stack Overflow here, the override must have the same signature, which is why it won't compile.

It will compile (if you fix the syntax errors). It will compile because the override does have the same signature:

virtual void method (uint8_t * subject, uint8_t parameter ) =0;

versus

        void method( uint8_t * subject, uint8_t parameter )

On the other hand, option 2. (different name in favour of overload) still sounds appealing. Overloads can sometimes be tricky, confusing and counter intuitive.


However, even though your example is correct, what you might find is that following does not work, which might be counter intuitive:

uint8_t b;
Derived2 d2;
d2.method(&b);

This is because of how overload resolution works. Your suggested option 2. trivially resolves this issue, and that is why I'm recommending you to do that. Other ways to call the method in parent:

  • Call d2.Base::method(&b);
  • Only call the single argument method through a base object pointer/reference.
  • Add using Base::method; declaration to each derived class. This is described more in depth in John Burger's answer.
查看更多
登录 后发表回答