如何使struct成员私有化?

时间:2011-03-20 10:58:13

标签: c struct

我在头文件中定义了一个结构,如下所示:

typedef struct {
    void *data;
} point;

我想让其他人直接访问*数据,所以我想我会在.c文件中声明结构,并在头文件中使用类似extern typedef struct point;的内容。但是,这不起作用。

实现这一目标的最佳方式是什么?

8 个答案:

答案 0 :(得分:14)

在您的(公共)标头文件中:

typedef struct point point;

.c文件中:

struct point
{
    void *data;
};

请注意,代码的用户将无法再在堆栈上创建point,因为编译器不知道它有多大。您可能必须提供point_create()函数,该函数分配内存并将其地址返回给调用者。

答案 1 :(得分:4)

使用C ++

<小时/> 由于这里似乎不允许使用笑话,因此纯C版本。 正如另一位评论者所指出的,如果你真的想要保护你的内部用户免受你的Api用户的影响,那么你已经看过并使用过很多这样的Apis。这个Apis例如是Windows或Linux用户模式Apis。在那里,您可以创建您永远无法访问的内核对象。处理内核对象的Apis使用一个名为handle的合成构造,它不仅仅是指向你自己对象的指针,而是一个内核为你的对象存储相关元数据的数组的索引。 你也可以为你的Apis使用相同的想法。 例如,这是一个C风格的公共Api:

// Public.h
#include <stdlib.h>

typedef enum 
{
    None = 0,
    PointType = 1
} Types;

typedef int Handle;

Handle CreateType(Types type);
int    DeleteType(Handle object);

void IncrementX(Handle point);
void PrintPoint(Handle point);

正如您所看到的,您可以使用通用方法创建和删除在枚举中定义的对象。然后,使用该对象的方法将需要查找整数句柄以获取存储实际数据的元数据对象。 如果您管理的对象很小,则此设计效率不高,因为对于每个对象,需要存储对象类型,句柄值和指向实际数据的指针的第二个对象。 但是你会获得更强大的安全保障,例如

  • 类型安全
  • 无效的句柄很容易找到
  • 双重免费是不可能的,因为您可以管理元对象中的自由状态

您的Api的典型用法可能如下所示:

Handle h = CreateType(PointType);
IncrementX(h);
IncrementX(h);
PrintPoint(h);
DeleteType(h);

在private.cpp中有超级秘密实现,其中存在Handle查找数组和一些辅助方法:

// Private.C
#include "stdafx.h"
#include <stdlib.h>
#include <Windows.h>  // for ZeroMemory

#include "Public.h"

typedef struct 
{
    LPVOID pData;
    Types  type;
    Handle handle;
} HandleInfo;


typedef struct
{
    int x;
    int y;
} Point;

HandleInfo *pAllocated;
int HandleBuffer = 0xffff;
unsigned char bInit = 0;

HandleInfo *GetFreeHandle()
{
    int i;

    if( !bInit )
    {
        pAllocated = (HandleInfo *) malloc(sizeof(HandleInfo)*HandleBuffer);
        bInit = 1;
        ZeroMemory(pAllocated, sizeof(HandleInfo)*HandleBuffer);
    }

    for(i=0; i<HandleBuffer; i++)
    {
        HandleInfo *pInfo = (pAllocated+i);
        if( 0 == pInfo->handle  )
        {
            pInfo->handle = i+1;
            return pInfo;
        }
    }

    return NULL;
}

HandleInfo * GetHandleInfo(Handle h)
{
    if( h <= 0 || h >= HandleBuffer-1)
    {
        return NULL;
    }

    return (pAllocated+h-1);
}

Handle CreateType(Types typeId)
{
    HandleInfo *pInfo;

     pInfo = GetFreeHandle();
     if( NULL == pInfo )
     {
         return -1;
     }

     pInfo->type = typeId;
     switch(typeId)
     {
         case PointType:
             pInfo->pData = malloc(sizeof(Point));
             ZeroMemory(pInfo->pData, sizeof(Point));
         break;

     }

     return pInfo->handle;
}

int DeleteType(Handle object)
{
    HandleInfo *pInfo = GetHandleInfo(object);

    if( NULL == pInfo  )
    {
        return -1;
    }

    if( pInfo->handle != 0 )
    {
        free(pInfo->pData);
        pInfo->pData = NULL;
        pInfo->handle = 0;
        return 1;
    }
    else
    {
        return 0; // Handle was already closed
    }
}

void *GetObjectOfCorrectType(Handle object, Types type)
{
    HandleInfo *p = GetHandleInfo(object);
    if( p == NULL )
    {
        return NULL;
    }

    if( p->type != type)
    {
        return NULL; // handle has wrong object type
    }

    return p->pData;
}

void IncrementX(Handle point)
{
    Point *pPoint = (Point *) GetObjectOfCorrectType(point, PointType);
    if( pPoint == NULL )
    {
        return;
    }

    pPoint->x++;
}

void PrintPoint(Handle point)
{
    Point *pPoint = (Point *) GetObjectOfCorrectType(point, PointType);
    if( pPoint == NULL )
    {
        return;
    }

    printf("Point has x: %d y: %d", pPoint->x, pPoint->y);
}

此致,   Alois Kraus

答案 2 :(得分:3)

这是指向实现的指针 pimpl 成语。有关C ++的教程,请参阅http://en.wikibooks.org/wiki/C++_Programming/Idioms#Pointer_To_Implementation_.28pImpl.29,但该想法也适用于C语言。

答案 3 :(得分:2)

typedef struct {
    /* private members; don't access directly */
    void *data;
} point;

答案 4 :(得分:0)

您可以拥有单独的公共标头和私有标头文件。有些图书馆有这样的惯例:

  • Xt(X11) - &gt; header.hheaderP.h,例如:X11/Vendor.h vs X11/VendorP.h
  • Qt - &gt; header.h vs private/header_p.h,例如:qapplication.h vs private/qapplication_p.h

答案 5 :(得分:0)

如果您不想使用声明方法(例如,因为您希望库用户访问结构的其他成员),则通常会在私有成员前加上下划线,如下所示:

typedef struct {
    void * _data;
} point;

当然人们仍然可以访问_data,如果他们真的想要(就像人们可以通过在其包含之前添加#define private public来访问C ++中的私人数据),但这是他们自己的责任;至少你已经表明,如果他们希望你的图书馆能够按照自己的意愿行事,他们就不应该这样做。

答案 6 :(得分:0)

我使用这种方法让客户端在他的STACK中分配模块实例。

struct module_private {
    int data;
}

typedef uint8_t module_t [sizeof (struct module_private) ];

客户端将能够看到私有结构内容,但不能在不进行他不应该进行的强制转换的情况下访问它。

答案 7 :(得分:0)

使用以下解决方法:

#include <stdio.h>

#define C_PRIVATE(T)        struct T##private {
#define C_PRIVATE_END       } private;

#define C_PRIV(x)           ((x).private)
#define C_PRIV_REF(x)       (&(x)->private)

struct T {
    int a;

C_PRIVATE(T)
    int x;
C_PRIVATE_END
};

int main()
{
    struct T  t;
    struct T *tref = &t;

    t.a = 1;
    C_PRIV(t).x = 2;

    printf("t.a = %d\nt.x = %d\n", t.a, C_PRIV(t).x);

    tref->a = 3;
    C_PRIV_REF(tref)->x = 4;

    printf("tref->a = %d\ntref->x = %d\n", tref->a, C_PRIV_REF(tref)->x);

    return 0;
}

结果是:

t.a = 1
t.x = 2
tref->a = 3
tref->x = 4