Sự khác biệt giữa ‘struct’ và ‘typedef struct’ trong C ++?

Trong bài viết DDJ này , Dan Saks giải thích một khu vực nhỏ nơi các con bọ có thể chui qua nếu bạn không gõ các cấu trúc của bạn (và các lớp!):

Nếu bạn muốn, bạn có thể tưởng tượng rằng C ++ tạo ra một typedef cho mỗi tên thẻ, chẳng hạn như

typedef

class

string string

;

Thật không may, điều này không hoàn toàn chính xác. Tôi ước nó đơn giản, nhưng không phải. C ++ không thể tạo ra các typedefs như vậy cho các cấu trúc, công đoàn hoặc enum mà không đưa ra sự không tương thích với C.

Ví dụ: giả sử một chương trình C khai báo cả hàm và trạng thái có tên cấu trúc:

int

status

();

struct

status

;

Một lần nữa, đây có thể là thực tiễn xấu, nhưng đó là C. Trong chương trình này, trạng thái (tự nó) đề cập đến chức năng; trạng thái cấu trúc đề cập đến các loại.

Nếu C ++ đã tự động tạo typedefs cho các thẻ, thì khi bạn biên dịch chương trình này thành C ++, trình biên dịch sẽ tạo:

typedef

struct

status status

;

Thật không may, tên loại này sẽ xung đột với tên hàm và chương trình sẽ không biên dịch. Đó là lý do tại sao C ++ không thể đơn giản tạo ra một typedef cho mỗi thẻ.

Trong C ++, các thẻ hoạt động giống như tên typedef, ngoại trừ việc một chương trình có thể khai báo một đối tượng, hàm hoặc liệt kê có cùng tên và cùng phạm vi với thẻ. Trong trường hợp đó, tên đối tượng, hàm hoặc tên liệt kê ẩn tên thẻ. Chương trình chỉ có thể tham chiếu tên thẻ bằng cách sử dụng lớp từ khóa, struct, union hoặc enum (nếu thích hợp) trước tên thẻ. Một tên loại bao gồm một trong những từ khóa này theo sau là một thẻ xác định kiểu công phu. Ví dụ, trạng thái cấu trúc và tháng enum là các chỉ định kiểu công phu.

Do đó, một chương trình C chứa cả hai:

int

status

();

struct

status

;

hành xử tương tự khi được biên dịch là C ++. Tình trạng tên một mình đề cập đến chức năng. Chương trình chỉ có thể tham chiếu loại bằng cách sử dụng trạng thái cấu trúc chỉ định kiểu công phu.

Vì vậy, làm thế nào điều này cho phép lỗi để len ​​vào các chương trình? Hãy xem xét chương trình trong Liệt kê 1 . Chương trình này định nghĩa một lớp foo với hàm tạo mặc định và toán tử chuyển đổi chuyển đổi một đối tượng foo thành char const *. Cách diễn đạt

p

=

foo

();

trong main nên xây dựng một đối tượng foo và áp dụng toán tử chuyển đổi. Báo cáo đầu ra tiếp theo

cout

<<

p

<<

'\n'

;

sẽ hiển thị lớp foo, nhưng nó không. Nó hiển thị chức năng foo.

Kết quả đáng ngạc nhiên này xảy ra vì chương trình bao gồm tiêu đề lib.h được hiển thị trong Liệt kê 2 . Tiêu đề này xác định một chức năng cũng được đặt tên là foo. Tên hàm foo ẩn tên lớp foo, vì vậy tham chiếu đến foo trong chính đề cập đến hàm chứ không phải lớp. main chỉ có thể tham chiếu đến lớp bằng cách sử dụng một specifier-type-specifier, như trong

p

=

class

foo

();

Cách để tránh sự nhầm lẫn như vậy trong suốt chương trình là thêm typedef sau cho tên lớp foo:

typedef

class

foo foo

;

ngay trước hoặc sau khi định nghĩa lớp. Typedef này gây ra xung đột giữa tên loại foo và tên hàm foo (từ thư viện) sẽ gây ra lỗi thời gian biên dịch.

Tôi biết không có ai thực sự viết những typedefs này như một vấn đề tất nhiên. Nó đòi hỏi rất nhiều kỷ luật. Vì tỷ lệ mắc các lỗi như trong Liệt kê 1 có lẽ là khá nhỏ, nên nhiều người không bao giờ gặp phải vấn đề này. Nhưng nếu một lỗi trong phần mềm của bạn có thể gây thương tích cho cơ thể, thì bạn nên viết các typedefs cho dù lỗi đó có khó xảy ra như thế nào.

Tôi không thể tưởng tượng được tại sao bất cứ ai cũng muốn ẩn tên lớp bằng một hàm hoặc tên đối tượng trong cùng phạm vi với lớp. Các quy tắc ẩn trong C là một sai lầm và chúng không nên được mở rộng cho các lớp trong C ++. Thật vậy, bạn có thể sửa lỗi, nhưng nó đòi hỏi kỷ luật lập trình thêm và nỗ lực không cần thiết.