2012-05-03 40 views
6

Tôi đang học C, chủ yếu là bởi K & R, nhưng bây giờ tôi đã tìm thấy hướng dẫn về hướng đối tượng C pdf và bị mê hoặc. Tôi sẽ trải qua nó, nhưng kỹ năng/kiến ​​thức C của tôi có thể không liên quan đến nhiệm vụ. Đây là hướng dẫn: http://www.planetpdf.com/codecuts/pdfs/ooc.pdfCon trỏ không có dấu vết cho các con trỏ cấu trúc trong C

Câu hỏi của tôi xuất phát từ việc xem xét nhiều chức năng khác nhau trong vài chương đầu của pdf. Dưới đây là một trong số họ. (trang 14 của pdf)

void delete(void * self){ 
    const struct Class ** cp = self; 

    if (self&&*cp&&(*cp)->dtor) 
       self = (*cp)->dtor(self); 
    free(self); 

} 

dtor là một con trỏ hàm hủy. Nhưng kiến ​​thức về điều này không thực sự cần thiết cho câu hỏi của tôi.

  • Câu hỏi đầu tiên của tôi là tại sao hằng số ** cp? Là nó cần thiết hoặc chỉ được toàn diện để các nhà văn mã không làm bất cứ điều gì gây tổn hại do tai nạn?
  • Thứ hai, tại sao cp là con trỏ trỏ đến con trỏ (dấu hoa thị kép?). Lớp struct được định nghĩa ở trang 12 của pdf. Tôi không hiểu tại sao nó không thể là một con trỏ duy nhất, vì chúng tôi đang đúc con trỏ tự đến một con trỏ lớp, có vẻ như.
  • Thứ ba, làm thế nào là một con trỏ void được thay đổi thành một con trỏ Class (hoặc con trỏ trỏ đến một lớp-con trỏ)? Tôi nghĩ câu hỏi này cho thấy sự thiếu hiểu biết của tôi về C. Những gì tôi tưởng tượng trong đầu của tôi là một con trỏ void chiếm một lượng bộ nhớ, nhưng nó phải nhỏ hơn con trỏ Class, bởi vì một Class có rất nhiều "thứ" trong đó. Tôi biết một con trỏ void có thể được "đúc" để một loại con trỏ, nhưng tôi không hiểu làm thế nào, vì có thể không có đủ bộ nhớ để thực hiện điều này.

Cảm ơn trước

+3

@Joe Hoàn toàn không có gì sai với điều đó. Kỹ năng lập trình C tốt rất có giá trị và khó có thể tìm thấy những ngày này của các bản viết nhanh. – jman

+0

@ Joe - Tôi cho rằng nó có nghĩa là ANSI C89, cuốn sách K & R mới hơn được thảo luận. mã trong ví dụ không phải là K & R C IIRC. – Flexo

+0

Tôi đang đọc K & R ANSI (Tôi nghĩ từ năm 1989, như bạn đã nêu.) Ví dụ mã có dạng pdf OOC –

Trả lời

2

pdf thú vị.

Câu hỏi đầu tiên của tôi là tại sao hằng số ** cp? Có cần thiết hay chỉ cần là hoàn toàn để người viết mã không làm bất cứ điều gì gây tổn hại bởi vụ tai nạn ?

Đó là cần thiết để các nhà văn không làm bất cứ điều gì một cách tình cờ, có, và để giao tiếp một cái gì đó về bản chất của con trỏ và việc sử dụng nó vào đọc của mã này.

Thứ hai, tại sao cp là con trỏ trỏ tới con trỏ (dấu hoa thị kép?). Lớp cấu trúc được định nghĩa ở trang 12 của pdf. Tôi không hiểu tại sao nó không thể là một con trỏ duy nhất, vì chúng tôi đang đúc con trỏ tự đến một con trỏ lớp, có vẻ như.

Hãy nhìn vào định nghĩa của new() (pg 13) nơi con trỏ p được tạo ra (con trỏ cùng đó là thông qua như self-delete()):

