传递成员函数(或解决方法)

5

I want to do the following:

class A{
private:
  //some data
  B b;
  double f(double);
public:
  A(){
    b = new B(f); // does not work of course
  }
};

class B{
public:
  B(double (*func)(double));
};

Class B旨在解决由函数func指定的数学问题。现在,Class A应该使用B来解决这个问题,使得func=f。成员函数f访问A的私有数据成员。

问题是,当然我不能简单地传递一个成员函数指针。我知道有方法可以做到这一点,但B仍然应该能够接受任何函数,而不仅仅是A的成员函数。

到目前为止,我只是将f和A的成员变量设为静态的,但我认为这是一个相当糟糕的设计。你能想到任何解决方法吗?


4
使用std::function。如果您需要将一个对象用于成员函数的this,现在很容易做到。如果成员函数不使用this,则将其定义为自由函数或静态函数。 - chris
这个问题看起来很相似。 - Gluttton
谢谢,std::function 看起来不错,我稍后会尝试一下。我不是很熟悉 C++11,所以可能没找到它。 - cthl
@cthl,boost::function在C++11之前就存在了 :) - chris
5个回答

3
如何考虑使用多态作为您当前设计的替代方案呢?
例如:
class A
{
protected:
    virtual double f(double x) = 0;
};

class B1 : public A
{
public:
    double f(double x) {return x+1.0;}
};

class B2 : public A
{
public:
    double f(double x) {return x+2.0;}
};

...

A* arr[4];
arr[0] = new B1;
arr[1] = new B2;
arr[2] = new B1;
arr[3] = new B2;
for (int i=0; i<4; i++)
    cout << arr[i]->f(0.0);

感谢您的建议,但我认为我不能在我的问题上使用它。我必须使用数百个不同的函数f,它们都依赖于程序的输入,即事先我无法知道它们。此外,我正在进行数学模拟,因此性能真的是一个问题。使用你的设计,我将不得不调用数百万次虚函数。 - cthl
@cthl 可能虚拟调用与实际计算相比微不足道。如果确实需要,先尝试并进行优化。我会选择使用虚拟调用的清晰和安全方法 :) - Mircea Ispas
@cthl:不用谢。请注意,虚函数本质上是一个函数指针(即,在两种情况下,每当调用函数时都会应用额外的内存加载操作)。因此,在运行时性能方面,无论使用哪种机制,都没有区别。 - barak manos
@Felics:谢谢。无论如何,在运行时性能方面,使用虚函数和使用函数指针没有区别(请参见我上面的评论)。 - barak manos

3
你可以使用标准的std::function<>模板作为函数的多态包装器。
你的类 B 仅仅存储了一个 std::function<double (double)> 的实例,并通过 foo 调用它:
class B
{
  public:
    B(const std::function<double (double)>& func) : func(func) {}

    void foo(double d) 
    {
       std::cout << func(d);   
    }

  private:
    std::function<double (double)> func;
};

当你的类 A 通过其中一个成员函数 (f) 构建其 B 成员时,由于 std::bind 的帮助:

class A
{   
  public:
    double md;
    B b;

    double f(double d) const
    {
      return md * d;
    }

  public:
    A(double d) : md(d), b(std::bind(&A::f, this,  std::placeholders::_1)) { }
};

我们现在可以简单地使用它:
int main() {
   A a(42);
   a.b.foo(2); // Output : 84
}

这里有实时演示.


2
这是一个完整的程序示例,可供您使用。
#include <memory>
#include <iostream>
#include <functional>

class B
{
    public:
        B(std::function<double(double)> func)
        {
            std::cout<<func(1.0);
        }
};

class A
{
    private:
        std::unique_ptr<B> b;

        double f(double)
        {
            std::cout<<"A::f";
            return 2.0;
        }

    public:
        A() : b(new B(std::bind(&A::f, this, std::placeholders::_1)))
        {
        }
};

int main()
{
    A a;
}

请注意,您不要在那里销毁b,并且请记住您正在将this传递给B,这可能是危险的(A被销毁后B可能仍然存在,如果您在f内部使用this...砰!)。
我还建议避免使用指针,如果不可能,请使用std::unique_ptr 编辑:还有一个没有b作为指针,std::functionstd::bind的版本。
#include <iostream>

class A;

class B
{
    public:
        B(A* obj, double(A::*func)(double))
        {
            std::cout<<(obj->*func)(1.0);
        }
};

class A
{
    private:
        B b;
        double f(double)
        {
            std::cout<<"A::f";
            return 2.0;
        }

    public:
        A():b(this, &A::f)
        {
        }
};

int main()
{
    A a;
}

1
在析构函数中应该有一个 delete 来释放 new B(...) 的内存(或者你可以使用 unique_ptr :) ) - Scis
我知道,我在答案中指出了这一点:) 我想保留他原来的代码。 我会进行编辑,因为它可能会令人困惑。 - Mircea Ispas
谢谢。我知道我必须在析构函数中删除 b。我只是没有在这里写出来,以保持简单。现在我将尝试使用 std::function/std::bind 组合。 - cthl
当我尝试运行你的示例时,一切都正常。但在我的程序中,创建B并调用std::bind导致了分段违规错误... - cthl
抱歉,segmentation fault 错误是我的错误。现在一切正常了,谢谢。 - cthl

0

由于函数f()不是静态的,它的类型是"double (A::*)(double)", 而不是"double (*)(double)"。

然而针对这个特定情况,重新设计可能会更好。

A是否真的由数据和问题解决器(类B)的实例组成,或者A是否只包含数据并将某些操作委托给B?

类B是否真的需要了解类A中的方法,还是只期望以某种格式获取数据?

class DataType {};
class ResultType {};
class A
{
    DataType data;
    DataType preprocess(Data d) {/*...*/}
    ResultType process() { return B::compute(data); }
    bool isSolvable() { return B::solve(preprocess(data)); }
};
class B
{
public:
    ResultType compute(DataType);
    bool solve(DataType);
};

0
可能的解决方案:
class A
{
    B* b;
    static double f(double d); //static change function type form `double(A::*)(double)` to `double(*)(double)`
public:
    A()
    {
        b = new B(f);
    }
}

唯一的缺点是f无法从AB中获取任何数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接