2010-09-23 22 views
14

thể trùng lặp:
Typedef pointers a good idea?Được đánh máy là kiểu con trỏ được coi là thực hành không tốt?

Tôi đã nhìn thấy kỳ quặc này trong nhiều API Tôi đã sử dụng:

typedef type_t *TYPE; 

Quan điểm của tôi là khai báo một biến kiểu TYPE sẽ không làm cho nó rõ ràng rằng trong thực tế, một con trỏ được khai báo.

Bạn, như tôi, nghĩ rằng điều này mang lại nhiều sự nhầm lẫn? Điều này có nghĩa là để thực thi đóng gói, hoặc có những lý do khác không? Bạn có coi đây là một thực tế không tốt?

+0

Confusions Confusions! –

+1

Chỉ một trong những câu trả lời cho "Con trỏ Typedef là một ý tưởng hay?" ám chỉ đến sự tương tác vô dụng với 'const' - và câu trả lời đó được xếp hạng thấp. IMHO vấn đề 'const' là vấn đề quan trọng nhất với các loại typedefs, ... do đó không thực sự là một bản sao. –

+0

@ John: Tôi không đồng ý với bạn về kết luận. Bạn đã lưu ý rằng câu trả lời được thêm vào lâu sau khi câu hỏi được hỏi đã không nhận được nhiều sự chú ý, nhưng các câu hỏi vẫn cơ bản giống nhau. – dmckee

Trả lời

22

Nói chung, đó là một thực tế không tốt.Vấn đề quan trọng là nó không chơi tốt với const:

typedef type_t *TYPE; 
extern void set_type(TYPE t); 

void foo(const TYPE mytype) { 
    set_type(mytype); // Error expected, but in fact compiles 
} 

Để cho tác giả của foo() để diễn tả những gì họ thực sự có nghĩa là, các thư viện cung cấp TYPE cũng phải cung cấp CONST_TYPE:

typedef const type_t *CONST_TYPE; 

để foo() có thể có chữ ký void foo(CONST_TYPE mytype) và tại thời điểm này, chúng tôi đã rơi vào tình trạng bừa bãi.

Do đó một quy tắc của ngón tay cái:

Hãy typedefs của cấu trúc (đặc biệt là cấu trúc không đầy đủ), chứ không phải con trỏ đến những cấu trúc.

Nếu định nghĩa của các cấu trúc cơ bản không phải là để được công bố rộng rãi (mà thường là rất đáng khen ngợi), sau đó đóng gói cần được cung cấp bởi các struct là không đầy đủ, chứ không phải bởi typedefs bất tiện:

struct type_t; 
typedef struct type_t type_t; 

void set_type(type_t *); 
int get_type_field(const type_t *); 
+0

Thú vị rằng một số loại dường như không cần phiên bản const. Ví dụ, tôi đã không bao giờ cảm thấy bất kỳ mong muốn cho một 'const_pthread_t' mà tôi có thể gọi là' pthread_getschedparam' nhưng không phải là 'setschedparam'. 'const pthread_t' không đạt được điều đó (dĩ nhiên), giống như con trỏ đã gõ không đạt được nó trong ví dụ của bạn. Và 'ftell' lấy' FILE * ', không phải' const FILE * ', mặc dù tôi cho rằng nó phải là const, để hỗ trợ việc sử dụng hàm FILE * an toàn. Vì vậy, tôi nghĩ rằng vấn đề 'const' này là quan trọng/quyết định trong một số trường hợp, nhưng không phải lúc nào. Di sản tiền bối của C hiện qua, có thể? Hoặc chỉ lười biếng :-) –

+0

Thật vậy. Tôi nhìn xem có gì trong tiêu chuẩn đã được trang bị thêm với 'const FILE * ', nhưng ... không nhiều lắm. (Và có tôi hy vọng sẽ giúp bạn hiểu 'FILE *'! :-)) Có lẽ nó là để làm gì cho dù một cái gì đó cảm thấy giống như một loại nguyên tử - một trong những không viết nhiều 'const int' hoặc. Hoặc một cái gì đó của một vấn đề gà và trứng: nếu không có hàm hỏi nào lấy một 'const FILE *', tại sao bạn sẽ viết hàm 'foo()' của riêng bạn để lấy một - làm thế nào bạn sẽ sử dụng tham số của bạn? –

