繁体   English   中英

如何创建接受具有不同签名的函数指针的 C++ 类?

[英]How can I create a C++ class that accepts function pointers with different signatures?

我对 C++ 比较陌生,我正在做一个个人项目,用于练习我需要创建一个可以使用具有不同签名的函数指针的类。

这个想法是类的每个实例都将存储一个指向特定函数的指针,并且可以随时调用该函数。

为了更好地了解我想要什么,让我更详细地解释一下我想要做什么。 我正在处理的项目是一个非常基本的控制台游戏,我尝试创建的对象是一个对象,该对象将存储玩家可以在游戏中访问的每个位置的详细信息。

(免责声明:我知道我后面描述的大部分内容对于基本的控制台游戏来说可能是一种矫枉过正。我可以使用简单的函数轻松地将整个游戏制作成几个文件,而且我知道如何做到这一点。但这里的想法是我想练习更高级的 C++ 技术,而不必弄清楚一个复杂的项目。所以,既然我知道如何制作一个基本的控制台游戏,我认为尝试弄清楚如何实现相同的目标是个好主意结果,但使用更先进的技术)

我认为应该存储的细节之一是每个位置发生的事情,基本上是输出到屏幕的文本,描述发生的事情并提示用户采取行动。

由于每个位置都会有所不同,因此我不能只在类中声明和实现一个函数。

解决这个问题的一种方法是创建一个带有虚函数的基类,然后在一系列派生类中实现这个函数,每个派生类定义一个新的位置。

我使用这种方法的问题是它使每个位置成为一个可以进一步继承和实例化的类,我不需要它,因为每个位置只有 1 个实例。

我当然可以只创建类的 1 个实例,但我想看看是否有办法避免为每个位置创建单独的类。

这就是为什么我开始考虑函数指针的原因。

现在,我知道我可以声明一个函数指针并在这样的类中初始化它:

class Test
{
    public:
        Test(void (*p)())
            : print{p}
        {}
    private:
        void (*print)();
};

只要函数返回 void 并且不接受任何参数,就可以正常工作。

所以,我想也许我可以用一个模板来做到这一点:

template <typename Function>
class Test
{
    public:
        Test(Function *p)
            : print{p}
        {}
        Function *print;
};

这实际上运作良好。 我现在可以拥有一个接受具有不同返回类型的不同函数的类。

我可以通过以下方式创建类的实例:

void print();

Test<void ()> a {print};

但是,我对这种方法有一个问题。 因为它是一个类模板,所以我不能有一个指针来指向 Test 类的实例,而不管传递给它们的函数如何。

例如,如果我声明以下指针:

Test<void ()> *b = &a;

除非传递给它的函数指针也返回 void 并且不接受任何参数,否则无法将该指针重新分配给 Test 类的另一个实例。 否则,我必须创建一个新指针。

有没有办法避免这种情况? 有没有比使用函数指针更好的方法来实现我正在寻找的东西?

非常感谢你,很抱歉给你这么长的消息。

除非传递给它的函数指针也返回 void 并且不接受任何参数,否则无法将该指针重新分配给 Test 类的另一个实例。

你希望它如何运作? 每个Testprint方法都有不同的签名。 在下面的示例中,如果您将action2分配给action1 ,那么它如何知道要传递给打印函数的内容?

void fun1() {}
void fun2(int) {}

void test() {
  Test<void()> action1= &fun1;
  action1.print();
  Test<void(int)> action2= &fun2;
  action2.print(42);
}

由于 C++11 的改进,您想要的非常简单: std::function<void()>

每当您需要向函数传递一些参数时,您都可以使用 lambda,可能带有捕获:

#include <functional>

void fun1() {}
void fun2(int) {}

using Action = std::function<void()>;

int main() {
  class MyObject { public: void doSomething() {} } obj;

  Action action1;
  action1 = fun1;
  action1 = []{ fun2(42); };
  action1 = [&obj]{ obj.doSomething(); }; // obj captured by reference
}

所以这就是你应该做的。

现在当然你可能会问“嘿,但如果我想自己实现像std::function这样的东西怎么办”? 如果您想获得std::function全部功能,包括小对象优化(即std::function通常不会分配,除非它包装的函子“大”),这不是一项微不足道的任务。

我想看看如果我有理由使用具有不同返回类型的函数来获取每个游戏位置,我该怎么办。

您选择使用一个通用的抽象,即您可以始终以相同方式调用的某个类(如Action ),并可以做各种事情。 那么,您希望以下代码做什么?

Action someAction;
int result = someAction();
std::string result = someAction();

现在假设例如someAction已经捕获了一个void()函数。 将“结果”分配给整数或字符串的代码应该做什么? 您将如何防止错误,例如,如果您真的希望someAction()返回一个 int,但您不小心使用了一个什么都不返回的方法?

请记住,编译器必须为您调用someAction所有站点生成代码,因此它需要提前知道要做什么,即使您可以通过其他方式确保不会进行“不正确”的调用。

您可以在std::function之上实现自定义Action类,该类可以支持std::optional<ResultType>而不是ResultType 这样,如果包装在Action中的函子的实际结果可转换为该ResultType ,您将获得一个非空的可选项,否则该可选项将为空。 并且支持的结果类型列表需要预先确定,即编译器将无法动态添加它们,但添加新类型应该相当于传递一个附加类型作为模板参数,所以它是很容易支持相当多的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM