2010-09-29 27 views
11

Trong C++ và Java, cấu trúc dữ liệu có thể có các vùng private, publicprotected. Tôi muốn chuyển khái niệm này sang một chương trình ngôn ngữ C mà tôi đang viết.Phân vùng cấu trúc thành các phần riêng tư và công cộng?

Có thành ngữ nào để triển khai các con trỏ hàm và trường dữ liệu riêng tư hoặc được bảo vệ trong C struct không? Tôi biết rằng C struct s là công khai, tôi đang tìm một thành ngữ để giúp ẩn một số chi tiết triển khai và buộc người dùng sử dụng giao diện công khai.

Lưu ý: Ngôn ngữ đã được lựa chọn bởi các cửa hàng, vì vậy tôi đang mắc kẹt thực hiện khái niệm Object Oriented vào C.

Cảm ơn.

+0

Ẩn và đóng gói thông tin có trước OOD. –

Trả lời

20

Như bạn đã biết, bạn không thể thực hiện việc này. Tuy nhiên, có những thành ngữ sẽ cho phép một hiệu ứng tương tự.

C sẽ cho phép bạn thực hiện điều gì đó tương tự như thành ngữ "pimpl" trong thiết kế hướng đối tượng. Cấu trúc của bạn có thể có một con trỏ đục đến một cấu trúc được khai báo khác, hoạt động như dữ liệu riêng của cấu trúc.Các hàm hoạt động trên cấu trúc, lấy vị trí của các hàm thành viên, có thể có định nghĩa đầy đủ cho thành viên riêng và có thể sử dụng nó, trong khi các phần khác của mã không thể. Ví dụ:

Trong một tiêu đề, foo.h:

struct FooPrivate; 

    struct Foo { 
    /* public: */ 
     int x; 
     double y; 
    /* private: */ 
     struct FooPrivate* p; 
    }; 

    extern struct Foo* Foo_Create(); /* "constructor" */ 

    extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */ 

Trong việc thực hiện, foo.c:

struct FooPrivate { 
    int z; 
    }; 

    struct Foo* Foo_Create() 
    { 
    struct Foo* foo = malloc(sizeof(Foo)); 

    foo->p = malloc(sizeof(FooPrivate)); 

    foo->x = 0; 
    foo->y = 0; 
    foo->p->z = 0; 

    return foo; 
    } 

    void Foo_DoWhatever(struct Foo* foo) 
    { 
     foo->p->z = 4; /* Can access "private" parts of foo */ 
    } 

Trong một chương trình:

#include "foo.h" 

    int main() 
    { 
     struct Foo* foo = Foo_Create(); 

     foo->x = 100; /* Can access "public" parts of foo */ 
     foo->p->z = 20; /* Error! FooPrivate is not fully declared here! */ 

     Foo_DoWhatever(foo); /* Can call "member" function */ 

     return 0; 
    } 

Lưu ý cần sử dụng hàm "hàm tạo" để cấp phát bộ nhớ cho dữ liệu riêng tư. Rõ ràng bạn sẽ cần phải ghép nối điều này với một hàm "destructor" đặc biệt để xử lý dữ liệu riêng tư đúng cách.

Hoặc cách khác, nếu bạn muốn struct của bạn để không có trường công nào, bạn có thể làm cho toàn bộ struct đục, và chỉ có tiêu đề là cái gì đó giống như

struct Foo; 

    extern struct Foo* Foo_Create(); /* "constructor" */ 

    extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */ 

Với định nghĩa thực tế của các hàm struct Foo trong foo.c và getter và setter có sẵn cho bất kỳ thuộc tính nào bạn muốn cung cấp quyền truy cập trực tiếp.

+0

+1 Cảm ơn bạn đã nhắc tôi về thành ngữ Pimple. Tôi chưa bao giờ xem xét áp dụng nó vào ngôn ngữ C. –

+2

Trong C99, bạn có thể cân nhắc việc thay thế con trỏ thành 'struct FooPrivate' bằng một thành phần mảng linh hoạt' struct FooPrivate p [] '. Điều này sẽ ngăn cản người dùng giao diện vô tình tạo ra một bản sao nông của 'struct Foo', vì nó sẽ làm cho' struct Foo' trở thành một kiểu không đầy đủ. – caf

+0

@caf Bạn có thể mở rộng những gì thay đổi câu trả lời sẽ được yêu cầu để sử dụng linh hoạt mảng thành viên thay thế? – CMCDragonkai

0

Tôi cảm thấy tiếc cho bạn, bởi vì mashing các khái niệm OO khác nhau thành C thường giống như một hình vuông trong một lỗ tròn. Điều đó đang được nói, GObject có hỗ trợ cho các thành viên công cộng và tư nhân, nhưng đó là một trong những kiến ​​trúc yêu thích nhất của tôi trên trái đất. Nếu bạn không quan tâm đến hit hiệu suất nhỏ, bạn có thể làm một giải pháp đơn giản hơn - có cấu trúc phụ được lấp đầy với các thành viên riêng và có một con trỏ ẩn danh với cấu trúc đó từ cấu trúc chính (public).

+1

Tôi muốn tránh xa các gói thư viện vì điều này liên quan đến các vấn đề cấp phép và sự phát triển cần được hoàn thành nhanh chóng. –

+0

(a) GObject là một thư viện nguồn mở, mặc dù phải thừa nhận rằng thậm chí có thể trình bày các vấn đề về giấy phép; (b) cấu trúc riêng thứ cấp không yêu cầu thư viện. – Reinderien