+1

'ftell' không thể lấy' const FILE * 'vì (trên POSIX hoặc bất kỳ việc thực hiện nào khác với luồng) nó phải có khóa mutex trên đối tượng' FILE' trừ khi vị trí hiện tại có thể thu được bằng một phép toán nguyên tử đơn lẻ. Toàn bộ ý tưởng của một 'const FILE *' khá nhiều vô nghĩa; nó không thể được sử dụng cho bất cứ điều gì. –

6

Thành ngữ phổ biến là hậu tố loại với _p để cho biết rằng đó là một con trỏ trong khi vẫn giữ lại chất lượng con trỏ.

Đôi khi, chỉ cần sử dụng loại con trỏ nếu cấu trúc trỏ đến không có sẵn công khai. Điều này giúp tạo điều kiện ẩn dữ liệu. I E.

typedef struct hidden_secret_object * object; 
void change_object(object foo); 

điều này cho phép bạn thay đổi cách hidden_secret_object được cấu trúc mà không vi phạm mã bên ngoài.

+2

Tại sao bạn cần con trỏ trong typedef để ẩn dữ liệu? 'typedef struct h_s_o object; void change_object (object * foo); 'sẽ thực hiện điều tương tự. Ví dụ có vẻ không liên quan đến câu hỏi. – schot

+0

Đôi khi một loại được dự định là mờ đục và trên các hệ thống khác nhau, một loại khác sẽ được sử dụng. Trên một số hệ thống loại này có thể lớn, vì vậy việc chuyển nó qua con trỏ sẽ hiệu quả hơn, trong khi trên các hệ thống khác, nó có thể đơn giản là một mã thông báo như một số nguyên. – nategoose

0

Có lẽ một cách để làm cho nó cụ thể hơn sẽ được gọi là con trỏ kiểu mới type_ptr hoặc một cái gì đó như thế:

typedef type_t* type_ptr; 
5

