2011-01-19 35 views
9

Tuyên bố từ chối trách nhiệm: Tôi là một người mới hoàn thành với C, nhưng tôi đã chơi với nó cố gắng bắt chước một số tính năng của các lớp học. Ok, Tôi biết rằng nếu tôi muốn đi theo cách đó tôi nên học C++, nhưng hãy xem xét sau đây một thí nghiệm nhỏ.Spicing C với các lớp học

Schreiner, trong sách Lập trình hướng đối tượng với ANSI-C gợi ý cách sử dụng con trỏ để có các đối tượng hướng đối tượng trong C. Tôi phải thừa nhận rằng tôi chỉ lướt qua cuốn sách, nhưng tôi không thích cách tiếp cận của anh ấy quá nhiều. Về cơ bản, ông sử dụng con trỏ chức năng để sắp xếp mà

func(foo); 

thực sự dẫn đến gọi

foo.methods->func(); 

nơi foo.methods là một cấu trúc chứa các con trỏ tới các chức năng. Điều tôi không thích trong phương pháp này là người ta phải có chức năng toàn cầu foo; cảm giác của tôi là điều này sẽ sớm dẫn đến lộn xộn: suy nghĩ hai đối tượng foobar, cả hai đều có phương thức func nhưng với số lượng tham số khác nhau.

Vì vậy, tôi đã cố gắng để có được một cái gì đó phù hợp hơn với khẩu vị của tôi. Lần thử đầu tiên là những điều sau (tôi bỏ qua các khai báo vì lợi ích ngắn gọn)

#include <stdio.h> 

//Instances of this struct will be my objects 
struct foo { 
    //Properties 
    int bar; 

    //Methods 
    void (* print)(struct foo self); 
    void (* printSum)(struct foo self, int delta); 
}; 

//Here is the actual implementation of the methods 
static void printFoo(struct foo self) { 
    printf("This is bar: %d\n", self.bar); 
} 

static void printSumFoo(struct foo self, int delta) { 
    printf("This is bar plus delta: %d\n", self.bar + delta); 
} 

//This is a sort of constructor 
struct foo Foo(int bar) { 
    struct foo foo = { 
     .bar = bar, 
     .print = &printFoo, 
     .printSum = &printSumFoo 
    }; 
    return foo; 
} 

//Finally, this is how one calls the methods 
void 
main(void) { 
    struct foo foo = Foo(14); 
    foo.print(foo); // This is bar: 14 
    foo.printSum(foo, 2); // This is bar plus delta: 16 
} 

Đây là một công việc không thuận tiện. Tuy nhiên, điều tôi không thích là bạn phải thêm chính đối tượng đó làm đối số đầu tiên. Với một số công việc tiền xử lý, tôi có thể làm tốt hơn một chút:

#include <stdio.h> 
#define __(stuff)  stuff.method(* stuff.object) 

//Instances of this struct will be my objects 
struct foo { 
    //Properties 
    int bar; 

    //Methods 
    //Note: these are now struct themselves 
    //and they contain a pointer the object... 
    struct { 
     void (* method)(struct foo self); 
     struct foo * object; 
    } print; 
}; 

//Here is the actual implementation of the methods 
static void printFoo(struct foo self) { 
    printf("This is bar: %d\n", self.bar); 
} 

//This is a sort of constructor 
struct foo Foo(int bar) { 
    struct foo foo = { 
     .bar = bar, 
     //...hence initialization is a little bit different 
     .print = { 
      .method = &printFoo, 
      .object = &foo 
     } 
    }; 
    return foo; 
} 

//Finally, this is how one calls the methods 
void 
main(void) { 
    struct foo foo = Foo(14); 
    //This is long and unconvenient... 
    foo.print.method(* foo.print.object); // This is bar: 14 
    //...but it can be shortened by the preprocessor 
    __(foo.print); // This is bar: 14 
} 

