3

我正在创建一个基于回调的类。我需要给客户一个调用他定义的回调的自由。例如,我有一个类 Base:

class Base
{
...
 public:
    virtual void OnMsgReceived(const char *msg, char *&response);
    virtual void OnMsgReceived(const char *msg, string &response);
};

客户端必须实现其中一个,但不是 2。我如何知道他实现了哪一个,以便在处理回调时调用正确的?

我知道在对象的构造实例化期间我不能这样做,但是一旦对象被实例化,有什么方法可以让我检查这些虚函数中的哪一个已经实现?谢谢。

4

5 回答 5

0

在没有纯virtual技术的 C++ 中,无法在运行时知道virtual函数是否由类实现。

将这两个函数都定义为纯虚函数,并让客户端实现它们。如果需要,您仍然可以拥有纯虚函数体Base

客户端必须实现其中之一,但不是 2。

假设两个版本都是真正需要的,我觉得不需要上述要求。

我怎么知道他实现了哪一个,以便在处理回调时调用正确的?

一旦为引用/指针分配了适当的对象Base,您就不必担心这一点。编译器为您完成了这项肮脏的工作。

另外,您还可以探索CRTP技术。派生类必须实现给定方法才能通过编译。但是,这不会像virtual功能那样灵活。

于 2012-11-10T04:15:23.590 回答
0

荒谬的做法:在基类中从一个分派到另一个。除非用户至少覆盖了其中一个,否则会导致很容易检测到的 stackoverflow :

void Base::OnMsgCallback(const char *msg, char*& response) {
   std::string resp;
   OnMsgCallback(msg,resp);
   response = new char[resp.size()+1]();
   std::copy(resp.begin(), resp.end(), response);
}
void Base::OnMsgCallback(const char *msg, std::string& response) {
   char *resp;
   OnMsgCallback(msg,resp);
   response = resp;
   delete [] resp;
}

但这是一个可怕的 hack,您可能已经注意到该char*版本需要手动处理资源,这很容易出错。通过定义一个传递对 a 的引用的接口(返回std::string类型为打回来。std::stringresponsestd::string

于 2012-11-10T04:31:34.013 回答
0

由于很多原因,这种设计很糟糕,无论如何有一种方法可以做到:

class Base
{
public:
    virtual void OnMsgReceived(const char *msg, char *&response)
    {
        response = (char*)"OMG! nothing derived"; // or throw if you want
    }

    virtual void OnMsgReceived(const char *msg, string &response)
    {
        char *ptr_to_dest = 0;
        OnMsgReceived(msg, ptr_to_dest);
        response = ptr_to_dest;
    }
};

总是string调用那个,你不需要检查他们做了哪一个。

这将取决于他们实施的内容:

  • 只实现char版本-> 它将被调用并转换为字符串
  • 只实现字符串版本-> 它将被直接调用
  • 实现两个版本-> 只会调用字符串
  • 什么都不做->你会得到你的默认消息(或者如果你抛出异常)
于 2012-11-10T11:49:10.837 回答
0

我想在iammilind 的回复中添加一些内容:

如果你在一个类中使用纯虚函数,你就是把这个类抽象化,因此你不能实例化它。

如果 A 是一个抽象类

class A
{
  public:
  //
  virtual void foo() = 0;
  //
}

你不能写

A myA

与往常一样,关于这方面的一个很好的资源是Parashift C++ 常见问题解答网站

于 2012-11-10T04:24:25.333 回答
0

要弄清楚哪个函数被覆盖并不容易——即使你可以,它也是一个开发人员很难遵循的设计:客户端必须实现其中一个,但不是 2——开发人员可能会忘记它,并且他们可能不喜欢!

我会根据您的设计建议一种替代方法:

你的框架

class Base0
{
public:
    // other basic members

};

template<class T>
class Base : public Base0
{
    virtual void OnMsgReceived(const char *msg, T &response) = 0;
};


typedef Base<char*> BaseChar;

typedef Base<string&> BaseString;



void run(Base0* pBase)
{
    const char* msg = getmsg();
    BaseChar* p = dynamic_cast<BaseChar*>(pBase);
    if(p != NULL)
    {
        char* response; // 
        p->OnMsgReceived(msg, response)
        return;
    }

    BaseString* p2 = dynamic_cast<BaseString*>(pBase);
    if(p2 != NULL)
    {
        string response; // 
        p2->OnMsgReceived(msg, response)
        return;
    }

}

客户代码

class Derived: public BaseChar
{
public:
    virtual void OnMsgReceived(const char *msg, char* &response)
    {
        // do sth
    }       
};

您的框架的用户需要实现 BaseChar 或 BaseString,然后一切正常。

于 2012-11-10T04:38:21.793 回答