tôi không tìm thấy nó rõ ràng một trong hai. Tôi cũng không thích các loại viết hoa đầy đủ (tôi cố gắng đặt trước các loại này cho #defines).

Cách này giúp bạn dễ dàng đánh lừa chính mình bằng cách nghĩ rằng đó là một loại giá trị, trong khi chúng ta đang nói về một loại con trỏ. Kiểu con trỏ có thể hoàn toàn trừu tượng với các con trỏ thông minh, nhưng đó không phải là thông lệ phổ biến trong C.

Kết hợp (như đã đề cập trước đó) _p, _ptr, Con trỏ hoặc bất kỳ thứ gì dọc theo các dòng đó tạo ra sự rõ ràng; tăng khả năng đánh máy, điều đó đúng, nhưng sẽ ngăn cản bạn khỏi những sai lầm ngớ ngẩn (chẳng hạn như sử dụng '.' thay vì '->', ...) khiến bạn mất nhiều thời gian.

1

Tôi không nghĩ rằng đó là hành động xấu nếu đó là một con trỏ đến một loại không đầy đủ, hoặc nếu vì lý do nào khác mà người dùng không được mong đợi để dereference nó. Tôi chưa bao giờ hiểu được FILE*. Tôi cũng không nghĩ rằng đó là thực tế xấu nếu bạn đang làm điều đó bởi vì bạn có một số mức độ của indirection, và bạn muốn sử dụng nó trong các tình huống mà một số người trong số họ là không thích hợp. typedef char **argarray hoặc thứ gì đó.

Nếu người dùng được dự kiến ​​sẽ coi trọng nó thì trong C, tôi nghĩ tốt nhất nên giữ lại *. Trong C++, mọi người được sử dụng cho các kiểu do người dùng định nghĩa với quá tải operator*, chẳng hạn như vòng lặp. Trong C, điều đó không bình thường.

0

Các loại vòng loại lưu trữ như 'const' sẽ hoạt động khác với các con trỏ được đánh máy so với con trỏ 'tự nhiên'. Mặc dù đây không phải là điều tốt với 'const', nhưng nó có thể rất hữu ích với các lớp lưu trữ dành riêng cho trình biên dịch như "xdata". Tuyên bố như:

 
xdata WOKKA *foo; 

sẽ khai báo "foo" làm con trỏ, được lưu trữ trong lớp lưu trữ mặc định, cho WOKKA trong xdata. Tuyên bố:

 
xdata WOKKA_PTR bar; 
sẽ khai báo "thanh" thành con trỏ, được lưu trữ trong xdata, cho WOKKA trong bất kỳ lớp lưu trữ nào được chỉ định trong WOKKA_PTR. Nếu các thường trình thư viện sẽ mong đợi con trỏ tới những thứ có một lớp lưu trữ cụ thể, có thể hữu ích khi xác định các lớp lưu trữ đó trong các kiểu con trỏ.

0

Nó cắn tôi trong ass dịp:

for (vector<typedef_name_that_doesnt_indicate_pointerness_at_all>::iterator it; 
    it != v.end(); ++it) 
{ 
    it->foo(); // should have been written (*it)->foo(); 
} 

Thời gian duy nhất đó là có thể chấp nhận là nếu loại được hiểu là thực sự mờ đục và không truy cập trực tiếp ở tất cả. IOW, nếu ai đó sẽ phải dereference nó bên ngoài của một API, sau đó pointerness không nên được ẩn đằng sau một typedef.

4

Tùy thuộc vào những gì bạn đang cố gắng đạt được. Không có câu trả lời "có hoặc không" có ý nghĩa đối với câu hỏi của bạn theo cách được nêu.

  • Nếu bạn đang cố gắng để tạo ra một trừu tượng xử lý loại loại, ngụ ý rằng người dùng không phải biết hoặc quan tâm đến những gì đang ẩn đằng sau các loại, sau đó typedef-ing một kiểu con trỏ là hoàn toàn tốt đẹp. Toàn bộ vấn đề là hôm nay nó có thể là một kiểu con trỏ, và ngày mai nó có thể trở thành một kiểu số nguyên, và sau này nó có thể trở thành một thứ khác. Đây chính xác là kiểu gõ của con trỏ thường được sử dụng trong hầu hết các giao diện thư viện.

Bạn đang nói rằng đôi khi "không rõ ràng là con trỏ được khai báo". Nhưng theo mô hình sử dụng này, đó chính xác là điểm! Đó là được cho là là "không rõ ràng". Thực tế là các loại xảy ra là một con trỏ obfuscated là không có doanh nghiệp của bạn. Đó là điều mà bạn không cần biết và không phải dựa vào.

Ví dụ điển hình về kiểu sử dụng này là loại va_list trong thư viện chuẩn. Trong một số thực hiện nó có thể dễ dàng là một typedef cho một kiểu con trỏ. Nhưng đó là điều mà bạn không được biết hoặc dựa vào.

Ví dụ khác sẽ là định nghĩa của loại HWND trong API Windows. Nó là một typedef cho loại con trỏ là tốt, nhưng đó không phải là doanh nghiệp của bạn.

  • Một tình huống hoàn toàn khác nhau là khi bạn đang typedef-ing một loại con trỏ như một hình thức viết tắt, chỉ để làm cho các tờ khai ngắn hơn vì không cần phải gõ kí tự * mỗi lần. Trong trường hợp này thực tế là typedef là (và sẽ luôn luôn là) đứng cho một loại con trỏ được tiếp xúc với người dùng. Thông thường việc sử dụng này không phải là một thực hành lập trình tốt. Nếu người dùng sẽ muốn tạo bí danh để tránh nhập * mỗi lần, họ có thể tự làm điều đó.

Mô hình sử dụng này thường dẫn đến mã phức tạp hơn vì những lý do bạn đã đề cập trong OP của bạn.

Ví dụ về việc sử dụng sai số typedef này cũng có thể được tìm thấy trong Windows API. Các tên Typedef như PINT tuân theo chính xác mô hình sử dụng sai sót đó.

+0

+1 không thể nói tốt hơn –

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