将函数作为类的属性 - C++

Having a function as a property on a class - C++

提问人:MWRazer 提问时间:8/29/2023 最后编辑:MWRazer 更新时间:8/29/2023 访问量:100

问:

我希望在 C++ 中创建一个类,它可以将可更改的功能作为其单个属性之一。有没有标准方法可以实现这一点?语法会是什么样子?

例如,我尝试并想做的事情:

#include <iostream>

class Thing {
  private:
    
    int val;
    
    void func();
    
  public:
    
    Thing(int val = 0) {
      this->val = val;
    }
    
    void setFunc(void func()) { this->func() = func; }

    void useFunc() { this->func(); }
    
};

int main() {
  
  Thing thing1 = Thing(1);
  
  thing1.setFunc({
    
    std::cout << "i am thing 1, my value is: " << this->val;
    
  });
  
  thing1.useFunc();
  
  return 0;

}

我不确定这样的事情的语法会是什么样子,或者是否需要指针。这(可能很明显)返回了几个错误,它们是:

./main.cpp:16:46: error: expression is not assignable
    void setFunc(void func()) { this->func() = func; }
                                ~~~~~~~~~~~~ ^
./main.cpp:28:51: error: invalid use of 'this' outside of a non-static member function
    std::cout << "i am thing 1, my value is: " << this->val;
                                                  ^

我意识到我可以使用很多不是函数的属性,然后有一种方法使用这些属性来确定结果,但是我不想这样做,因为它需要更多的属性,我想尝试一些不同的东西来学习如何做到这一点。在这个例子中,做我想做的事情是没有意义的,但这只是一个例子。

C++ 方法 属性

评论

8赞 Alan Birtles 8/29/2023
函数指针??std::function
0赞 πάντα ῥεῖ 8/29/2023
您是否在寻找函数 ponter 或 lambda 表达式?
0赞 user4581301 8/29/2023
稍后阅读:指向成员函数的指针。你知道你迟早会想这样做。
0赞 Ted Lyngmo 8/29/2023
另一个注意事项:您尝试定义和传递到的 lambda 不是 因此不起作用。setFuncThingthis->val
1赞 paddy 8/29/2023
为此使用任何一个或通用函数指针都是微不足道的,但是您的示例期望该函数可以访问私有成员,这是不行的。您不能允许允许任何任意函数或 lambda 访问私有的接口。std::function

答:

1赞 user4581301 8/29/2023 #1

第 1 步:定义函数指针!

class Thing {
  private:

    int val;
    // void func(); declares a function that will be defined later
    // not what we want.
    using func_ptr = void (*)(); // define a new type. It make everything 
                                 // that follows much easier 
    func_ptr func; // declares func to be a pointer to a function

  public:

    Thing(int val = 0): val(val) // use member initializer list
                                 // all members must be initialized
                                 // before entering the body of the
                                 // constructor. not that important
                                 // for an int, but very important for
                                 // types with expensive initialization
                                 // or no default initialization
    {
    }

    void setFunc(func_ptr func)  // pass a function pointer
    {
        this->func = func; // Do not call the function!
                           // avoid reusing names in the same scope.
                           // It only leads to trouble
    }

    void useFunc()
    {
        func(); // which func is not ambiguous here. No need for this
    }
};

第 2 步:分配函数!

  thing1.setFunc({
    
    std::cout << "i am thing 1, my value is: " << this->val;
    
  });

看起来您正在尝试使用 Lambda 表达式。这里有几个问题。

  1. 语法错误。C++不是一种奖励猜测的语言。买一两本好书,以可控的方式学习语法。另一种选择是缓慢的、痛苦的、不值得做的。

正确的语法如下所示

  thing1.setFunc([](){

      std::cout << "i am thing 1, my value is: " << this->val;

    });
  1. 不幸的是,Lambda 没有线索表明它与 关联,也不是 的成员,因此它不能使用 。这有点令人费解。Lambda 需要 Capture ,但一旦捕获,就不能再使用函数指针。它们太简单了,无法处理捕获。输入 std::functionthing1Thingthisthing1

第 3 步:重新开始std::function

#include <iostream>
#include <functional>

class Thing {
  private:

    int val;
    using func_ptr = std::function<void ()>; 
    func_ptr func; 

  public:

    Thing(int val = 0): val(val) 
    {
    }

    void setFunc(func_ptr func)  
    {
        this->func = func; 
    }

    void useFunc()
    {
        func(); 
    }

    int get_val()
    {
        return val;
    }
};
int main() {

  Thing thing1 = Thing(1);
  thing1.setFunc([thing1](){ // captures by value. Not always what you 
                             // want, but generally the safest option.
                             // Just fine for a simple example like this
      std::cout << "i am thing 1, my value is: " << thing1.val;

    });

  thing1.useFunc();

  return 0;

}

这仍然无法工作,因为是私人成员,无法从外部或朋友访问。Lambda 两者都不是,也不容易被塑造成一个。要么 A) Lambda 必须调用一个成员函数来完成实际工作,这毫无意义,因为你会丢弃所有这些 rigamarole,而只是直接调用成员函数,B) 你添加一个 getter for,Lambda 使用 getter,或者 C) 你做 .Thing::valThingThingThingThing::valThing::valpublic

B 看起来是最好的选择。但是,如果您需要的逻辑必须改变怎么办?这需要...thing1

第 4 步:为总体问题找到一个更简单的答案。

评论

1赞 paddy 8/29/2023
如果函数应始终在调用它的对象的上下文中执行,那么它可能应该接受一个参数,而不是在 lambda 中捕获对象(通过引用,或在您的示例中通过复制)。Thing&
0赞 user4581301 8/29/2023
好点子。使切线变成 std::function 有点毫无意义
0赞 paddy 8/29/2023
我不会说这一定是毫无意义的。Lambda 仍然可以捕获其他值,使它们比直接函数更通用。虽然很难从原来的问题中知道整个设计是否是XY问题