转载自:https://www.cnblogs.com/MikeZhang/p/CStructPrivateTest20180628.html
问题描述
C语言结构体定义中的变量默认是公有(Public)属性,如果实现成员变量的私有(Private)化?
有人说可以通过设置private使成员变量私有化,但如果这样做那就真的“私有化”了。因为一般结构体都没有成员函数(尽管可以有),因此如果设置成私有,那结构体外的所有函数都无法调用成员变量。其实我们这里说的私有是这些成员变量只能在结构体定义的源文件中使用,而不能被其他源文件使用。
解决方案
将结构体的定义放入源文件中实现,头文件只放声明。
将结构体的定义放入源文件中实现,头文件只放声明。
将结构体的定义放入源文件中实现,头文件只放声明。
比如有如下结构体(obj.h文件中定义,一般我们是这样做,在头文件中定义结构体):
struct Obj {
int id;
char *name;
};
typedef struct Obj Obj;
并定义如下变量:
Obj *o;
正常情况下,可以通过如下代码正常访问结构体中的变量:
printf("id : %d\n",o->id);
如果将结构体的定义放入源文件中,上述代码将会报如下错误:
error: dereferencing pointer to incomplete type
printf("id : %d\n",o->id);
如需外部访问,可通过在该源文件中添加相关接口实现,比如:
int get_obj_id(const Obj* o)
{
int ret = 0;
if(o)ret = o->id;
return ret;
}
这里做一下解释(我自己的理解):这其实跟C的编译有关,当结构体定义在头文件中时,因为我们主函数所在的源文件(main.cpp)会include这个头文件,而编译时相当于将该头文件的内容直接替换掉inlcude,那么main中或者其他include了该头文件的函数都可以访问结构体成员;但当结构体定义在源文件中时,编译时没有将结构体的定义复制过来,因此结构体成员在main中不可用,但因为在源文件中定义了结构体,因此该源文件下的所有函数均可以访问结构体成员。
main.cpp
#include "obj.h"
#include <cstdlib>
#include <cstdio>
int main()
{
Obj *o = nullptr;
o = create_obj(1, "test1");
// printf("id : %d\n", o->id);
printf("id: %d, name: %s\n", get_obj_id(o), get_obj_name(o));
release_obj(o);
printf("id: %d, name: %s\n", get_obj_id(o), get_obj_name(o));
}
obj.h
#ifndef UNTITLED1_OBJ_H
#define UNTITLED1_OBJ_H
typedef struct Obj Obj;
Obj* create_obj(int id, const char* name);
void release_obj(Obj* &o);
int get_obj_id(const Obj* o);
char* get_obj_name(const Obj* o);
#endif //UNTITLED1_OBJ_H
obj.cpp
#include "obj.h"
#include <cstring>
#include <cstdlib>
struct Obj {
int id;
char *name;
};
Obj* create_obj(int id, const char* name)
{
Obj* ret = new Obj();
if(ret) {
size_t len=0;
ret->id = id;
len = strlen(name);
ret->name = new char[len+1];
// ret->name = (char *) name;
if(ret->name)
{
memset(ret->name, 0, len + 1);//内存初始化
memcpy(ret->name, name, len);//内存赋值
}
}
return ret;
}
void release_obj(Obj* &o)//o为引用变量
{
if(!o) return ;
delete o->name;//释放内存
o->name = nullptr;//释放完毕要赋空指针
delete o;
o = nullptr;
}
int get_obj_id(const Obj* o)
{
int ret = 0;
if(o)ret = o->id;
return ret;
}
char* get_obj_name(const Obj* o)
{
char* ret;
if(o)ret = o->name;
return ret;
}
这里再浅谈一下new和delete,动态分配内存,为了解决内存泄漏问题,在使用完毕后要及时释放掉;
new调用有三种格式
格式1:指针变量名= new 类型标识符;
格式2:指针变量名= new 类型标识符(初始值);
格式3:指针变量名= new 类型标识符 [内存单元个数];
格式1和格式2都是申请分配某一数据类型所占字节数的内存空间,释放内存直接delete 指针变量名;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组,释放内存时delete []指针变量名。这里中括号[]不可省略,如果省略编译器就认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。