【问题标题】:Overloading method in base class, with member variable as default基类重载方法,默认成员变量
【发布时间】:2016-06-13 13:33:18
【问题描述】:

我有一个类结构如下:

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

我希望能够在派生自Base 的类的任何实例上调用method( ... ),这将使用成员变量作为默认参数调用派生类中定义的方法。

从我在 Stack Overflow here 其他地方读到的内容来看,覆盖必须具有相同的签名,这就是它无法编译的原因。

这种方法是否存在任何潜在的歧义?

我能想到的两种方法来解决这个问题:

  1. 为每个派生类声明一个默认的method( void ),但这似乎不是很干
  2. 为默认方法使用不同的名称(例如defaultMethod( uint8_t * subject )),但我觉得这让我的课程不那么直观

有没有更好的解决方法?


这是一个完整的示例,无法编译(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
}

产生的错误是:

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.

【问题讨论】:

  • 如果你把拼写错误和保留关键字的使用放在一边(默认在 C++ > 11 中), il 可以正常工作。只需添加一个虚拟覆盖以使其更清晰,您就可以开始了。
  • @Davidbrcz - 谢谢,更正了示例中保留字的误用

标签: c++ overloading overriding


【解决方案1】:

我相信您正在寻找的是 using 指令。

您在Base 中已正确完成所有操作。虽然使用默认参数(通常)比定义第二个函数更好,但考虑到您的要求(使用成员),这在此处是不可能的:所以您定义了第二个重载函数来修复它(并定义了它inline - 赞!) .

但是问题来自于派生类。如果您没有重写虚函数,一切都会好起来的:method 的两个版本都可供您使用。但是你必须覆盖它,所以你有效地用method(subject,parameter);“掩盖”method(subject);的基本版本。

您想要做的是将所有Basemethods“提升”为各种Deriveds,以赋予它们同等的权重。如何?使用using 指令。

在每个Derived 定义中,放入以下代码:

using Base::method;

这会将Base 的所有方法“提升”到Derived - 同时仍然允许您覆盖个别的方法。我建议您将该行直接放在每个 Derivedmethod() 覆盖上方。

【讨论】:

  • 这行得通 - 我现在还不完全理解它,但我会继续阅读它。泰。
  • 这是用同名的派生定义“隐藏”基类定义的概念,以避免编译器在尝试使用派生类时抱怨太多选项。这在理论上是有效的——直到它碍事。这就是他们发明using的原因!
【解决方案2】:

从我在 Stack Overflow 其他地方读到的内容来看,覆盖必须具有相同的签名,这就是它无法编译的原因。

它将编译(如果您修复语法错误)。它将编译,因为覆盖 确实 具有相同的签名:

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

        void method( uint8_t * subject, uint8_t parameter )

另一方面,选项 2.(支持重载的不同名称)听起来仍然很吸引人。重载有时会很棘手、令人困惑并且违反直觉。


但是,即使您的示例是正确的,您可能会发现以下内容不起作用,这可能与直觉相反:

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

这是因为重载解析的工作原理。您建议的选项 2. 可以轻松解决此问题,这就是我建议您这样做的原因。其他调用父类方法的方式:

  • 致电d2.Base::method(&b);
  • 仅通过基础对象指针/引用调用单参数方法。
  • 为每个派生类添加using Base::method; 声明。这在 John Burger 的回答中有更深入的描述。

【讨论】:

  • 后半部分没用,抱歉我原来的问题没有说清楚。我选择了using Base::method;,因为它给了我最满意的解决方案,尽管它让我的代码看起来有点湿。泰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
  • 2021-12-29
相关资源
最近更新 更多