void * new (const void * _class, ...) 
{ 
    const struct Class * class = _class; 
    void * p = calloc(1, class —> size); 
    * (const struct Class **) p = class; 

Vì vậy, 'p' được phân bổ không gian, sau đó dereferenced và gán một giá trị con trỏ (địa chỉ trong lớp học, điều này giống như dereferencing và gán cho một con trỏ int, nhưng thay vì một int, chúng tôi đang chỉ định một địa chỉ). Điều này có nghĩa là điều đầu tiên trong p là một con trỏ đến định nghĩa lớp của nó. Tuy nhiên, p được phân bổ không gian cho nhiều hơn chỉ là (nó cũng sẽ giữ dữ liệu cá thể của đối tượng). Bây giờ hãy xem xét delete() một lần nữa:

const struct Class ** cp = self; 
if (self&&*cp&&(*cp)->dtor) 

Khi cp bị hủy đăng ký, vì nó là con trỏ trỏ, bây giờ nó là một con trỏ. Con trỏ chứa gì? Một địa chỉ. Địa chỉ gì? Con trỏ đến định nghĩa lớp là ở đầu khối được trỏ tới bởi p.

Đây là loại thông minh, bởi vì p không thực sự là một con trỏ đến một con trỏ - nó có một đoạn lớn hơn của bộ nhớ được phân bổ trong đó có dữ liệu đối tượng cụ thể. Tuy nhiên, ngay từ đầu của khối đó là một địa chỉ (địa chỉ của định nghĩa lớp), do đó, nếu p được dereferenced vào một con trỏ (thông qua đúc hoặc cp), bạn có quyền truy cập vào định nghĩa đó. Vì vậy, định nghĩa lớp chỉ tồn tại ở một nơi, nhưng mỗi cá thể của lớp đó chứa tham chiếu đến định nghĩa. Có lý?Nó sẽ được rõ ràng hơn nếu p được gõ như một cấu trúc như thế này:

struct object { 
    struct class *class; 
    [...] 
}; 

Sau đó, bạn chỉ có thể sử dụng giống như p->class->dtor() thay vì mã hiện có trong delete(). Tuy nhiên, điều này sẽ lộn xộn và làm phức tạp bức tranh lớn hơn.

Thứ ba, cách một con trỏ void bị thay đổi thành con trỏ Lớp (hoặc con trỏ trỏ đến một lớp)? Tôi nghĩ câu hỏi này cho thấy sự thiếu nhất của tôi của sự hiểu biết về C. Điều tôi tưởng tượng trong đầu của tôi là một con trỏ void chiếm một số lượng bộ nhớ, nhưng nó phải nhỏ hơn Class con trỏ, bởi vì một lớp có rất nhiều "thứ" trong đó.

Con trỏ giống như int - có kích thước nhỏ, được đặt để giữ giá trị. Giá trị đó là địa chỉ bộ nhớ. Khi bạn dereference một con trỏ (thông qua * hoặc ->) những gì bạn đang truy cập là bộ nhớ tại địa chỉ đó. Nhưng vì các địa chỉ bộ nhớ đều có độ dài giống nhau (ví dụ 8 byte trên hệ thống 64 bit), tất cả các con trỏ đều có cùng kích thước bất kể loại. Đây là cách ma thuật của con trỏ đối tượng 'p' hoạt động. Để lặp lại: điều đầu tiên trong khối bộ nhớ p trỏ đến là một địa chỉ, cho phép nó hoạt động như một con trỏ trỏ đến một con trỏ, và khi được bỏ qua, bạn nhận được khối bộ nhớ chứa định nghĩa lớp, tách biệt với dữ liệu cá thể trong p.

0
  1. const được sử dụng để gây ra một lỗi biên dịch nếu mã cố gắng để thay đổi bất cứ điều gì trong đối tượng được trỏ tới. Đây là một tính năng an toàn khi lập trình viên chỉ dự định đọc đối tượng và không có ý định thay đổi nó.

  2. ** được sử dụng vì đó phải là những gì được chuyển cho hàm. Nó sẽ là một lỗi lập trình nghiêm trọng để tái tuyên bố nó như là một cái gì đó nó không phải là.

  3. Con trỏ chỉ đơn giản là địa chỉ. Trên hầu hết các CPU hiện đại, tất cả các địa chỉ đều có cùng kích thước (32 bit hoặc 64 bit). Thay đổi một con trỏ từ kiểu này sang kiểu khác không thực sự thay đổi giá trị. Nó nói về những gì ở địa chỉ đó như là một cách bố trí dữ liệu khác nhau.

1
  1. Trong trường hợp này, đó chỉ là một biện pháp phòng ngừa. Hàm không nên sửa đổi lớp (trên thực tế, không có gì nên có thể), do đó, việc đúc thành const struct Class * đảm bảo rằng lớp khó thay đổi hơn.

  2. Tôi không quen thuộc với thư viện C hướng đối tượng đang được sử dụng ở đây, nhưng tôi nghi ngờ đây là một thủ thuật khó chịu. Con trỏ đầu tiên trong self có lẽ là một tham chiếu đến lớp, vì vậy dereferencing self sẽ cung cấp cho một con trỏ đến lớp. Có hiệu lực, self luôn có thể được coi là struct Class **.

    Một sơ đồ có thể giúp đây:

     +--------+ 
    self -> | *class | -> [Class] 
         | .... | 
         | .... | 
         +--------+ 
    
  3. Hãy nhớ rằng tất cả các con trỏ chỉ là địa chỉ * Các loại một con trỏ không có mang về kích thước của con trỏ;. chúng rộng 32 hoặc 64 bit, tùy thuộc vào hệ thống của bạn, vì vậy bạn có thể chuyển đổi từ loại này sang loại khác bất kỳ lúc nào. Trình biên dịch sẽ cảnh báo bạn nếu bạn cố gắng chuyển đổi giữa các loại con trỏ mà không cần một diễn viên, nhưng các con trỏ void * luôn có thể được chuyển đổi thành bất kỳ thứ gì mà không có diễn viên, vì chúng được sử dụng trong suốt C để chỉ ra con trỏ "chung".

*: Có một số nền tảng kỳ lạ, điều này không đúng và các loại con trỏ khác nhau đôi khi có kích thước khác nhau. Nếu bạn đang sử dụng một trong số họ, tuy nhiên, bạn sẽ biết về nó. Trong tất cả các xác suất, bạn không.

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