【问题标题】:C++ Dynamic binding in method argument方法参数中的 C++ 动态绑定
【发布时间】:2014-09-25 21:36:23
【问题描述】:

我最近想让 c++ 通过其输入参数动态解析成员/函数,该输入参数出现在某些派生版本中。这就是我的意思:

#include <iostream>

class Base {
};

class DerivedA : public Base {
};

class DerivedB : public Base {
};

class DerivedC : public Base {
};

class Test {
public:
    void world( DerivedA *instance )
    {
        std::cout << "DerivedA" << std::endl;
    }

    void world( DerivedB *instance )
    {
        std::cout << "DerivedB" << std::endl;
    }

    void world( Base *instance )
    {
        std::cout << "Base" << std::endl;
    }
};

int main()
{
    Base *a = new Base;
    Base *b = new DerivedA;
    Base *c = new DerivedB;
    Base *d = new DerivedC;

    Test hello;
    hello.world( a );
    hello.world( b );
    hello.world( c );
    hello.world( d );

    return 0;
}

想要的行为是这样的:

Base
DerivedA
DerivedB
Base

但可以肯定的是,我真正得到的输出是这样的:

Base
Base
Base
Base

我知道,动态绑定是另一种方式,在基类的派生类中解析正确的成员函数,而不是那样 - 但它可以以任何方式工作吗?

也许我只是错过了要点..

不过,提前非常感谢!

塞巴斯蒂安

【问题讨论】:

  • 这里的用例是什么?我不明白为什么不能简单地将每个变量定义为相应的派生类型而不是Base。获得所需功能的唯一方法是使用dynamic_cast&lt;&gt;
  • 您将Base* 传递给所有这些函数。您需要使用 virtual functions 来实现您想要实现的目标(您必须将 world 函数设为成员函数)。
  • @RedAlert:您还需要至少有一个虚拟成员函数,然后执行if(dynamic_cast&lt;X&gt;(instance) { ... } - 这两者可能比一开始调用虚拟函数更耗时。
  • 首先,这背后的意义是,实例是在其他函数中创建的,但是在我想从“测试”类运行不同代码的地方,我无法确定每个实例是哪个类对象是一个实例。我知道,我可以像 Mats Petersson 所说的那样做,但我想将代码保留在测试类中,而不是对象中。
  • 我现在能想到的一个解决方案(我可能会在几个小时或几天内想出其他东西)是添加一个包装类。让我把它写下来......

标签: c++ rtti dynamic-binding


【解决方案1】:

abcd的类型都是Base*。编译器不跟踪“变量包含的内容”。如果这是您想要做的,那么您需要在您派生的类中使用虚函数,例如:

class Base {
 public: 
   virtual const char* MyName() { return "Base"; }
};

class DerivedA : public Base {
 public: 
   virtual const char* MyName() { return "DerivedA"; }
};
... similiar for all derived classes ... 

void world( Base *instance )
{
    std::cout << instance->MyName() << std::endl;
}

(编辑:要准确获得您在第一种情况下列出的行为,您不需要在 DerivedC 类中实现 MyName() 函数)

因此,使用包装类可能是测试设置的解决方案。这是我刚刚破解的东西,没有太多考虑和复杂性:

#include <iostream>

class Base {
};

class DerivedA : public Base {
};

class DerivedB : public Base {
};

class DerivedC : public Base {
};

class Test {
public:
    void world( DerivedA *instance )
    {
        std::cout << "DerivedA" << std::endl;
    }

    void world( DerivedB *instance )
    {
        std::cout << "DerivedB" << std::endl;
    }

    void world( Base *instance )
    {
        std::cout << "Base" << std::endl;
    }
};

template<typename T>
class Wrapper
{
public:
    Wrapper(T* i) : instance(i)
    {
    }
    ~Wrapper()
    {
        delete instance;
    }

    void doTest(Test& t)
    {
        t.world(instance);
    }

    T* instance;
};


int main()
{
    Test hello;
    Wrapper<Base> a(new Base);
    Wrapper<DerivedA> b(new DerivedA);
    Wrapper<DerivedB> c(new DerivedB);
    Wrapper<DerivedC> d(new DerivedC);

    a.doTest(hello);
    b.doTest(hello);
    c.doTest(hello);
    d.doTest(hello);

    return 0;
}

【讨论】:

  • 在 C++ 中,你不能这样做(好吧,至少在不使用 if(dynamic_cast&lt;...&gt;(...)) { ... } 类型构造的情况下不能这样做,这真的不是正确的方法)。
  • 使用包装器的方法非常棒。还显示了 c++ 的限制;)不确定我是在使用它还是接受它作为使用更合适的 OO 设计的提示:) - 谢谢!
  • 很可能有更漂亮/更好的解决方案...但是在我的编译器中尝试让访问者模式在我的 AST 上工作时,我无法在晚上 11 点半想到... ;)
【解决方案2】:

在您的示例中,您没有运行时多态方案(即动态绑定)。您所拥有的是一个重载的成员函数,并且在重载决议中编译器正确选择了void world( Base *instance )

为了得到你想要的,你应该应用如下继承方案:

class Base {
public:
  virtual ~Base() {}
  virtual void world() const { std::cout << "Base" << std::endl; }
};

class DerivedA : public Base {
public:
  virtual ~DerivedA() {}
  void world() const { std::cout << "DerivedA" << std::endl; }
};

class DerivedB : public Base {
public:
  virtual ~DerivedB() {}
  void world() const  { std::cout << "DerivedB" << std::endl; }
};

class DerivedC : public Base {
public:
  virtual ~DerivedC() {}
  using Base::world;
};

Live Demo

编辑:

为了将您的代码保持在一个地方,您可以将以下 Test 类的更改版本添加到上述方案中:

class Test {
public:
  void world( DerivedA *instance ) { instance->world(); }
  void world( DerivedB *instance ) { instance->world(); }
  void world( Base     *instance ) { instance->world(); }
};

Live Demo

不幸的是,重载决议发生在编译时,而动态调度发生在运行时。因此,如果您打算让编译器从Base 指针推断出基础类型,然后从Test 类中提取正确的成员函数,这是不可行的。

【讨论】:

  • 谢谢,但正如我对 Mats Peterssons 解决方案的评论,我想将代码保留在“测试”类中。
猜你喜欢
  • 2018-10-12
  • 1970-01-01
  • 2018-12-21
  • 1970-01-01
  • 2018-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多