Điều này càng xa càng tốt. Vấn đề ở đây là nó sẽ không làm việc cho các phương thức với các đối số, như các macro tiền xử lý không thể lấy một số lượng các đối số biến. Tất nhiên người ta có thể xác định các macro _0, _1 và cứ như vậy theo số lượng đối số (cho đến khi một người bị mệt mỏi), nhưng đây không phải là cách tiếp cận tốt.

Có cách nào để cải thiện điều này và để C sử dụng cú pháp hướng đối tượng hơn không?

Tôi nên thêm thực sự Schreiner làm nhiều hơn những gì tôi đã nói trong cuốn sách của mình, nhưng tôi nghĩ rằng việc xây dựng cơ bản không thay đổi.

+0

Tôi sẽ sử dụng một cách tiếp cận vtable cho các chức năng, mà là tương tự như phương pháp thứ hai của bạn, ngoại trừ 'print' sẽ là một con trỏ. – leppie

+0

"cả hai có một phương thức func" ... func là tên của một trường con trỏ tới hàm trong một cấu trúc: không có lý do hàm toàn cầu mà nó trỏ đến phải được gọi đơn giản là "func". Tiền tố/postfix nó với một cái gì đó lớp cụ thể và bạn đã giải quyết vấn đề đó. –

+2

Macro tiền xử lý có thể lấy số lượng đối số thay đổi, tính từ 12 năm trước. –

Trả lời

2

Các khung công tác khác nhau đã tồn tại. Xem ví dụ http://ldeniau.web.cern.ch/ldeniau/html/oopc.html

+0

Cảm ơn bạn, điều này có vẻ rất thú vị.Tôi sẽ có một cái nhìn tại nguồn để xem cách hoạt động này – Andrea

+0

500 - Lỗi máy chủ nội bộ Đã xảy ra lỗi khi tải trang bạn yêu cầu: http: //ldeniau.web .cern.ch/ldeniau/html/oopc.html - Có sự cố với tài nguyên bạn đang tìm kiếm và không thể hiển thị. – lpapp

+0

@LaszloPapp, Laurent có giấy tờ (bạn có thể tìm thấy một hoặc hai tài liệu bằng cách tìm kiếm tài nguyên của nó tôi) và một số khung công tác được liên kết từ trang chủ của anh ấy. Có vẻ như một trong các khung công tác của anh ấy có sẵn tại http://sourceforge.net/projects/cos/ (cũng có một dự án githup có vẻ không hoạt động). – AProgrammer

1

Một cuốn sách (dưới dạng PDF) giải thích cách thực hiện, là object oriented programming in ANSI C Cũ (1993) nhưng vẫn chứa một số ý tưởng và mẹo hợp lệ, IMHO.

+0

Thực ra đây là cuốn sách tôi đề cập đến, nhưng tôi không thích cách tiếp cận của nó quá nhiều. Xem câu hỏi. – Andrea

+0

OK, tôi không thấy tham chiếu lúc đầu! –

1

Bạn đã xem Google Go chưa? Về cơ bản nó là một C hiện đại, nơi mọi thứ được thực hiện phần nào theo cách bạn đề nghị. Nó có đa hình tham số. Vì vậy, bạn không cần phải làm điều này:

static void printFoo(struct foo self) { 
    printf("This is bar: %d\n", self.bar); 
} 

Trong Go nó có thể được thực hiện theo cách này:

static void print(Foo self) { 
    printf("This is foo: %d\n", self.foo); 
} 

static void print(Bar self) { 
    printf("This is bar: %d\n", self.bar); 
} 

Trong Go Foo và Bar cũng sẽ là cấu trúc.Vì vậy, bạn về cơ bản trên đường đua samet là nhà thiết kế ngôn ngữ Go.

Đối với một cái nhìn tổng quan của Go thấy http://golang.org/doc/effective_go.html Đây là Go mô tả ngôn ngữ chính: http://golang.org/doc/effective_go.html

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