【问题标题】:Polymorphism without virtual in C++ for multi level inheritanceC ++中没有虚拟的多态性用于多级继承
【发布时间】:2013-03-22 16:54:39
【问题描述】:

我有一种情况,我需要在没有 vtable 的情况下实现多态性。这是我正在尝试做的事情

  • 有一个类层次结构:C 扩展 B,B 扩展 A
  • 想法是在A中声明一个函数指针,B和C的构造函数将它们对应的方法分配给A中的函数指针
  • 使用下面的代码,我可以实现 C 类的多态性,但不能实现 B 类的多态性。

显然我在这里遗漏了一些东西。我不确定这是否可能。非常感谢您对此问题的任何见解。

我可以用下面的代码做到这一点

A<C> *c = new C();
c->BasePrint(); //Reached C's Print

但不是这个

// A<B> *b = new B();
// b->BasePrint(); //Intentionally incorrect to demonstrate the problem.

有什么办法可以做到吗?

template <typename T>
class A
{
public:
    typedef void (T::*PrintFn)(void);
protected:
    PrintFn printFn;
public:
    void BasePrint()
    {
        if(printFn)
            (((T*)this)->*printFn)();
    }
};


template <typename T>
class B : public A<T>
{
public:
    B()
    {
        printFn = &B::Print;
    }

    void Print()
    {
        //Print B
    }
};



class C : public B<C>
{
public:
    C()
    {
        printFn = &C::Print;
    }

    void Print()
    {
        //Print C
    }
};

【问题讨论】:

  • 你能不能展示一些失败的测试用例?
  • 这不是没有 vtable 的多态性。这是使用手动编码的 vtable 而不是编译器生成的 vtable 的多态性。我看不出有什么原因。
  • 是的,我同意它是一种手工编码的 vtable。我确实有一种情况,我需要将大量代码库移植到不支持虚函数的编译器。试图找到一种聪明的方法来做到这一点。
  • @anumalla: 什么样的C++编译器不支持虚函数?
  • 我认为即使是 20 多年前的 Tubro C++ 也支持虚函数。我不记得曾经发布过任何不支持虚函数的 C++ 编译器。即使是 80 年代的 Cfront,它是 C 语言的预处理器,也支持虚函数。

标签: c++ templates polymorphism virtual function-pointers


【解决方案1】:
#include <iostream>
#include <typeinfo>

struct own_type {};

template<template<typename T>class CRTP, typename In, typename D>
struct DoCRTP: CRTP<In> {};
template<template<typename T>class CRTP, typename D>
struct DoCRTP<CRTP, own_type, D>: CRTP<D> {};

template<typename D>
struct A {
   D* self() { return static_cast<D*>(this); }
   D const* self() const { return static_cast<D*>(this); }
   A() {
      std::cout << "A<" << typeid(D).name() << ">\n";
      self()->print();
   }
};

template<typename T=own_type>
struct B:DoCRTP<A, T, B<T>> {
   B() {
      std::cout << "B<" << typeid(T).name() << ">\n";
   }
   void print() { std::cout<<"I am a B\n"; }
};

template<typename T=own_type>
struct C:DoCRTP<B, T, C<T>> {
   C() {
      std::cout << "C<" << typeid(T).name() << ">\n";
   }
   void print() { std::cout<<"I am a C\n"; }
};

void test() {
   std::cout << "Instance of B<>:\n";
   B<> b;
   std::cout << "Instance of C<>:\n";
   C<> c;
}

int main() {
   test();
}

这里我们有一种方法可以传入派生度最高的类,如果不传入任何内容,则假定您是派生度最高的类。

但是,你的设计有一个问题——A 已经完全知道它的类型情况,所以不需要虚拟行为! BasePrint 可以 static_cast&lt;T*&gt;(this)-&gt;Print() 并且你会取消你的开销。

您遇到的根本问题是您将特定类型的成员函数指针存储在您的基类A 中。

无模板的A 可以存储指向非特定类型函数指针的指针——比如显式将A* 作为第一个参数的“静态”指针。在 C++11 中,您可以从成员函数中自动构建这些函数。在 C++03 中,std::bind 应该允许您将指向 D 的成员函数指针转换为以 A* 作为第一个参数的函数。

【讨论】:

  • 谢谢,这是我一直在寻找的东西。但我仍然无法用这个解决方案做类似的事情。 A* a = 新 A(); A* b = 新 B(); A* c = 新 C();
  • 是的。在代码之后,我讨论了如何去做。这个想法是同时存在AA_impl&lt;T&gt;,其中A 存储您的手动vtable 函数,如void (*)(A*,...),而A_impl&lt;T&gt; 采用void (T::*)(...) 之类的方法并将它们存储在A 中。如何做到这一点取决于您的编译器支持哪些其他功能——有了 lambda 支持,这很容易。
【解决方案2】:

您没有为 B in 指定模板参数:

A<B> *b = new B();

与其声明相反:

template <typename T>
class B : public A<T>

您应该使用以下几行:

A<B<X>> *b = new B<X>();

X 是非模板类型。

【讨论】:

    【解决方案3】:

    我可以用下面的代码来做到这一点 [...] 但不是这个:

        A<B> *b = new B();
        b->BasePrint(); //Intentionally incorrect to demonstrate the problem.
    

    好吧,这里的问题是B 是一个类模板,而你没有实例化它。它与多态性或 vtable 没有太大关系。类模板只是通过向类型传递参数来实例化类型的蓝图(嗯,实际上是一个模板),但它本身并不是一个类型

    在实例化B 时应该使用一些模板参数。例如:

    A<C>* b = new B<C>();
    b->BasePrint();
    

    你应该看到这个调用B::Print()。这是live example

    【讨论】:

    • 应该是A&lt;B&gt;* b = new B&lt;B&gt;()。但这自然是行不通的:B 是一个模板,而不是一个类型。
    猜你喜欢
    • 2014-10-12
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-06
    相关资源
    最近更新 更多