9

Khái niệm đôi khi được sử dụng trong C là

// lib.h 
typedef struct { 
    int publicInt; 
    //... 
    char * publicStr; 
} Public; 

Public * getPublic(); 
int function(Public * public); 

// lib.c 

typedef struct { 
    Public public; 
    int privateInt; 
    // ... 
    char * privateStr 
} Private; 

static Private * getPrivate(); 

Public * getPublic() { return (Public*) getPrivate(); } 
int function(Public * public) { 
    Private * private = (Private *) public; 
    // ... 
} 

này sử dụng các thủ thuật tiêu chuẩn mà một con trỏ đến một cấu trúc có thể được trao đổi với một con trỏ đến phần tử đầu tiên trong một cấu trúc.

Nếu bạn muốn tất cả lĩnh vực của bạn để được tư nhân, nó thậm chí còn dễ dàng hơn:

// lib2.h 
typedef struct AllPrivate * Handle; 
Handle getHandle(); 
int function2(Handle handle); 

// lib2.c 
struct AllPrivate { /* ... */ } 

tập tin mà #include lib2.h sẽ không phàn nàn, vì chúng tôi chỉ sử dụng struct AllPrivate *, và tất cả các con trỏ đều giống nhau kích thước, do đó, trình biên dịch không cần phải biết nội bộ của struct AllPrivate.

Để làm một khu vực bảo vệ, bạn sẽ chỉ phải xác định

// include/public.h 
struct Public { /* ... */ } 
struct Public * getPublic(); 
int somePublicFunction(struct Public *); 

// dev/include/protected.h 
struct Protected { struct Public public; /* ... */ } 
struct Protected * getProtected(); 
int someProtectedFunction(struct Protected *); 

// dev/src/private.c 
struct Private { struct Protected protected; /* ... * /} 
struct Public * getPublic() { return (struct Public *) getPrivate(); } 
struct Public * getProtected() { return (struct Protected *) getPrivate(); } 
int somePublicFunction(struct Public * public) { 
    struct Private private = (struct Private *) public; 
    // ... 
} 
int someProtectedFunction(struct Protected * protected) { 
    struct Private private = (struct Private *) protected; 
    // ... 
} 

Sau đó, nó chỉ là một vấn đề đảm bảo rằng dev/include không thông qua xung quanh.

2

Đối với trường dữ liệu - không sử dụng chúng. Bạn có thể làm một số thủ đoạn như cho họ những cái tên điên rồ để ngăn cản việc sử dụng chúng, nhưng điều đó sẽ không ngăn cản mọi người. Cách thực sự duy nhất để làm điều đó là tạo một cấu trúc riêng khác được truy cập bởi một con trỏ rỗng bằng các hàm thư viện của bạn.

Đối với các chức năng riêng tư - hãy sử dụng các hàm static. Đặt tất cả các chức năng thư viện của bạn vào một tệp C và khai báo những tệp bạn muốn riêng tư là static và không đặt chúng trong bất kỳ tệp tiêu đề nào.

1

Thường theo quy ước, một thành viên riêng có thêm dấu gạch dưới trong tên của nó hoặc một số nội dung như được thêm vào _pri. Hoặc có thể là một bình luận. Kỹ thuật này không thực hiện kiểm tra được thực thi bởi trình biên dịch để đảm bảo không ai truy cập vào các trường đó một cách không thích hợp, nhưng đóng vai trò cảnh báo cho bất kỳ ai đọc tuyên bố struct rằng nội dung là chi tiết triển khai và không được nhìn trộm.

Một kỹ thuật phổ biến khác là để lộ cấu trúc của bạn dưới dạng loại không hoàn chỉnh. Ví dụ, trong tập tin tiêu đề của bạn, bạn có thể có:

struct my_struct; 

void some_function(struct my_struct *); 

Và trong việc thực hiện, hoặc một số tiêu đề nội bộ mà không thể truy cập cho người tiêu dùng của thư viện, bạn có:

struct my_struct 
{ 
    /* Members of that struct */ 
}; 

Bạn có thể cũng làm các thủ thuật tương tự với con trỏ void, có được truyền tới đúng chỗ trong phần "riêng tư" của mã. Cách tiếp cận này mất một số tính linh hoạt (bạn không thể có một cá thể được cấp phát ngăn xếp của một loại không xác định, ví dụ), nhưng điều đó có thể chấp nhận được.

Nếu bạn muốn có một hỗn hợp của các thành viên tư nhân và công cộng, bạn có thể làm điều tương tự như trên, nhưng lưu trữ con trỏ cấu trúc riêng tư như một thành viên của công chúng và để nó không đầy đủ trong người tiêu dùng công cộng của thư viện .

Mặc dù, điều này giới thiệu một số hướng dẫn, có thể làm giảm hiệu suất. Có một số (thường không cầm tay, nhưng sẽ làm việc trên các trình biên dịch hợp lý) gõ-punning thủ thuật bạn có thể sử dụng quá:

struct public_struct 
{ 
    int public_member; 
    int public_member2; 
    /* etc.. */ 
}; 

struct private_struct 
{ 
    struct public_struct base_members; 

    int private_member1; 
    int private_member2; 
}; 

void some_function(struct public_struct *obj) 
{ 
    /* Hack alert! */ 
    struct private_struct *private = (struct private_struct*)obj; 
} 

này cũng giả định rằng bạn không thể lưu trữ các đối tượng trên stack hay trong lưu trữ tĩnh hoặc nhận kích thước tại thời gian biên dịch.

Các vấn đề liên quan