簡體   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