繁体   English   中英

在内部使用结构成员和函数指针

[英]Using struct member internally with function pointer

我有一个结构定义为:

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-> rvs();

我只是不知道该怎么做,或者这是否可能。 这有可能吗? 感觉就像我正在尝试在C中使用轻型对象方向,并且可能只是不使用它。

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

它有助于了解“ OO语言”通常已经在内部执行的操作。 ,您可能会像这样编写示例:

#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的整个定义。 您的示例如下所示:

一个参数

#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

一个参数

#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);
}

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

试图将函数指针绑定为始终不接受p作为第一个参数是不可能的。 这是C作为非面向对象语言的局限性。

也许最容易做的就是接受这一点。 不要认为C结构像C ++类,而是像它们一样-结构化数据容器。

但是,如果您确实需要代码以使用方式工作,那么我想到的最直接的方法是,使rvs函数可以直接访问的pa模块级变量与指向外部p的指针一起存储在结构。

#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;
}

暂无
暂无

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

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