【问题标题】:How can I pass "Any kind of data" to a function in C++如何将“任何类型的数据”传递给 C++ 中的函数
【发布时间】:2012-04-10 14:38:25
【问题描述】:

假设我有一个类 Handler,它带有一些子类,如 stringhandler、SomeTypeHandler、AnotherTypeHandler。类 Handler 将方法“handle”定义为所有子类的公共接口。对于不同的处理程序,“处理”的逻辑当然是完全不同的。

所以我需要做的就是将任何值传递给 handle 方法。然后,特定的类可以将“任何东西”转换为他们期望的类型。

基本上我需要的是 java 类 Object :D 之类的东西

我尝试的第一件事是void*,但显然你不能做到B* someB = dynamic_cast<B*>(theVoidPointer),所以没有运气。

我的第二个想法是使用boost::any。但是,使用 boost::any 的要求是该值必须是可复制的,而我的数据并非如此。

有什么想法可以让它发挥作用吗?

谢谢

编辑:请注意,我知道我可以使用没有成员的 SomeData 类,并让我的数据成为它的子类,但我正在寻找一种更通用的方法,它不需要我制作自己的包装类.

【问题讨论】:

  • 您确定需要dynamic_cast,而不能使用reinterpret_cast
  • 看起来你的大部分选项都在这里描述:stackoverflow.com/questions/913505/…
  • 你也可以使用 boost::any 来存储指针,所以处理的类型不需要是可复制构造的
  • 我认为你应该退后一步,告诉我们你想要完成什么,而不是你对如何实现它的想法。在我看来,你基本上是带着一个可能在 Java 中可行的想法而陷入困境,但这是对 C++ 的可怕滥用(老实说,总体而言,这是一种可怕的 OO,但 Java 接受了这种想法)。
  • 后退,后退,后退。 Upcast 通常是糟糕设计的标志。类型转换(在开放集上)通常是糟糕设计的标志。

标签: c++ pointers boost-any


【解决方案1】:

好的,这是一个简单的方法,使用 boost::any 来保存指向数据类型的指针。但是,请注意 boost::any 会增加一些开销代码,稍微降低性能(在大多数情况下可以忽略不计)。考虑使用 boost::spirit::hold_any 代替,如果不需要类型安全,则使用 void*。

class Handler {
public:
    void handle( boost::any value ) { 
        do_handle( value );
    }
private:
    virtual void do_handle( boost::any& value ) = 0;
};

template<class T>
class HandlerBase : public Handler {
private:
    void do_handle( boost::any& value ) {
        // here you should check if value holds type T*...
        handle_type( *(boost::any_cast<T*>( value )) );
    }

    void handle_type( const T& value ) = 0;
};

class StringHandler : HandlerBase<std::string> {
private:
    void handle_type( const std::string& value ) {
        // do stuff
    }
};

现在您可以编写许多从 HandlerBase 派生的处理程序类,而无需假设处理的类型具有公共基类。

【讨论】:

  • 这是我使用的解决方案,除了模板化的 HandlerBase。我把它留了下来。我只是让特定的处理程序转换为他们需要的类型。一旦我有多个相同数据类型的处理程序,我就会考虑将模板化基础放在两者之间。谢谢!
【解决方案2】:

例如,您可以定义一个基类:

class BaseHandlerData {
   ...
};

然后派生您的处理程序所期望的特定数据类:

class StringData: public BaseHandlerData {
   ...
};

class SomeData: public BaseHandlerData {
   ...
};

然后你应该能够将 BaseHandlerData* 参数传递给 handle 方法,并使用类似的东西:

void handle(BaseHandlerData *data) {
    StringData* stringData = dynamic_cast<StringData*>(...);
    // handle your string data here ...
}

安全地转换为您预期的数据类型。

杰拉德

【讨论】:

  • 这是我在编辑中简要介绍的内容。确实是一个很好的解决方案,但我宁愿不为此编写额外的课程。我宁愿把它作为一个基本的实用程序(比如 boost::any)。
【解决方案3】:

另一个更接近C 世界的替代方案是union 类型(http://en.wikipedia.org/wiki/Union_(computer_science)#C.2FC.2B.2B)。这将只允许您传递您指定的类型,而不是 任何 类型,但具有您描述的行为类型。

【讨论】:

    【解决方案4】:

    您需要有一个名为 DataObject 或其他东西的基类。您的所有数据类型(字符串、数字、诸如此类)都是 DataObject 的子类。你这样定义句柄:

    void Handle(DataObject *dataObject);
    

    这是一种更安全的方式来做你想做的事。为了使它更好,DataObject 甚至可以知道它包含什么样的数据。然后处理程序可以检查是否已向它们发送了正确类型的数据。

    【讨论】:

    • 这基本上是我在编辑中添加的内容。这似乎确实是一个很好的解决方案,但我一直在寻找一种更通用的方法。我仍然将它保留在我的选项列表中,以防其他解决方案不起作用。
    【解决方案5】:

    你可以的

    B* someB = static_cast<B*>(theVoidPointer);
    

    【讨论】:

    • @R.MartinhoFernandes 和 BertR 您能否详细说明重新解释、静态和动态之间的区别?我认为 reinterpret 和 static 有一些危险。
    • @R. Martinho Fernandes:在 C++11 之前,reinterpret_cast 确实会导致未定义的行为,但对于 C++11,情况不再如此。好吧,Stroustrup 似乎犯了同样的错误,所以我本可以犯更糟的:compgroups.net/comp.lang.c++.moderated/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 2018-10-30
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多