简体   繁体   English

使用C进行数据封装的OOP编程

[英]OOP programming with data encapsulation in C

I tried to do data encapsulation in C based on this post here https://alastairs-place.net/blog/2013/06/03/encapsulation-in-c/ . 我尝试根据此处https://alastairs-place.net/blog/2013/06/03/encapsulation-in-c/上的这篇文章在C中进行数据封装。

In a header file I have: 在头文件中,我有:

#ifndef FUNCTIONS_H
#define FUNCTIONS_H

// Pre-declaration of struct. Contains data that is hidden
typedef struct person *Person;


void getName(Person obj);
void getBirthYear(Person obj);
void getAge(Person obj);
void printFields(const Person obj);

#endif

In ´functions.c´ I have defined the structure like that 在“ functions.c”中,我定义了这样的结构

#include "Functions.h"

enum { SIZE = 60 };

struct person
{
    char name[SIZE];
    int birthYear;
    int age;
};

pluss I have defined functions as well. 另外,我还定义了函数。

In main.c I have: 在main.c中,我有:

#include "Functions.h"
#include <stdlib.h>

int main(void)
{
    // Works because *Person makes new a pointer
    Person new = malloc(sizeof new);

    getName(new);
    getAge(new);
    getBirthYear(new);
    printFields(new);

    free(new);

    return 0;
}

Is it true, that when I use Person new , new is already pointer because of typedef struct person *Person; 是的,当我使用Person new ,由于typedef struct person *Person;new已经是指针了typedef struct person *Person; .

How is it possible, that linker cannot see the body and members that I have declared in my struct person 链接程序怎么可能看不到我在struct person声明的主体和成员

Is this only possible using pointer? 这只能使用指针吗?

Is the correct (and only) way to implement OOP prinicples in my case to make a different struct in functions.h like so: 在我的情况下,是实现OOP原理以在functions.h创建不同struct的正确(也是唯一方法),如下所示:

typedef struct classPerson
{   // This data should be hidden
    Person data;

    void (*fPtrGetName)(Person obj);
    void (*fPtrBirthYear)(Person obj);
    void (*fPtrGetAge)(Person obj);
    void (*fPtrPrintFields)(const Person obj);
} ClassPerson;

First of all, it is usually better to not hide pointers behind a typedef, but to let the caller use pointer types. 首先,通常最好不要将指针隐藏在typedef后面,而应让调用者使用指针类型。 This prevents all kinds of misunderstandings when reading and maintaining the code. 这样可以防止在阅读和维护代码时出现各种误解。 For example void printFields(const Person obj); 例如void printFields(const Person obj); looks like nonsense if you don't realize that Person is a pointer type. 如果您没有意识到Person是指针类型,则看起来像胡话。

Have I understood correctly, that when I use Person new, new is already pointer because of typedef struct person *Person;. 我是否正确理解,当我使用Person new时,由于typedef struct person * Person;,new已经是指针。

Yes. 是。 You are confused because of the mentioned typedef. 您由于提到的typedef而感到困惑。

How is it possible, that linker cannot see the body and members that I have declared in my ´struct person´? 链接程序怎么可能看不到我在“结构人”中声明的主体和成员?

The linker can see everything that is linked, or you wouldn't end up with a working executable. 链接器可以看到所有链接的内容,否则最终将无法正常运行。

The compiler however, works on "translation units" (roughly means a .c file and all its included headers). 但是, 编译器可在“翻译单元”上工作(大致意味着.c文件及其所有包含的头文件)。 When compiling the caller's translation unit, the compiler doesn't see functions.c, it only sees functions.h. 编译调用方的翻译单元时,编译器看不到functions.c,而只看到functions.h。 And in functions.h, the struct declaration gives an incomplete type . 并且在functions.h中,struct声明给出了一个不完整的type Meaning "this struct definition is elsewhere". 意思是“此结构定义在其他地方”。

Is this only possible using pointer? 这只能使用指针吗?

Yes, it is the only way if you want to do proper OO programming in C. This concept is sometimes called opaque pointers or opaque type . 是的,这是您要在C中进行适当的OO编程的唯一方法。此概念有时称为opaque指针opaque type

