Bootstrap

C语言实现面向对象(一) 实现数据与接口的封装

封装的要素

C++中对一个类中的数据及接口有属性的描述, : public, private, protected
三种. 本文采用C语言的方式实现三种属性

实现private封装

private属性, 只有本类内部可使用, 即不对外开放, 也不对子类开放

C语言中声明的struct的属性都是public属性
如:

在c_elephant.h中声明

struct Elephant{
    int weight;
    int height;
    int width;
    char* name;
};

这样无法达到private的效果. 所以改为在.h中声明, 在.c中实现
c_elephant.h

#ifndef __C_ELEPHANT_H__
#define __C_ELEPHANT_H__
//仅做声明, 这样还有一个好处, 外部无法直接定义使用这个类, 
//必须调用我们手写的构造函数才能获得此类的对象.
typedef struct Elephant c_elephant_private;
#endif

c_elephant.c

#include "c_elephant.h"
#include "stdlib.h"
#include "string.h"

struct Elephant{
//private
    int weight;
    int height;
    int width;
    char* name;
};

这样无法在外部获取内部成员:
main.c

#include <stdio.h>
#include "c_elephant.h"
int main(int argc, char **argv)
{
    c_elephant_private elephant;				 //编译报错
    printf("name : %s\n", elephant.name);//编译报错
    return 0;
}

实现构造函数,析构函数,private方法, public方法

要使用c_elephant, 必须使用给定的构造函数来获得完整对象:

c_elephant.h

#ifndef __C_ELEPHANT_H__
#define __C_ELEPHANT_H__
//仅做声明, 这样还有一个好处, 外部无法直接定义使用这个类, 
//必须调用我们手写的构造函数才能获得此类的对象.
typedef struct Elephant c_elephant_private;

//声明构造函数
c_elephant_private *c_class_elephant_newElephant(int weight, int height, int width, const char* name, const int length);

//声明析构函数
void c_class_elephant_free(c_elephant_private* this_ptr);

//声明一个public方法, 返回对外私有数据
char* getName(c_elephant_private* this_ptr);
#endif

private方式是由本类能使用, 所以只需要在.c中对函数声明加static关键字即可.:
c_elephant.c

#include "c_elephant.h"
#include "stdlib.h"
#include "string.h"

struct Elephant{
//private
    int weight;
    int height;
    int width;
    char* name;
};

//private方法
static void c_elephant_initName(c_elephant* p, const char* name, const int length);

//实现构造函数
c_elephant_private *c_class_elephant_newElephant(int weight, int height, int width, const char* name, const int length)
{
    c_elephant_private *new_c_elephant = (c_elephant_private *) malloc(sizeof(c_elephant));
    c_elephant_initName(new_c_elephant, name, length);
    new_c_elephant->height = height;
    new_c_elephant->width = width;
    new_c_elephant->weight = weight;

    return new_c_elephant;
}

static void c_elephant_initName(c_elephant* this_ptr, const char* name, const int length)
{
    this_ptr->name = (char *) malloc((length + 1) * sizeof(char));
    memset(this_ptr->name, 0, length+1);
    strncpy(this_ptr->name, name, length);
}

//实现析构函数
void c_class_elephant_free(c_elephant_private* this_ptr)
{
	free(this_ptr->name);
	free(this_ptr);
	this_ptr = NULL;
}

char* c_class_elephant_getName(c_elephant_private* this_ptr)
{
	return this_ptr->name;
}

main.c

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

int main(int argc, char **argv)
{
//只有调用构造函数才能获取完整的对象
    c_elephant_private *c_class_elephant = c_class_elephant_newElephant(20, 30, 40, "1234", 4);
    printf("name : %s\n", c_class_elephant_getName(c_class_elephant));//输出1234
    c_elephant_free(c_class_elephant);
    return 0;
}

对public方法进行改造, 并添加public数据

在前面的代码中会发现, 此时的c_elephant_private添加所有的数据都是private, 无法添加public数据的, 以及对于public方法c_class_elephant_getName实际上是一个全局函数外部可见, 不符合封装思想

所以需要将 私有数据隔离开, 将public包含进类中

最终得到的代码

c_elephant.h

#ifndef __C_ELEPHANT_H__
#define __C_ELEPHANT_H__

//作为类中私有数据不对外开放
typedef struct Elephant c_elephant_private;


typedef struct class_elephant{
//作为对外开放的方法
    char* (*getName)(struct class_elephant* this_ptr);
    c_elephant_private *private_data;
} c_class_elephant;


//构造函数
c_class_elephant *c_class_elephant_newElephant(int weight, int height, int width, const char* name, const int length);

//析构函数
void c_class_elephant_free(c_class_elephant* this_ptr);
#endif

c_elephant.c

#include "c_elephant.h"
#include "stdlib.h"
#include "string.h"

struct Elephant{
//private
    int weight;
    int height;
    int width;
    char* name;
};

//private:
static void c_class_elephant_init_private(c_class_elephant *ptr, int weight, int height, int width, const char* name, const int length);
static void c_elephant_private_initName(c_elephant_private* p, const char* name, const int length);
//public 构造函数
c_class_elephant *c_class_elephant_newElephant(int weight, int height, int width, const char* name, const int length)
{
    
    c_class_elephant *new_c_class_elephant = (c_class_elephant *) malloc(sizeof(c_class_elephant));
    c_class_elephant_init_private(new_c_class_elephant, weight, height, width, name, length);
    new_c_class_elephant->getName = &c_class_elephant_private_getName;
    return new_c_class_elephant;
}

static void c_class_elephant_init_private(c_class_elephant *ptr, int weight, 
                                            int height, int width, 
                                            const char* name, const int length)
{
    ptr->private_data = (c_elephant_private *) malloc(sizeof(c_elephant_private));
    c_elephant_private_initName(ptr->private_data, name, length);
    ptr->private_data->height = height;
    ptr->private_data->width = width;
    ptr->private_data->weight = weight;
}

static void c_elephant_private_initName(c_elephant_private* this_ptr, const char* name, const int length)
{
    this_ptr->name = (char *) malloc((length + 1) * sizeof(char));
    memset(this_ptr->name, 0, length+1);
    strncpy(this_ptr->name, name, length);
}

//public 析构函数
void c_class_elephant_free(c_class_elephant* this_ptr)
{
    free(this_ptr->private_data->name);
    free(this_ptr->private_data);
    free(this_ptr);
    this_ptr = NULL;
}
//public 方法
char *c_class_elephant_private_getName(c_class_elephant* this_ptr)
{
    return this_ptr->private_data->name;
}

main.c

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

int main(int argc, char **argv)
{
    c_class_elephant* C_class_elephant = c_class_elephant_newElephant(20, 30, 40, "1234", 4);
    printf("name: %s\n", C_class_elephant->getName(C_class_elephant));
    c_class_elephant_free(C_class_elephant);
    return 0;
}
;