【问题标题】:C++ overloading method based on derived class基于派生类的C++重载方法
【发布时间】:2017-04-03 05:00:53
【问题描述】:

我的代码面临以下问题:

#include <iostream>

using namespace std;

class Base {
public:
    virtual void sayHello()=0;
};

class Impl1 : public Base {
public:
    void sayHello() { cout << "Hi from Impl1" << endl; }
};

class Impl2 : public Base {
public:
    void sayHello() { cout << "Hi from Impl2" << endl; }
};

void sayHello(Impl1 *i) {
    cout << "Impl1 says: ";
    i->sayHello();
}

void sayHello(Impl2 *i) {
    cout << "Impl2 says: ";
    i->sayHello();
}

int main()
{
    Impl1 *i1 = new Impl1();
    Base *b = i1;

    sayHello(b);

    return 0;
}

编译器在这里抱怨sayHello(b); 行 代码。

“重载 'sayHello(Base*&)' 的调用不明确”

有没有办法解决这个问题?

编辑:

我基本上想将我的对象传递给一个函数,该函数根据对象的类型进行一些计算。我的对象故意缺少信息以进行所需的计算。所以 Impl1 和 Impl2 只包含一些基本数据,不知道进行计算所需的更多数据。

【问题讨论】:

  • 您没有将 Base * 作为参数的 sayHello 函数。

标签: c++ overloading derived-class


【解决方案1】:

重载解析在编译时执行。这意味着对于sayHello(b);,编译器只知道b 的类型是Base*,它不会也不能知道b 实际上指向一个Impl1 对象。然后导致模棱两可的调用;将Base* 转换为Impl1*Impl2* 是呼叫的等效排名。

PS:可能是 OT,但对于您的代码示例,采用 Base* 的函数可以正常工作;动态调度将生效。

class Base {
public:
    virtual void sayHello()=0;
};

class Impl1 : public Base {
public:
    void sayHello() { cout << "Hi from Impl1" << endl; }
};

class Impl2 : public Base {
public:
    void sayHello() { cout << "Hi from Impl2" << endl; }
};

void sayHello(Base *i) {
    cout << "Some derived class of Base says: ";
    i->sayHello();
}

int main()
{
    Impl1 i1;
    Impl2 i2;

    Base *b = &i1;
    sayHello(b);   // "Hi from Impl1"
    b = &i2;
    sayHello(b);   // "Hi from Impl2"

    return 0;
}

如果您需要在运行时知道动态类型,可以使用dynamic_cast。例如

Base *b = /* something */;
Impl1 * pi1 = dynamic_cast<Impl1*>(b);
if (pi1 != nullptr) sayHello(pi1);

【讨论】:

  • 那么我怎样才能在运行时进行这个对话呢?我基本上想将我的对象传递给一个函数,该函数根据对象的类型进行一些计算。为了进行所需的计算,我的对象故意缺少信息。
  • @ZdravkoDonev 所以你需要知道函数内部的实际派生类型吗?你可能需要dynamic_cast
  • 所以你提出的是: if(dynamic_cast(b)) { } else if(dynamic_cast(b)) { } else { // “谁知道”输入 } 解决方案?
  • @ZdravkoDonev 你可以这样做,但这不是一个好主意。我的答案中显示的第一个代码示例会更好;不依赖于实际的派生类型,依赖于接口(即Base 类)。
  • 是的,但是我的派生类不包含进行计算所需的数据,我不想传递不同类的对象,因为我会得到一个意大利面条代码。这种情况我该怎么办?
【解决方案2】:

去掉两个独立的函数,直接调用b-&gt;sayHello();

Impl1 *i1 = new Impl1();
Base *b = i1;
b->sayHello();

或者使用dynamic_cast 使用丑陋的解决方法:

Impl1 *i1 = new Impl1();
Base *b = i1;
sayHello(dynamic_cast<Impl1*>(b));

诉诸dynamic_cast 的需要通常表明类设计存在错误。这里很可能就是这种情况。很有可能你一开始就不应该引入所谓的面向对象的基类。


还要注意,最后不要调用delete。如果这样做,您将需要在Base 中使用虚拟析构函数。

【讨论】:

  • 我基本上想将我的对象传递给一个函数,该函数根据对象的类型进行一些计算。为了进行所需的计算,我的对象故意缺少信息。
  • @ZdravkoDonev:我添加了另一种解决方法。
  • 是的,我想到了if-else if 和dynamic_cast,但这似乎不是一个好主意。也许你对引入基类的问题是正确的。但在我的代码的某些部分,我需要将我的实现视为一个基类,而在其他部分我需要区分它们。
【解决方案3】:

由于重载是在编译时解析的,因此您必须为编译器提供确切的类型才能使重载解析成功。

为了在类型上进行调度,给Base添加一个虚成员函数,并用它来选择重载:

class Base {
public:
    virtual void sayHello()=0;
    virtual void callSayHello() = 0;
};

class Impl1 : public Base {
public:
    void sayHello() { cout << "Hi from Impl1" << endl; }
    void callSayHello() {sayHello(this); }
};

class Impl2 : public Base {
public:
    void sayHello() { cout << "Hi from Impl2" << endl; }
    void callSayHello() {sayHello(this); }
};

void sayHello(Impl1 *i) {
    cout << "Impl1 says: ";
    i->sayHello();
}

void sayHello(Impl2 *i) {
    cout << "Impl2 says: ";
    i->sayHello();
}
...
b->callSayHello();

请注意callSayHello 的实现是相同的,但您不能将它们放入Base 类中,因为this 的类型会有所不同。

注意:这个实现的想法是从Visitor Pattern的C++实现中借来的。

【讨论】:

    猜你喜欢
    • 2014-10-17
    • 1970-01-01
    • 2013-05-18
    • 2011-09-05
    • 1970-01-01
    • 2014-12-23
    • 2012-11-10
    • 1970-01-01
    相关资源
    最近更新 更多