(Though you could also achieve "poor man's private encapsulation" though the static keyword. Which is usually not really recommended, since it wouldn't be thread-safe.) (尽管您也可以通过static关键字实现“穷人的私有封装”。通常不建议这样做,因为它不是线程安全的。)

Is the correct (and only) way to implement OOP prinicples in my case to make a different struct in functions.h like so: 在我的情况下,是实现OOP原理以在function.h中创建不同结构的正确(也是唯一方法),如下所示:

Pretty much, yeah (apart from the nit-pick about the mentioned pointer typedef). 是的,是的(除了关于提到的指针typedef的小提琴外)。 Using function pointers to the public functions isn't necessary though, although that's how you implement polymorphism. 尽管不必这样,但不必使用指向公共函数的函数指针,但这是实现多态的方法。

What your example lacks though is a "constructor" and "destructor". 您的示例缺少的是“构造函数”和“析构函数”。 Without them the code wouldn't be meaningful. 没有它们,代码将毫无意义。 The malloc and free calls should be inside those, and not done by the caller. malloc和free调用应该在其中,而不是由调用者完成。

With or without typedef, in C you hide data by declaring incomplete types. 使用或不使用typedef,在C中,您都通过声明不完整的类型来隐藏数据。 In /usr/include/stdio.h , you'll find fread (3) takes a FILE * argument: /usr/include/stdio.h ,您会发现fread (3)带有FILE *参数:

extern size_t fread (void *__restrict __ptr, size_t __size,
                     size_t __n, FILE *__restrict __stream) __wur;

and FILE is declared something like this: 并且FILE声明如下:

struct _IO_FILE;
typedef struct _IO_FILE FILE;

Using stdio.h you cannot define a variable of type FILE , because type FILE is incomplete: it's declared, but not defined. 使用stdio.h不能定义FILE类型的变量,因为FILE类型不完整:已声明但未定义。 But you can happily pass FILE * around, because all data pointers are the same size. 但是您可以愉快地传递FILE * ,因为所有数据指针的大小都相同。 You're just going to have to call fopen (3) to make it point to an open file. 您只需要调用fopen (3)使其指向打开的文件即可。

To partially define a type, as in your case: 部分定义类型,如您的情况:

struct classPerson
{   // This data should be hidden
    Person data;

    void (*fPtrGetName)(Person obj);
...
};

is a little trickier. 有点棘手。 First of all, you should have a really good reason, namely that two implementations of fPtrGetName are implemented. 首先,您应该有一个很好的理由,那就是实现了fPtrGetName两个实现。 Otherwise you're just building complexity on the altar of OOP. 否则,您只会在OOP的坛上增加复杂性。

A good example of a good reason is bind (2). 绑定 (2)是一个很好的理由。 You can bind a unix domain socket or a network socket, among others. 您可以绑定unix域套接字或网络套接字等。 Both types are represented by struct sockaddr , but that's just a stand-in type for struct sockaddr_un and struct sockaddr_in . 两种类型都由struct sockaddr表示,但这只是struct sockaddr_unstruct sockaddr_in的替代类型。 Functions that take struct sockaddr depend on the fact that all such structures start with the member sun_family , and branch accordingly. 采用struct sockaddr函数取决于所有这样的结构sun_family成员sun_family并相应地分支的事实。 Et voila , polymorphism: one function, many types. 等瞧 ,多态:一个功能,种类很多。

For an example of a struct full of function pointers, I recommend looking at SQLite . 对于充满函数指针的结构的示例,我建议查看SQLite Its API is loaded with structures to isolate it from the OS and let the user define plug-ins. 它的API加载有结构以将其与操作系统隔离,并让用户定义插件。

BTW, if I may say so, fPtrGetName is a terrible name. 顺便说一句,如果我可以这样说, fPtrGetName是一个糟糕的名字。 It's not interesting that it's a function pointer and (controversy!) "get" is noise on a function that takes no arguments. 它是一个函数指针,并且(有争议!)“ get”是不带参数的函数的噪音,这没什么有趣的。 Compare 相比

struct classPerson sargent;
sargent.fPtrGetName();
sargent.name();

Which would you rather use? 您宁愿使用哪个? I reserve "get" (or similar) for I/O functions; 我为I / O功能保留“ get”(或类似名称); at least then you're getting something, not just moving it from one pocket to another! 至少然后您会得到一些东西,而不仅仅是将它从一个口袋移到另一个口袋! For setting, in C++ I overload the function, so that get/set functions have the same name, but in CI wind up with eg set_name(const char name[]) . 为了进行设置,在C ++中,我重载了该函数,以便使get / set函数具有相同的名称,但在CI中,例如使用set_name(const char name[])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM