2010-02-02 30 views
52

thể Duplicates:
Can you write object oriented code in C?
Object Oriented pattern in C ?Object định hướng trong C

Tôi nhớ đọc một thời gian trước đây về một người nào đó (Tôi nghĩ đó là Linus Torvalds) nói về cách C++ là một ngôn ngữ khủng khiếp và cách bạn có thể viết các chương trình hướng đối tượng với C. Khi có thời gian để phản ánh, tôi không thực sự thấy cách tất cả các khái niệm hướng đối tượng chuyển sang C. Một số điều khá rõ ràng. Ví dụ:

  1. Để mô phỏng chức năng của thành viên, bạn có thể đặt con trỏ hàm trong cấu trúc.
  2. Để thi đua đa hình, bạn có thể viết một hàm mang theo một số biến của tham số và làm một số voodoo tùy thuộc vào, nói rằng, các sizeof tham số (s)

Làm thế nào bạn sẽ bắt chước đóng gói, thừa kế mặc dù?

Tôi cho rằng đóng gói có thể được mô phỏng bằng cách có cấu trúc lồng nhau lưu trữ thành viên riêng tư. Nó sẽ là khá dễ dàng để có được xung quanh, nhưng có lẽ có thể được đặt tên PRIVATE hoặc một cái gì đó bằng nhau rõ ràng để báo hiệu rằng nó không có nghĩa là để được sử dụng từ bên ngoài cấu trúc. Thế còn thừa kế thì sao?

+4

nó là khá khó chịu IMO. Đó là lý do tại sao chúng tôi có các ngôn ngữ cấp cao hơn. – ChaosPandion

+4

Dupes: http://stackoverflow.com/questions/351733?tab=votes#tab-top, http://stackoverflow.com/questions/1201521/object-oriented-pattern-in-c –

+0

Sử dụng sai mục đích ngăn xếp luồng xây dựng hồ sơ trực tuyến. Tại sao địa ngục bất cứ ai muốn làm điều này? –

Trả lời

75

Bạn có thể thực hiện đa hình với chức năng thông thường và bảng ảo (vtables).Đây là một hệ thống khá gọn gàng mà tôi đã phát minh (dựa trên C++) cho một bài tập lập trình: alt text

Các nhà xây dựng cấp phát bộ nhớ và sau đó gọi hàm init của lớp nơi bộ nhớ được khởi tạo. Mỗi hàm init cũng nên chứa một cấu trúc vtable tĩnh có chứa các con trỏ hàm ảo (NULL cho thuần ảo). Các hàm init của lớp bắt nguồn gọi hàm init của siêu lớp trước khi thực hiện bất cứ điều gì khác. Một API rất đẹp có thể được tạo bằng cách thực hiện các hàm bao hàm ảo (không bị nhầm lẫn với các hàm được chỉ ra bởi vtables) như sau (thêm static inline ở phía trước nó, nếu bạn làm điều này trong tiêu đề):

int playerGuess(Player* this) { return this->vtable->guess(this); } 

Độc thừa kế có thể được thực hiện bằng cách lạm dụng cách bố trí nhị phân của một cấu trúc: alt text

ý rằng đa kế thừa là Messier như sau đó bạn thường cần phải điều chỉnh giá trị con trỏ khi đúc giữa các loại thuộc hàng giáo phẩm.

Dữ liệu loại cụ thể khác cũng có thể được thêm vào các bảng ảo. Ví dụ bao gồm thông tin loại thời gian chạy (ví dụ: nhập tên dưới dạng chuỗi), liên kết với vtable siêu lớp và chuỗi hủy. Bạn có thể muốn destructors ảo, nơi destructor lớp có nguồn gốc demotes đối tượng để siêu lớp của nó và sau đó đệ quy gọi destructor của đó và như vậy, cho đến khi destructor lớp cơ sở đạt và cuối cùng giải phóng struct.

Đóng gói được thực hiện bằng cách xác định cấu trúc trong player_protected.h và thực hiện các chức năng (được chỉ ra bởi vtable) trong player_protected.c và tương tự cho các lớp dẫn xuất, nhưng điều này khá vụng về và làm giảm hiệu suất (như ảo đóng gói không thể được đặt vào tiêu đề), vì vậy tôi sẽ khuyên bạn nên chống lại nó.

+7

+1 cho hình ảnh đẹp. –

+0

@Tronic - Tôi không chắc liệu bạn có phải là một người Java hay không, nhưng cách mà HotSpot JVM thực hiện OO rất giống với điều này. Tất nhiên, Java được thừa hưởng duy nhất để thực hiện, do đó, điều này giải quyết nhiều đau đầu thừa kế. Tôi có thể phải điểm C/C++ guys ở đây nếu tôi cần giải thích Java/JVM OO cho họ trong tương lai. – kittylyst

+4

C++ cũng thực hiện OOP như thế này. – Tronic

2

thư viện gtk và glib sử dụng macro để truyền đối tượng sang các loại khác nhau.
add_widget (GTK_WIDGET (myButton));
Tôi không thể nói cách thực hiện nhưng bạn có thể đọc nguồn của họ để tìm hiểu chính xác cách thức thực hiện.

+0

điều quan trọng là nó không biên dịch nếu bạn quên macro. –

+0

Khuôn khổ GObject được sử dụng bởi Glib/GTK được ghi lại ở đây: http://library.gnome.org/devel/gobject/unstable/ –

+0

Chúng có thể thoát khỏi điều này rất dễ dàng vì chúng không cho phép đa thừa kế. Bất kể bạn nhập vào loại gì, địa chỉ cơ sở của một đối tượng luôn giống nhau. "Thừa kế" chỉ trở thành tuyên bố một cấu trúc mới, nơi các thành viên đầu tiên là lớp cơ sở và các thành viên mới cho lớp đó đến sau đó. Vì vậy, bạn có thể truyền đến lớp cơ sở và địa chỉ vẫn giữ nguyên. – asveikau

1

Để có ví dụ tuyệt vời về lập trình hướng đối tượng trong C, hãy xem xét nguồn của POV-Ray từ vài năm trước - phiên bản 3.1g đặc biệt tốt. "Đối tượng" được cấu trúc với con trỏ chức năng, tất nhiên. Các macro được sử dụng để cung cấp các phương thức và dữ liệu cốt lõi cho một đối tượng trừu tượng và các lớp dẫn xuất là các cấu trúc bắt đầu với macro đó. Tuy nhiên, không có nỗ lực để giải quyết riêng tư/công khai. Những thứ cần xem trong tệp .h và chi tiết triển khai nằm trong các tệp .c, chủ yếu, ngoại trừ rất nhiều ngoại lệ.

Có một số thủ thuật gọn gàng mà tôi không thấy làm thế nào có thể được chuyển sang C++ - chẳng hạn như chuyển đổi một lớp thành một lớp khác nhưng tương tự trên một con ruồi chỉ bằng cách gán lại con trỏ hàm. Đơn giản cho các ngôn ngữ động ngày nay. Tôi quên chi tiết; Tôi nghĩ rằng nó có thể là giao lộ CSG và các đối tượng công đoàn.

http://www.povray.org/

+0

Việc thực hiện đóng gói sẽ khó khăn trong bất kỳ thiết lập "OO C" nào (bạn có thể chỉ cần làm cho nó là một vấn đề tiềm ẩn), nhưng vâng, tiền đề cơ bản là có một loạt các con trỏ hàm trong đối tượng (hoặc một con trỏ đến một loạt các con trỏ hàm, hoặc một cái gì đó như thế) và gọi chúng. – fennec

+2

@fennec: đó thực sự là ngược, vì C++ yêu cầu bạn đặt chi tiết thực hiện của một lớp vào một tệp tiêu đề công khai, trong khi c thì không. –

28

Bạn đã đọc "kinh thánh" về chủ đề chưa? Xem Object Oriented C ...

+0

@ tur1ng - do tai nạn - Tôi có phần mở rộng PDF trong Chrome được cài đặt:> –

+0

Cảm ơn bài đăng. Tên của tác giả tiếp theo là gì? Nó không được liệt kê trong tập tin. – user1527227

1

Một chút lịch sử thú vị. Cfront, mã C C++ đầu ra thực hiện gốc và sau đó yêu cầu trình biên dịch C thực sự xây dựng mã cuối cùng. Vì vậy, bất cứ điều gì có thể được thể hiện trong C + + có thể được viết như C.

+1

Đúng. Tuy nhiên, mã C cfront outputted không phải là mã mà bất kỳ lập trình viên nào của con người đã từng viết (ví dụ người dùng nói trước). –

+0

@Neil: True. Tuy nhiên nó vẫn có nghĩa là bất kỳ mã C++ nào cũng có thể được chuyển đổi thành mã C - đó là những gì mà OP tự hỏi và có lẽ Linus ám chỉ đến điều gì. – slebetman

1

Một cách để xử lý thừa kế là do có cấu trúc lồng nhau:

struct base 
{ 
    ... 
}; 

void method_of_base(base *b, ...); 

struct child 
{ 
    struct base base_elements; 
    ... 
}; 

Sau đó bạn có thể thực hiện cuộc gọi như thế này:

struct child c; 
method_of_base(&c.b, ...); 
8

Làm thế nào bạn sẽ bắt chước đóng gói, thừa kế mặc dù?

Thực ra, đóng gói là phần dễ nhất. Đóng gói là một thiết kế triết lý, nó không có gì cả để làm với ngôn ngữ và tất cả mọi thứ để đến với cách bạn suy nghĩ về các vấn đề.

Ví dụ, api Windows FILE được đóng gói hoàn toàn. Khi bạn mở một tệp, bạn lấy lại một đối tượng mờ có chứa tất cả thông tin trạng thái cho tệp 'đối tượng'. Bạn đưa tay này lại cho mỗi tập tin io apis. Việc đóng gói thực sự là nhiều hơn tốt hơn so với C++ vì không có tệp tiêu đề công khai mà mọi người có thể xem và xem tên của các biến riêng tư của bạn.

Thừa kế khó hơn, nhưng không cần thiết để mã của bạn được định hướng đối tượng. Trong một số cách tập hợp là tốt hơn so với thừa kế anyway, và tập hợp chỉ là dễ dàng trong C như trong C + +. xem ví dụ this.

Để trả lời Neil, hãy xem Wikipedia để giải thích lý do tại sao thừa kế không cần thiết cho đa hình.

Giờ cũ chúng tôi đã viết mã định hướng đối tượng năm trước khi trình biên dịch C++ có sẵn, đó là bộ óc không phải là bộ công cụ.

+1

Nếu bạn không có thừa kế, bạn không có tính đa hình thời gian chạy và bạn chưa có OO. –

+2

@Neil: Tôi không thấy bạn mắc lỗi thường xuyên, nhưng trong điều này bạn hoàn toàn sai. thừa kế không phải là tính năng _necessary_ của OO. bạn có thể nhận được đa hình thông qua tập hợp/phái đoàn. –

+0

Bạn sẽ không xem xét Ada83 hướng đối tượng, nhưng nó có tất cả các tính năng ngoại trừ thừa kế và đa hình. Tôi đồng ý với Neil. Nói cách khác là hoàn toàn từ bỏ toàn bộ mục đích, mô hình OOP đã được phát minh: tái sử dụng mã. –

5

Khung CoreFoundation C dựa trên C của Apple đã thực sự được viết sao cho "đối tượng" của nó có thể tăng gấp đôi làm đối tượng trong Mục tiêu-C, một ngôn ngữ OO thực tế. Một tập hợp con khá lớn của khung công tác là nguồn mở trên trang web của Apple là CF-Lite. Có thể là một nghiên cứu điển hình hữu ích trong một khung hệ điều hành chính được thực hiện theo cách này.

0

Bạn có thể muốn xem xét Mục tiêu-C, đó là khá nhiều những gì nó làm. Nó chỉ là một front-end mà biên dịch mã Oive Objective-C để C.

2

Hãy xem cách lớp VFS hoạt động trong hạt nhân Linux cho ví dụ về mẫu thừa kế. Các hoạt động tệp cho các hệ thống tệp khác nhau "kế thừa" một tập hợp các hàm hoạt động chung của tệp (ví dụ: generic_file_aio_read(), generic_file_llseek() ...), nhưng có thể ghi đè chúng bằng cách triển khai của riêng chúng (ví dụ: ntfs_file_aio_write()).

5

Từ độ cao cao hơn một chút và xem xét vấn đề khá cởi mở hơn là chính OOP có thể đề xuất, Lập trình hướng đối tượng có nghĩa là suy nghĩ về các đối tượng như dữ liệu có chức năng liên quan. Nó không có nghĩa là một chức năng phải được thể chất gắn liền với một đối tượng vì nó là ngôn ngữ phổ biến có hỗ trợ mô hình của OOP, ví dụ trong C++:

struct T 
{ 
    int data; 
    int get_data() const { return data; } 
}; 

tôi sẽ đề nghị để có một cái nhìn sâu hơn về GTK+ Object and Type System. Đó là một ví dụ tuyệt vời của OOP thực hiện trong ngôn ngữ lập trình C:

GTK + thực hiện riêng hệ thống đối tượng tùy chỉnh của nó, trong đó cung cấp tiêu chuẩn hướng đối tượng tính năng như thừa kế và chức năng ảo

Các hiệp hội cũng có thể là hợp đồng và thông thường.

Về kỹ thuật đóng gói và ẩn dữ liệu, phổ biến và đơn giản có thể là Opaque Pointer (hoặc Loại dữ liệu mờ) - bạn có thể truyền nó xung quanh nhưng để tải hoặc lưu trữ bất kỳ thông tin nào, bạn phải gọi hàm liên quan. nói chuyện với đối tượng ẩn đằng sau con trỏ đục.

Một số khác, tương tự nhưng khác nhau là Shadow Data type - hãy kiểm tra liên kết này nơi Jon Jagger giải thích tuyệt vời về kỹ thuật không được biết đến này.

+0

Vậy làm thế nào để thực hiện một giao diện mà không thực sự gắn các phương thức vào đối tượng? Tôi nghĩ rằng con trỏ đục của bạn có thể giải quyết vấn đề này, nhưng tôi không hiểu làm thế nào mà không có một ví dụ. Ngôn ngữ lập trình Go chia sẻ triết lý rất chung chung của bạn về OOP nhưng họ vẫn buộc phải cho phép các phương thức được gắn vào các loại để tạo điều kiện cho các giao diện. – weberc2

2

Chắc chắn nhìn vào Objective-C.

typedef struct objc_object { 
    Class isa; 
} *id; 

typedef struct objc_class { 
    struct objc_class *isa; 
    struct objc_class *super_class 
    const char *name; 
    long version; 
    long info 
    long instance_size; 
    struct objc_ivar_list *ivars; 
    struct objc_method_list **methodLists; 
    struct objc_cache *cache; 
    struct objc_protocol_list *protocols; 
} *Class; 

Như bạn có thể thấy, thông tin thừa kế, cùng với các chi tiết khác được tổ chức trong cấu trúc lớp (thuận tiện lớp cũng có thể được coi là đối tượng).

Objective-C bị theo cùng cách thức như C++ với gói gọn trong đó bạn cần khai báo biến của bạn một cách công khai. Thẳng C là linh hoạt hơn nhiều trong đó bạn chỉ có thể trả về con trỏ void rằng chỉ có mô-đun của bạn có quyền truy cập nội bộ, do đó, trong đó tôn trọng đóng gói là tốt hơn nhiều.

Tôi đã từng viết một chương trình vẽ C theo phong cách OO cơ bản như là một phần của khóa học đồ họa - tôi không đi xa như khai báo lớp, tôi chỉ đơn giản sử dụng con trỏ vtable làm phần tử đầu tiên của cấu trúc và được thực hiện bằng tay thừa kế mã hóa.Điều gọn gàng về việc chơi xung quanh với vtables ở mức thấp như vậy là bạn có thể thay đổi hành vi của lớp trong thời gian chạy bằng cách thay đổi một vài con trỏ, hoặc thay đổi trên các lớp đối tượng động. Đó là khá dễ dàng để tạo ra tất cả các loại của các đối tượng lai, đa kế thừa giả vv

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