在不使用函数指针的情况下替代C中引用虚拟表的方法。

时间:2018-08-03 07:49:14

标签: c oop polymorphism virtual-table

我通过使用Polymorphism (in C)中所述的虚拟表来利用C语言中的多态性,并且效果很好。

不幸的是,由于当前项目的限制,我无法在代码的某些部分中使用函数指针或对结构的引用。结果,我不能直接使用原始方法。

在上述方法中,基本“类/结构”具有指向虚拟表的成员。为了利用此指针,我决定将其替换为枚举,该枚举充当访问虚拟表的键。

它可以工作,但是我想知道是否是最好的解决方案。您能提出比我的建议更合适的替代方案吗?

/**
 * This example shows a common approach to achive polymorphism in C and an 
 * alternative that does NOT include a reference to function pointer in the 
 * base 
 * class.
 **/
#include<stdio.h>

//  some functions to make use of polymorphism
void funBase1()
{
    printf("base 1 \n");
}
void funBase2()
{
    printf("base 2 \n");
}
void funDerived1()
{
    printf("derived 1 \n");
}
void funDerived2()
{
    printf("derived 2 \n");
}


// struct to host virtual tables
typedef struct vtable {
    void (*method1)(void);
    void (*method2)(void);
}sVtable;

// enumerate to access the virtual table
typedef enum {BASE, DERIVED} eTypes;

// global virtual table used for the alternative solution
const sVtable g_vtableBaseAlternative[] = { 
    {funBase1, funBase2}, 
    {funDerived1, funDerived2},  };


// original approach that i cannot use
typedef struct base {
    const sVtable* vtable;
    int baseAttribute;
}sBase;

// alternative approach
typedef struct baseAlternative {
    const eTypes vtable_key;
    int baseAttribute;
}sBaseAlternative;


typedef struct derived {
    sBase base;
    int derivedAttribute;
}sDerived;

// original way to use 
static inline void method1(sBase* base)
{
    base->vtable->method1();
}

const sVtable* getVtable(const int key, const sVtable* vTableDic)
{
    return &vTableDic[key];
}

// Alternative to get a reference to the virtual table
static inline void method1Aternative(sBaseAlternative* baseAlternative)
{
    const sVtable* vtable;
    vtable = getVtable(baseAlternative->vtable_key, g_vtableBaseAlternative);
    printf("alternative version: ");
    vtable->method1();
}

int main() {

const sVtable vtableBase[] = { {funBase1, funBase2} };
const sVtable vtableDerived[] = { {funDerived1, funDerived2} };


sBase base = {vtableBase, 0 };
sBase derived = {vtableDerived, 1 };
sBaseAlternative baseAlternative = {DERIVED, 1 };

method1(&base);
method1(&derived);
method1Aternative(&baseAlternative);

}

2 个答案:

答案 0 :(得分:1)

  

我当前的项目不允许我使用函数指针或对结构的引用

您可以使用<div class="container"> <div class="row"> <p>Some text heree</p> <h1>Title</h1> <div class="col-md-4 col-xs-12"> <img src="https://www.dike.lib.ia.us/images/sample-1.jpg"class="img-responsive"/> </div> <p>Some text here </p> <button>CTA</button> </div> </div>数组(您喜欢的任何类型)来表示数据类型。例如,我倾向于使用T数组对我的数据结构进行序列化和反序列化以进行Web传输...例如,假设您使用unsigned charsprintf进行序列化和反序列化(您实际上不应该这样做,但是对于演示来说,它们是可以的)...代替sscanf参数,使用struct参数,然后使用{{ 1}}将该数据读取到局部变量,char *对其进行修改...这涵盖了没有引用sscanf的问题问题。

关于功能指针问题,您可以将所有功能合并为sprintf所标记的...字符串形式的结构 ...这是一个简单的示例(尚未完成),涉及两个的候选对象:一个长度前缀的字符串,该字符串使用两个字节进行编码C字符串行为的衍生的长度和种类,以及 C字符串的长度。

struct

C是一种图灵完整的编程语言,因此它当然可以用于模仿面向对象的多态性 ...,但是它在模仿过程性方面要好得多多态,甚至在某些情况下甚至是功能性多态 ...例如,您可以说switchenum { fubar_is_string, fubar_is_length_prefixed_string }; typedef unsigned char non_struct_str_class; size_t non_struct_strlen(non_struct_str_class *fubar) { size_t length = 0; switch (fubar++[0]) { case fubar_is_length_prefixed_string: length = fubar++[0]; length <<= 8; length += fubar++[0]; // carry through into the next case // to support strings longer than 64KB case fubar_is_string: if (!length) length = strlen(fubar); /* handle fubar as string */ } return length; } 使用的原始形式的参数多态类似于qsort的含义(甚至更类似于bsearch的惯用法)。

通过为所有标准浮点数类型提供通用的map宏,您也可以将filter用于有限的成功,例如作为the C11 standard does

_Generic

如果您打算采用模仿的路线,则预处理器特别有用。...您可能会对Klemens的C11书感兴趣。

答案 1 :(得分:-2)

  

我正在利用C语言中的多态性

您当然不会。您仅创建它的假体。 IMO对C语言中的对象进行模拟是最糟糕的解决方案。如果您更喜欢OOP范例-请使用OO语言。在这种情况下,C ++。

回答您的问题-如果没有函数指针,您将无法(理性地)解决问题。

我不鼓励人们进行OOP尝试,例如使用过程语言进行编程。它通常导致可读性差,容易出错并且很难维护程序。

为任务和方法选择正确的工具(语言是工具)。

就像用刀代替螺丝刀一样。可以,但是螺丝刀肯定会更好。