运行时数据类型多态性

Fat*_*eep 1 c++ polymorphism inheritance templates c++17

我在C++中提出了运行时多态的解决方案.

#include <iostream>
using namespace std;

class base {
    public:
    virtual void call(double xx) {
        cout << "DERIVED: " << xx << endl;
    }
};

template<typename T>
class derivedT : base {
    public:
    virtual void call(double xx) {
        cout << "DERIVED_T: " << ((T) xx) << endl;
    }
};

int main() {
    base* sample = nullptr;

    cout << "CHOOSE TYPE: (BYTE = 1, UINT = 2, DOUBLE = 3)" << endl;
    uint8_t type = cin.get();
    type -= 48;

    switch (type) {
        case 1:
            sample = (base*) new derivedT<uint8_t>();
        break;
        case 2:
            sample = (base*) new derivedT<uint32_t>();
        break;
        case 3:
            sample = (base*) new derivedT<double_t>();
        break;
    }

    sample->call(2567.45);
    cin.get();
    cin.get();
}
Run Code Online (Sandbox Code Playgroud)

这里的想法是编译器将在switch语句中的运行时生成所有模板化类型.模板在编译时是已知的,所以现在我们可以从基类中调用在派生类中被覆盖的虚方法.

这有效的唯一原因是因为每个类的函数具有相同的参数.如果我们在使用T的派生类中使用参数,那么它将无法工作.因此,我们将参数转换为派生类中的T以实现所需的行为.我注意到这只适用于C风格的演员.

洙...这里发生了什么样的未定义行为?

Quu*_*one 5

您的代码正好是100%经典的多态性,除了一个小错字:你Derived<T>继承私下Base,而不是公开.

你写了:

template<typename T>
class derivedT : base {
//               ^ bases, just like members, default to 'private'
Run Code Online (Sandbox Code Playgroud)

你应该写的:

template<typename T>
class derivedT : public base {
//               ^^^^^^
Run Code Online (Sandbox Code Playgroud)

这就解释了为什么你必须使用"C风格"强制转换而不是简单的static_cast从派生类到其私有基类.如果基地已经公开,static_cast那就可以了.然后你的代码没有什么有趣的东西 - 它只是一个简单的"经典OOP",带有基类和几个派生类.

即使有私人基地,我相信这不是未定义的行为.当你调用虚方法时,你肯定没问题:你有一个类型的指针base*,它实际上指向一个类型的对象base,所以你没事.UB 可能进入的唯一地方就是你进行C风格的转换......但是在这里,编译器可以看到你正在将派生类转换为它的私有库,并且会使它工作.(允许C风格的强制转换绕过C++中的访问控制,但是它们仍然遵守其他规则.它们不会转向reinterpret_cast.)

无论如何,这个问题似乎是CodeReview的主题.您应该做的是发布到StackOverflow,主题为"为什么在将指针Base指向指针指向Derived?时需要C样式转换".答案是,"你只是忘了使用public继承."