2016-03-22 19 views
25

Đây là câu hỏi tiếp theo cho một câu hỏi khác của tôi: What is the optimal order of members in a class?Công cộng và riêng tư có ảnh hưởng gì đến bố trí bộ nhớ của một đối tượng không?

Điều đó có thay đổi bất kỳ thứ gì (trừ hiển thị) nếu tôi tổ chức các thành viên theo cách công khai, bảo vệ và thay phiên riêng tư không?

class Example 
{ 
public: 
    SomeClass m_sc; 
protected: 
    char m_ac[32];  
    SomeClass * m_scp; 
private: 
    char * m_name; 
public: 
    int m_i1; 
    int m_i2; 
    bool m_b1; 
    bool m_b2; 
private: 
    bool m_b3; 
}; 

Có sự khác biệt nào giữa lớp học này và lớp học nơi tôi làm cho tất cả thành viên công khai khi chạy không? Tôi muốn tuân theo quy tắc đặt hàng các loại từ lớn đến nhỏ (nếu khả năng đọc không bị tổn hại nghiêm trọng).

Tôi giả định rằng nó không ảnh hưởng đến chương trình đã biên dịch, giống như const chỉ được chọn trong khi biên soạn. Điều này có đúng không?

+0

Tránh 'được bảo vệ', tránh các con trỏ trần. –

+0

Thực tế một bản sao: http://stackoverflow.com/q/15763091/560648 Vui lòng tìm kiếm trước khi yêu cầu. –

Trả lời

32

Câu trả lời phụ thuộc vào phiên bản ngôn ngữ, vì điều này đã thay đổi từ C++ 03 thành C++ 11.

Trong C++ 03, quy tắc là:

thành viên trong khối điều khiển cùng truy cập (có nghĩa là, từ một trong những public, protected, private từ khóa kế tiếp từ bộ đó) là được phân bổ theo thứ tự khai báo trong lớp, không nhất thiết phải liên tục.

Trong C++ 11, các quy tắc đã được thay đổi như sau:

viên với mức độ kiểm soát cùng truy cập (công cộng, bảo vệ, tư nhân) đang được phân bổ theo thứ tự khai trong lớp, không nhất thiết phải liên tục.

Vì vậy, trong C++ 03, bạn có thể đảm bảo điều này (tôi sử dụng @ để có nghĩa là bù đắp của một thành viên trong lớp):

  • @m_ac < @m_scp
  • @m_i1 < @m_i2 < @m_b1 < @m_b2

Trong C++ 11, bạn có thêm một số bảo đảm:

  • @m_ac < @m_scp
  • @m_sc < @m_i1 < @m_i2 < @m_b1 < @m_b2
  • @m_name < @m_b3

Trong cả hai phiên bản, trình biên dịch có thể sắp xếp lại các thành viên trong chuỗi khác nhau vì nó thấy phù hợp, và nó thậm chí có thể interleave các dây chuyền.


Lưu ý rằng có thêm một cơ chế có thể nhập vào hình ảnh: các lớp bố cục tiêu chuẩn.

Lớp là bố cục chuẩn nếu không có ảo, nếu tất cả thành viên dữ liệu không tĩnh của nó có cùng kiểm soát truy cập, nó không có lớp cơ sở hoặc thành viên dữ liệu không tĩnh của loại bố cục không chuẩn hoặc kiểu tham chiếu và nếu nó có nhiều nhất một lớp với bất kỳ thành viên dữ liệu không tĩnh nào trong chuỗi kế thừa của nó (ví dụ:nó không thể vừa định nghĩa các thành viên dữ liệu không tĩnh của nó và kế thừa một số từ một lớp cơ sở).

Nếu lớp là bố cục chuẩn, có bảo đảm bổ sung rằng địa chỉ của thành viên dữ liệu không tĩnh đầu tiên giống với địa chỉ của đối tượng lớp (điều này có nghĩa là không thể có lớp đệm ở đầu bố cục lớp). Lưu ý rằng các điều kiện về bố trí chuẩn, cùng với các trình biên dịch thực tế không đưa ra lựa chọn bi quan, có nghĩa là trong lớp bố cục chuẩn, các thành viên sẽ được sắp xếp liên tục theo thứ tự khai báo (với phần đệm cho liên kết xen kẽ khi cần thiết).

+0

Bạn có bất kỳ dữ liệu nào về cách trình biên dịch thường đặt hàng các thành viên trong thực tế không? –

+0

@JanDvorak Không, xin lỗi. Tôi không lập trình các hệ thống nơi đặt hàng thành viên dữ liệu sẽ quan trọng đến mức đó, vì vậy tôi chưa bao giờ có lý do để tìm kiếm nó. Tôi sẽ nói rằng nền tảng tài liệu ABI và tài liệu trình biên dịch có thể là điểm khởi đầu tốt cho việc tìm kiếm. – Angew

4

Tôi cho rằng quy tắc đặt hàng không phải lúc nào cũng tốt nhất. Điều duy nhất bạn đạt được là tránh "đệm".

Tuy nhiên, một "quy tắc" khác để theo dõi là có các thành viên "nóng" nhất ở trên cùng để chúng có thể vừa với một dòng bộ nhớ cache, thường là 64 byte.

Hãy tưởng tượng bạn có vòng kiểm tra cờ của lớp học và số lần chênh lệch là 1 và thành viên khác của bạn ở mức bù 65 và một thành viên khác ở mức chênh lệch 200. Bạn sẽ bị mất bộ nhớ cache.

int count = 0; 

for (int i = 0; i < 10; i++) 
{ 
    if (class->flag/*offset:1*/ == true && class->flag2 == true/*offset:65*/) 
      count += class->n; /*offset: 200*/    
} 

vòng lặp đó sẽ có rất nhiều hơn chậm hơn so với một phiên bản thân thiện với bộ nhớ cache như:

int count = 0; 

for (int i = 0; i < 10; i++) 
{ 
    if (class->flag/*offset:1*/ == true && class->flag2 == true/*offset:2*/) 
      count += class->n; /*offset: 3*/ 

} 

Các vòng sau chỉ cần đọc một dòng bộ nhớ cache duy nhất cho mỗi lần lặp. Nó không thể nhanh hơn.

+1

Có các thành viên "nóng" được đóng gói cùng nhau là một tối ưu hóa vi mô mà không đáng để nói chung. Trong trường hợp của các cấu trúc dữ liệu đồng thời, nó cũng là một sự bi quan (vì chia sẻ sai), đó là một trường hợp được thừa nhận hiếm nhưng củng cố thực tế rằng nó không phải là một quy tắc phổ quát. –

+0

@MatthieuM. Bạn đúng về việc chia sẻ sai, đó là lý do tại sao tôi nói "quy tắc" (cố gắng làm cho nó nghe rằng nó không phải là một quy tắc thực sự giống như cái mà anh ta đang sử dụng) nhưng tôi đã thất bại. Tôi làm việc ở một nơi mà sự chậm trễ không được phép vì vậy đó là một tối ưu hóa tôi sử dụng, không phải ai cũng cần nó tho, tôi đồng ý. Nếu bạn biết nhiều chủ đề sẽ viết/đọc một số dữ liệu được chia sẻ rất đồng thời, tất nhiên bạn sẽ sử dụng một thiết kế khác. – James

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