在内部使用struct member和函数指针

时间:2017-05-15 00:14:22

标签: c struct function-pointers

我有一个结构定义为:

typedef struct OneParamDist_t {
  double p;
  double (* rvs)(double);
} OneParamDist;

这样分配和调用看起来像:

OneParamDist * flip = malloc(sizeof(OneParamDist));

flip->p = 0.5;
flip->rvs = bernoulli_rvs;
double x;
for(int i = 0; i < 100000; i++){
  x = flip->rvs(flip->p);
}

理想情况下,我有:

typedef struct OneParamDist_t {
  double p;
  double (* rvs)(p);
} OneParamDist;

这样调用它看起来像:     x = flip-&gt; rvs();

我无法弄清楚如何做到这一点,或者这是否可行。这甚至可能吗?感觉就像我试图在C中使用光对象方向一样,它可能不会用于它。

3 个答案:

答案 0 :(得分:2)

中的面向对象代码是完全可能的,但是从语言中获得的只是数据结构和指针。其他一切都取决于你。

有助于理解“OO-Languages”通常已在内部执行的操作。在中,您可以像下面这样编写示例:

#include <iostream>

class OneParamDist
{
    private:
        double p;

    public:
        OneParamDist(double p) : p(p) {}
        double rvs();
};

double OneParamDist::rvs()
{
    double result = this->p;
    // calculate something
    return result;
}

int main(void)
{
    OneParamDist flip(2.4);
    double result = flip.rvs();
    std::cout << "flip.rvs() = " << result << std::endl;
}

此代码为您提供仅包含p的数据结构,根本没有函数指针。这些函数是构造函数(但它是空的,仅用于此处的初始化)和rvs,但它的名称为OneParamDist::rvs,因此编译器知道它“属于”OneParamDist

该函数使用指针“this”。那么this来自哪里?解决方案很简单:它作为参数传递。您没有在代码中看到它,因为会为您处理它。编译时,

double OneParamDist::rvs();

被翻译成类似

的内容
double mangled_name_for_OneParamDist_rvs(OneParamDist *this);

mangled_name_for_OneParamDist_rvs中的一些“普通”名称,生成此名称的规则特定于所使用的编译器。

有了这些知识,你可以很容易地将这个简单的程序“翻译”为普通

#include <stdio.h>

typedef struct OneParamDist
{
    double p;
} OneParamDist;

#define OneParamDist_init(p) { .p = (p) }

double OneParamDist_rvs(OneParamDist *self)
{
    double result = self->p;
    // calculate something
    return result;
}

int main(void)
{
    OneParamDist flip = OneParamDist_init(2.4);
    double result = OneParamDist_rvs(&flip);
    printf("OneParamDist_rvs(&flip) = %lg\n", result);
}

我在这里使用了自己的命名方案,在每个“方法”名称前加上“[classname]_”。我决定使用self代替this,为了避免混淆,在普通中使用this是完全合法的。但这当然会破坏与的兼容性。

基本上,答案是:接受对于OO方法的事实,你总是需要一个指向对象的指针作为参数(按照惯例,第一个参数)和不像那样自动为你传递指针。相应地创建代码。

回到你在struct中有一个函数指针的想法,你应该什么时候这样做?请查看virtual关键字。它允许您从您的类派生,并通过指向基类的指针调用此派生类的方法。如果你考虑一下,这需要指向方法的指针在某处可用。 通常使用vtable对象来解决这个问题,这些对象只包含函数指针。如果您使用自己的“虚拟”方法,将它们作为函数指针放在对象中是最直接的方法。但是只要你不需要虚方法,就不要使用函数指针,它们只是开销而无法获得。

另一件值得一提的是,使用,您可以实现完美的信息隐藏级别(例如 pimpl成语如果总是如此动态分配对象。在这种情况下,您可以隐藏来自呼叫者的struct的整个定义。您的示例可能如下所示:

<强> oneparamdist.h

#ifndef ONEPARAMDIST_H
#define ONEPARAMDIST_H

typedef struct OneParamDist OneParamDist;

OneParamDist *OneParamDist_create(double p);
double OneParamDist_rvs(OneParamDist *self);
void OneParamDist_destroy(OneParamDist *self);

#endif

<强> oneparamdist.c

#include <stdlib.h>
#include "oneparamdist.h"

struct OneParamDist
{
    double p;
};

OneParamDist *OneParamDist_create(double p)
{
    OneParamDist *self = malloc(sizeof(*self));
    if (!self) return 0;
    self->p = p;
    return self;
}

double OneParamDist_rvs(OneParamDist *self)
{
    double result = self->p;
    // calculate something
    return result;
}

void OneParamDist_destroy(OneParamDist *self)
{
    if (!self) return;
    free(self);
}

<强> example.c

#include <stdio.h>
#include <stdlib.h>

#include "oneparamdist.h"

int main(void)
{
    OneParamDist *flip = OneParamDist_create(2.4);
    if (!flip) return EXIT_FAILURE;
    double result = OneParamDist_rvs(flip);
    printf("OneParamDist_rvs(flip) = %lg\n", result);
    OneParamDist_destroy(flip);
}

答案 1 :(得分:0)

C没有提供C ++所做的语法糖。您仍然可以在C中编写面向对象的代码,但是您需要显式传递this指针。

答案 2 :(得分:-3)

无法将函数指针绑定到始终接受p作为第一个参数,因为您尝试不执行任何操作。这是C作为非面向对象语言的限制。

可能最容易做的就是接受这个。不要认为C结构就像C ++类一样,但就像它们一样 - 结构化数据容器。

但是,如果你真的需要你的代码以你使用它的方式工作,我能想到的最直接的方法是制作rvs函数可以直接访问的pa模块级变量,并指向外部p存储在struct。

#include <stdlib.h>

double p;

typedef struct OneParamDist_t {
    double *p;
    double (* rvs)(void);
} OneParamDist;

double rvs_func(void);

int main(void)
{
    OneParamDist *flip = malloc(sizeof(OneParamDist));
    extern double p;

    p = 2.0;
    flip->p = &p;
    flip->rvs = rvs_func;

    flip->rvs();  // returns 4.0
    flip->rvs();  // returns 8.0
    *flip->p      // is now 8.0

    return 0;
}

double rvs_func(void)
{
    extern double p;
    p *= 2;
    return p;
}