如何在C中实现封装

时间:2011-05-13 20:45:40

标签: c encapsulation

我不确定我要做的是封装,但它是一个OOP概念。我正在实现二叉树,特别是插入函数:     

typedef struct __node* tree;
typedef struct __node { void* data; tree l,r; } node;

typedef struct {int (*cmp)(void* a,void* b); tree root;} avl_tree;

....

void tree_insert(tree node, tree* root, int (*cmp)(void* a,void* b))
{
  if (*root==NULL) { *root=node; return; }
  int c1 = cmp(node->data, (*root)->data);

  if (c1==-1) tree_insert(node, &((*root)->l), cmp);
}

tree tree_new_node(void*data){ tree a = malloc(...); ... return a; }

void avl_insert(void* data, avl_tree* a)
{
  tree_insert(tree_new_node(data), &(a->root), a->cmp);
  ....
}

该模块将通过avl_insert函数使用,该函数被赋予指向相关平衡树avl_tree的指针,该树包含指向原始树的指针以及指向比较器的指针。现在,显然应该调用tree inserttree_insert应该可以访问比较器以及我当前正在插入的节点。该函数在二叉树上行走,因此它自然是递归的。但是,如果我给它比较器和当前节点作为参数,它们将在每次递归调用时传递,这是不必要的,因为它们将始终是相同的。

我想避免这样做。我无法想出一个干净又好的解决方案。这些是我能想到的选择:

  1. 使用C ++类并使tree_insert函数成为avl_tree类的方法。然后它可以通过this指针访问比较器。这个解决方案的问题是我想使用C而不是C ++。此外,它不会消除当前节点参数的传递。

  2. 在函数(或全局数据)中使用静态成员。我不确定我是否可以在每次avl_insert电话时干净地初始化它们。此外,这个解决方案不是线程安全的。

  3. 现在我想起来,这似乎很容易在函数式编程语言中实现。我想知道,这是C的一个根本问题,还是我不知道该怎么做。实现这一目标的最简洁方法是什么?

    谢谢!


    在我想到Victor Sorokin的回答后,我读到了this指针,结果发现它是每个成员函数调用中的隐含参数。现在我想到它似乎是唯一合乎逻辑的解决方案。每次调用tree_insert函数都需要知道它所运行的结构的地址。即使在函数式语言中也不能避免使用额外的指针...

    一种可能的解决方案是在每个节点中保留指向主树结构的指针。

    所以这是一个基本的“问题”。

3 个答案:

答案 0 :(得分:1)

可用于实现封装的一种有趣方法是查看C ++编译器发出的汇编代码,然后将其转换为适当的C代码。

另一种更常规的方法是使用一些C对象库,如GLib。

我认为,这两种方法会产生类似的结果:)

顺便提一下,您提到的第一个选项与第二个选项一样容易受到线程问题的影响。 C ++中没有隐含的线程安全性。

“OOP”C代码 I 在Linux内核(文件系统层)中看到的主要是多态性,而不是封装。通过引入枚举可能的操作(作为函数的指针)的结构来实现多态性。然后创建各种“子类”,每个“子类”使用它自己的一组实现方法初始化该结构。

答案 1 :(得分:0)

您应该能够将该尾递归转换为迭代,并完全避免函数调用。像

这样的东西
void tree_insert(tree node,tree*root,int (*cmp)(void*a,void*b))
{
   tree* current = root;
   while (*current != NULL)
   {
     int c1=cmp(node->data,(*current)->data);
     if(c1==-1)current = &((*current)->l);
     else current = &((*current)->r);
   }
   *current=node;
 }  

答案 2 :(得分:0)

我的回答已经存在一个问题 - What does “static” mean in a C program?


您可以粗略地将C源文件作为类。关键字 static 使变量或函数只有内部链接,类似于经典OOP中的 private

foo.h中

#ifndef FOO_H
#define FOO_H

double publicStuff;

double getter (void);
void setter (double);
int publicFunction (void);

#endif

foo.c的

#include "foo.h"

static double privateStuff;

static int privateFunction (void)
{
    return privateStuff;
}

int publicFunction (void)
{
    return privateFunction();
}

double getter (void)
{
    return privateStuff;
}

void setter (double foo)
{
    privateStuff = foo;
}

的main.c

#include "foo.h"
#include <stdio.h>

static double privateStuff = 42;

static int privateFunction (void)
{
    return privateStuff;
}

int main (void)
{
    publicStuff = 3.14;

    setter(publicStuff);

    printf("%g %d %d\n", getter(), publicFunction(), privateFunction());

    return 0;
}