1

我通过使用多态性(在 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);

}
4

2 回答 2

2

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

您可以使用(任何您喜欢的类型)的数组T来表示数据类型。例如,我倾向于使用数组unsigned char来序列化和反序列化我的数据结构以进行网络传输......例如,假设您正在使用sprintfandsscanf进行序列化和反序列化(您实际上不应该这样做,但它们可以演示)...而不是struct参数,您使用char *参数,并且您使用sscanf将数据读取到局部变量,sprintf修改它...这涵盖了不允许引用struct问题。

关于函数指针问题,您可以将所有函数组合成一个switches on...一个字符串形式的标记结构...这是一个简单(但不完整)的示例,涉及两个候选:一个以长度为前缀的string使用两个字节来编码来自 C 字符串行为的长度和种类以及一个C 字符串

enum { 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;
}

C 是一种图灵完备的编程语言,所以它当然可以用来模仿面向对象的多态性……但它在模仿过程多态性,或者在某些情况下甚至是功能性多态性方面要好得多……举个例子,你可以说和使用类似于(甚至更类似于成语)的参数多态的原始形式。qsortbsearchmapfilter

您也可以使用_Generic有限的成功,例如C11 标准通过为所有标准浮点类型提供通用cbrt宏:

#define cbrt(X) _Generic((X),                                      \
                        long double: cbrtl,                        \
                        default: cbrt,                             \
                        float: cbrtf                               \
                        )(X)

如果您要走模仿路线,预处理器特别有用……您可能对 Klemens 的 C11 书感兴趣。

于 2018-08-03T08:33:38.713 回答
-2

我正在利用 C 中的多态性

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

回答你的问题 - 如果没有函数指针,你就无法做到(理智的方式)。

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

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

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

于 2018-08-03T08:32:51.090 回答