Tại sao chúng ta cần đưa các thành viên tư nhân vào tiêu đề?


62

Các biến riêng là một cách để che giấu sự phức tạp và chi tiết triển khai cho người dùng của một lớp. Đây là một tính năng khá hay. Nhưng tôi không hiểu tại sao trong c ++, chúng ta cần đặt chúng vào tiêu đề của một lớp. Tôi thấy hai nhược điểm khó chịu này:

  • Nó đóng tiêu đề từ người dùng
  • Nó buộc biên dịch lại tất cả các thư viện máy khách bất cứ khi nào nội bộ được sửa đổi

Có một lý do khái niệm đằng sau yêu cầu này? Có phải chỉ để giảm bớt công việc ra khỏi trình biên dịch?


bạn có thể khai báo một cấu trúc trống trong tiêu đề nhưng sau đó bạn chỉ có thể sử dụng các con trỏ tới một cấu trúc như vậy khi bạn sử dụng nó (và bạn không thể phân bổ một cấu trúc)
ratchet freak

3
@ratchetfreak: Không, trống ( struct foo{};) không được phép, nhưng khai báo chuyển tiếp ( struct foo;) là.
MSalters

@MSalters đó là những gì tôi muốn nói
ratchet freak

1
Hãy để tôi thêm một nhược điểm: * Viết các tiêu đề chức năng riêng tư trong tệp .h là một sự lãng phí rất lớn thời gian. (quên các lớp học bạn bè trong giây lát)
Jonny

Câu trả lời:


68

Đó là bởi vì trình biên dịch C ++ phải biết kích thước thực tế của lớp để phân bổ đúng dung lượng bộ nhớ khi khởi tạo. Và kích thước bao gồm tất cả các thành viên, cũng là những người riêng tư.

Một cách để tránh điều này là sử dụng thành ngữ Pimpl , được giải thích bởi Herb Sutter trong loạt bài của ông về Tuần lễ # 24# 28 .

Cập nhật

Thật vậy, điều này (hay nói chung hơn là sự phân biệt và tiêu đề tệp nguồn #include) là một trở ngại lớn trong C ++, được kế thừa từ C. Quay lại thời C ++ C được tạo ra, chưa có kinh nghiệm phát triển phần mềm quy mô lớn, trong đó điều này bắt đầu gây ra vấn đề thực sự Các bài học rút ra từ đó được các nhà thiết kế của các ngôn ngữ mới chú ý hơn, nhưng C ++ bị ràng buộc bởi các yêu cầu tương thích ngược, khiến cho việc giải quyết vấn đề cơ bản như vậy trong ngôn ngữ là rất khó.


Không phải loại thông tin này chỉ có trong thư viện lớp sao? Nó được sử dụng để liên kết?
Simon Bergot

@Simon, ý nghĩa của "thư viện lớp" là gì?
Péter Török

Ý tôi là tập hợp các tệp đối tượng chứa định nghĩa và phương thức lớp
Simon Bergot

7
Khi C ++ được tạo ra, AT & T / Bell Labs (nhà tuyển dụng Stroustrups tại thời điểm đó) chắc chắn đã có kinh nghiệm với việc phát triển C quy mô lớn. Phần mềm chuyển đổi điện thoại 5ESS của họ vào thời điểm đó có lẽ là chương trình C đơn lớn nhất thế giới. Những ý tưởng ban đầu về OO đã được nhìn thấy trong cơ sở mã đó và Cfront bắt chước các kỹ thuật đó. Tuy nhiên, khái niệm privatehiện đại hơn.
MSalters

1
Trong C, bạn chỉ cần đặt bộ cấp phát vào hàm thư viện; khách hàng sẽ không thể phân bổ một cấu trúc như vậy cả. Điều này làm tăng chi phí một chút, nhưng làm cho việc di chuyển mã qua các phiên bản trở nên tầm thường nên nó thường đáng giá. Tuy nhiên, nó có xu hướng dẫn đến một kiểu mã rất khác so với kiểu được thấy với C ++.
Donal Fellows

15

Định nghĩa lớp cần đủ để trình biên dịch tạo ra bố cục giống hệt nhau trong bộ nhớ bất cứ nơi nào bạn sử dụng một đối tượng của lớp. Ví dụ: đưa ra một cái gì đó như:

class X { 
    int a;
public:
    int b;
};

Trình biên dịch thường sẽ có aở offset 0 và bở offset 4. Nếu trình biên dịch thấy điều này chỉ là:

class X { 
public:
    int b;
};

Nó sẽ "nghĩ" bnên ở offset 0 thay vì offset 4. Khi mã sử dụng định nghĩa đó được gán cho b, mã sử dụng định nghĩa đầu tiên sẽ thấy ađược sửa đổi và ngược lại.

Cách thông thường để giảm thiểu tác động của việc thay đổi các phần riêng tư của lớp thường được gọi là thành ngữ pimpl (về điều đó tôi chắc chắn Google có thể cung cấp rất nhiều thông tin).


1
Tôi đang hỏi về một quyết định thiết kế. Tất nhiên bạn sẽ cần phải đặt tuyên bố thành viên tư nhân ở đâu đó để ngôn ngữ hoạt động. Nhưng tại sao nó phải ở trong tiêu đề mà không phải ở một nơi riêng tư hơn?
Simon Bergot

7
@Simon: Tiêu đề là tất cả các trình biên dịch nhìn thấy để cho nó biết lớp / struct trông như thế nào. Đã có các cuộc thảo luận về việc thêm một cái gì đó như các mô-đun vào C ++, điều này sẽ che giấu loại dữ liệu đó hơn một chút, nhưng cho đến nay nó vẫn chưa được phê duyệt (mặc dù nó cũng không bị loại bỏ hoàn toàn).
Jerry Coffin

3
Tuy nhiên, một quy tắc tầm thường sẽ là phân bổ các thành viên tư nhân ".cpp được xác định" như vậy cuối cùng. Điều đó có nghĩa là sự bù đắp của các thành viên tư nhân và "bình thường" sẽ không phụ thuộc vào họ. IMO lý do thực sự là bạn không thể kế thừa từ một lớp như vậy, vì phần dẫn xuất phải theo ngay cả những thành viên tư nhân đó.
MSalters

3

Có nhiều khả năng một số lý do. Mặc dù các thành viên tư nhân không thể được truy cập bởi hầu hết các lớp khác, nhưng họ vẫn có thể được truy cập bởi các lớp bạn bè. Vì vậy, ít nhất trong trường hợp này chúng có thể cần thiết trong tiêu đề, vì vậy lớp bạn bè có thể thấy chúng tồn tại.

Việc biên dịch lại các tệp phụ thuộc có thể phụ thuộc vào cấu trúc bao gồm của bạn. Bao gồm các tệp .h trong tệp .cpp thay vì một tiêu đề khác trong một số trường hợp có thể ngăn chặn các chuỗi biên dịch dài.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.