在 c++ 中类继承的情况下强制延迟方法解析

Forcing late method resolution in case of class inheritance in c++

本文关键字:延迟 方法 情况下 c++ 继承      更新时间:2023-10-16

考虑以下类结构:-

    class foo {
    public:
      int fun () {
        cout << "in foo" << endl;
      }
    };
    class bar_class1:public foo {
    public:
      int fun () {
        cout << "in bar_class1" << endl;
      }
    };
    class bar_class2:public foo {
    public:
      float fun () {
        cout << "in bar_class2" << endl;
      }
    };
    main () {
      foo * foo_pointer = new bar_class1();
      foo_pointer->fun();
    }

上述程序的输出在 foo 中。有没有办法,使用实际上指向 bar_class1bar_class2 类型的对象的类型 foo * 的指针,我们可以调用派生类的 fun 函数而不是基类?我无法在基类foo中使fun函数成为虚拟函数,因此派生类bar_class2中的函数foo存在返回类型冲突。

这是我的评论作为答案。

你不能那样做。

如果这种多态性是可能的,那么当代码在实际类型为 bar_class2 的对象上调用 foo::fun(期望一个 int)并因此获得浮点数时,这不会严重中断吗?你想简单地扔掉类型安全吗?

如果你想要不同的返回类型,听起来你想要一个模板。但是你不能完全按照你想使用 foo() 的方式使用模板。静态多态性(模板)和运行时多态性(后期绑定)不能很好地混合。你需要重新设计你的 oop 结构。

如果你绝对讨厌类型安全,你可以用 void 指针来做到这一点。 但是为了对飞行意大利面怪物的热爱,永远不要在 c++ 中这样做。请在阅读以下代码之前闭上眼睛以避免暴露。

#include <iostream>
class foo {
public:
    virtual void* fun() = 0;
    virtual ~foo(){};
};
class bar_class1: public foo {
public:
    void* fun() {
        return &value;
    }
private:
    int value = 1;
};
class bar_class2: public foo {
public:
    void* fun() {
        return &value;
    }
private:
    float value = 1.1;
};
int main() {
    foo* foo_pointer1 = new bar_class1();
    foo* foo_pointer2 = new bar_class2();
    // in c++ compiler must know the type of all objects during compilation
    std::cout << *reinterpret_cast<int*>(foo_pointer1->fun()) << 'n';
    std::cout << *reinterpret_cast<float*>(foo_pointer2->fun()) << 'n';
    delete foo_pointer1;
    delete foo_pointer2;
}

也许与现有的答案类似,我真的希望你意识到改变你的设计比这种混乱更好,但我相信这是你会得到的最好的。我强迫您在调用站点指定返回类型(例如,someFoo->fun<int>()),因为无论如何您都必须知道它,并基于此进行调度。任何有趣的生意,你都会得到一个例外。还要记住,我想,这种性能是不太理想的。

#include <cassert>
#include <stdexcept> 
#include <type_traits> 
struct foo {       
    virtual ~foo() = default;
    template<typename T, typename = typename std::enable_if<std::is_same<T, int>::value>::type, typename = void>
    T fun();
    template<typename T, typename = typename std::enable_if<std::is_same<T, float>::value>::type>
    T fun();
};
struct bar_class1 : foo {
    int fun() {
        return 2;
    }
};
struct bar_class2 : foo {
    float fun() {
        return 3.5f;
    }
};
template<typename T, typename, typename Dummy>
T foo::fun() {
    if (auto *p = dynamic_cast<bar_class1 *>(this)) {
        return p->fun();
    } else if (dynamic_cast<bar_class2 *>(this)) {
        throw std::invalid_argument("Mismatching dynamic type.");
    } else {
        return 1;
    }
}
template<typename T, typename>
T foo::fun() {
    auto *p = dynamic_cast<bar_class2 *>(this);
    if (dynamic_cast<bar_class1 *>(this) || !p) {
        throw std::invalid_argument("Mismatching dynamic type.");
    } else if (auto *p = dynamic_cast<bar_class2 *>(this)) {
        return p->fun();
    }
    assert(false); //should never get here, but compiler doesn't know that
}

如果你想要 main 函数,我已经写了一个完整的示例。

回答您的问题:不,如果不决定返回类型,就不可能进行后期绑定。至少不是以合理的方式,请参阅 User2079303 的精彩反例。但。。。

例如,您可以使用关键字 virtual 将代码更改为如下所示的内容,并将 Instance 的返回类型均衡为 void

class foo 
{
public:
    virtual void fun(std::ostream& out) {
        out << "in foo" << std::endl;
    }
};

因此,您可以稍后决定输出类型:

class intFoo: public foo 
{
public:
    void fun(std::ostream& out)  {
        // output an int
        out << "in bar_class1. data: " << data << endl;
    }
    int data;
};
class floatFoo: public foo 
{
public:
    void fun(std::ostream& out)  {
        // output a float 
        out << "in bar_class2. data: " << data << endl;
    }
    float data;
};

为简洁起见,我双重使用输出流(现在是函数fun()的参数)来演示派生类的类型相关部分。在应用程序中,参数可能是另一种更有用的类型。

函数fun不是虚拟函数,因为你没有使用关键字"virtual"来装饰它。因此,编译将确定在编译时调用哪个函数。因此,没有办法告诉编译器调用另一个函数,因为编译器将使用其静态类型,即变量定义类型 -- foo *。