[英]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中使用輕型對象方向,並且可能只是不使用它。
c語言中的面向對象代碼是完全可能的,但是從該語言中獲得的基本上是數據結構和指針。 其他一切都取決於您。
它有助於了解“ OO語言”通常已經在內部執行的操作。 在c ++中 ,您可能會像這樣編寫示例:
#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
是從哪里來的呢? 解決方案很簡單:將其作為參數傳遞。 您不會在代碼中看到它,因為c ++會為您處理它。 編譯時
double OneParamDist::rvs();
被翻譯成類似
double mangled_name_for_OneParamDist_rvs(OneParamDist *this);
在mangled_name_for_OneParamDist_rvs
是一些類似於c的 “普通”名稱的情況下,生成該名稱的規則特定於所使用的c ++編譯器。
有了這些知識,您可以很容易地將這個簡單的程序“翻譯”成普通的c :
#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
,只是為了避免混淆,在純c中使用this
完全合法。 但這當然會破壞與c ++的兼容性。
基本上,答案是:接受以下事實: 對於OO方法,您總是需要一個指向對象的指針作為參數 (按照慣例,第一個參數),並且c不會像c ++那樣自動為您傳遞該指針。 相應地創建代碼。
回到您在struct
中包含函數指針的想法,您什么時候應該這樣做? 看一下c ++的virtual
關鍵字。 它允許您從類中派生並讓該派生類的方法甚至通過指向基類的指針來調用。 如果您考慮一下,這需要在某個地方可以找到該方法的指針。 c ++通常使用vtable
對象(僅包含函數指針的結構)解決此問題。 如果滾動自己的“虛擬”方法,則將它們作為函數指針放入對象中是最直接的方法。 但是,只要您不需要虛方法,就不要使用函數指針,它們只是開銷而已,一無所獲。
值得一提的另一件事是, 如果您始終動態分配對象,則使用c可以達到完美的信息隱藏水平(如c ++ 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.