2016-02-04 14 views
9

Nếu tôi có các lớp sau:Thứ tự tối ưu của các thành viên trong một lớp học là gì?

class Example 
{ 
    bool m_b1; 
    SomeClass m_sc; 
    bool m_b2; 
    char * m_name; 
    bool m_b3; 
    int m_i1; 
    SomeClass * m_sc; 
    int m_i2; 
}; 

trật tự tối ưu của các thành viên là gì? Theo như tôi biết, thứ tự tuyên bố của các thành viên bằng với thứ tự của các thành viên trong bộ nhớ, khi tạo ra một thể hiện của lớp này.

  • Tôi có nên sắp xếp các thành viên để các loại bằng nhau không?
  • Mọi con trỏ có được tính là cùng loại không?
  • Sắp xếp lại có ảnh hưởng đến kích thước của đối tượng không? Có 4 hoặc 8 liên kết byte áp dụng ở đây?
  • Làm cách nào tôi có thể thấy các tác động của các yếu tố trên? Có sizeof hiển thị bộ nhớ được sử dụng bởi một đối tượng bao gồm cả không gian trống để căn chỉnh không?
  • Có tốt hơn vào cuối ngày để sắp xếp các thành viên sao cho ý nghĩa và ngữ cảnh của họ dễ hiểu hơn không?
+3

Trừ khi bạn đang lập trình cho một hệ thống nhúng với bộ nhớ rất ít tôi sẽ tranh luận thứ tự đó không quan trọng nhiều. Chỉ khi bạn có mảng hoặc * lô * của biến thành viên (trong trường hợp này tôi sẽ nói thiết kế của bạn sai) để nó có thể gây ra sự cố bộ nhớ đệm và mã cực kỳ quan trọng (ít khi xảy ra), chỉ khi đó bạn nên bắt đầu suy nghĩ về việc nhóm và đặt hàng. Bắt đầu bằng cách ưu tiên khả năng đọc (có nghĩa là khả năng bảo trì), và chỉ thay đổi nếu bạn hoàn toàn cần. –

+0

Tôi muốn tranh luận rằng kích thước chồng chéo của đối tượng có ảnh hưởng đến thời gian chạy nhiều hơn khi bạn khai báo thành viên. tất cả các thành viên bị CPU dư thừa bằng cách tính toán 'này + offset' tại thời gian biên dịch, vì vậy trong thời gian chạy không có nhiều sự khác biệt –

Trả lời

6

Theo quy tắc chung, bạn nên đặt hàng theo kích thước, lớn hơn thành nhỏ hơn. Điều này sẽ tạo ra các padding tối thiểu của cấu trúc, do đó giảm thiểu kích thước cấu trúc. Điều này quan trọng nhất nếu các đối tượng của cấu trúc được sử dụng trong bộ nhớ được phân bổ tiếp giáp, ví dụ: vector và giúp rất nhiều với bộ nhớ cache (nhiều đối tượng phù hợp trong cùng một dòng bộ nhớ cache).

Một tối ưu hóa đáng ngạc nhiên khác được chứng minh bởi Andrei Alexandrescu (tôi nghĩ là một trong CppCon) là ông đã mang lại thành viên truy cập nhiều nhất đầu tiên. Điều này nhanh hơn vì bù đắp bằng không. Tất nhiên, ông đã nói về tối ưu hóa vi mô, sau khi chấm điểm địa ngục ra khỏi ứng dụng và chăm sóc cho mỗi strop nhỏ của hiệu suất.

+0

Bạn có biết vị trí đặt hàng "lớn hơn đến nhỏ hơn" không? Tôi tin rằng bạn nhận được cùng kích thước theo thứ tự "nhỏ hơn đến lớn hơn", cộng với bạn có nhiều thành viên hơn ở mức bù nhỏ. – davidhigh

+0

@davidhigh Tôi không biết đặt hàng đến từ đâu. Tôi đã nghe từ nhiều nơi (một trong những giáo viên của tôi, một số hội nghị trên web). Tôi nghĩ rằng bạn là đúng, nhỏ hơn-to-lớn hơn sẽ mang lại cùng kích thước. "Bù đắp nhỏ" là không thích hợp. Việc tối ưu hóa đến từ thực tế là bù đắp bằng không (không phải từ độ lệch nhỏ). Trên ALU, các phép toán với '0' nhanh hơn. Tôi nghĩ rằng cũng với '1' (tăng ví dụ). Ngoài ra, toán hạng không quan trọng (nếu nó là '4',' 10' hoặc '32'). – bolov

3

Tôi có nên sắp xếp các thành viên để các loại bằng nhau không?

Nếu nó tăng khả năng đọc, Có!

Mọi con trỏ có được tính cùng loại không?

Có, tất cả con trỏ phải chọn 32 bit hoặc 64 bit hoặc bất kỳ hệ thống nào xác định.

Sắp xếp lại có ảnh hưởng đến kích thước của đối tượng không? Có 4 hoặc 8 liên kết byte áp dụng ở đây?

Điều này có thể là do căn chỉnh. Live example.

Có tốt hơn vào cuối ngày để sắp xếp các thành viên sao cho ý nghĩa và ngữ cảnh của họ dễ hiểu hơn không?

Nếu nó tăng ý nghĩa, khả năng đọc và khả năng bảo trì, CÓ.

8

Trình tự là rất quan trọng khi một biến thành viên phụ thuộc vào biến thành viên khác khi sử dụng danh sách initializer:

Hãy tưởng tượng một lớp học với hai biến nơi các nhà xây dựng của biến thứ hai (varC) cần là người đầu tiên (varB)

class A 
{ 
    B varB; 
    C varC; 

    A() 
    : varC(varB) 
    { 
    } 
}; 

Thứ tự quyết định các bước mà các nhà thầu được thực thi, vì vậy việc thay đổi thứ tự của varB và varC sẽ chuyển đối tượng chưa được khởi tạo của varB đến varC.

Tất nhiên, phong cách và khả năng đọc cũng rất quan trọng.

+0

Tại sao đoạn mã, nó không hoạt động. – Niall

+1

Không phân biệt đoạn mã, tôi nghĩ điểm này khá tốt. –

+0

Xin lỗi, tôi đã sử dụng sai loại đoạn mã, vì vậy có vẻ như :) –

1

Nguyên tắc chung mà tôi đã học được là "đặt các biến lớn nhất đầu tiên".

Trong ví dụ của bạn này sẽ là

class Example 
{ 

    SomeClass m_sc; 
    SomeClass * m_sc; 
    char * m_name; 
    int m_i1; 
    int m_i2; 
    bool m_b1; 
    bool m_b2; 
    bool m_b3; 
}; 

tôi không phải là tốt nhất xây dựng suy nghĩ của tôi, vì vậy chịu với tôi.

Biến thường chỉ có thể được đặt sao cho chúng được căn chỉnh với kích thước của chúng, điều đó có nghĩa là nếu chúng ta đặt một int giữa bất kỳ ba boolean cuối cùng thì trình biên dịch sẽ phải pad sizeof (int) - sizeof (bool) byte để đảm bảo rằng dữ liệu phù hợp với sự liên kết của nó. Điều đó có nghĩa là bạn sẽ sử dụng nhiều bộ nhớ hơn mức bạn cần.

1

Ngoài các câu trả lời đã đề cập trước:

Thứ tự của các biến thành viên là chủ yếu có liên quan (như tôi đã thấy) trong thiết kế dữ liệu theo định hướng, ví dụ trong công cụ trò chơi video. Bạn về cơ bản muốn giảm thiểu nhớ cache.

Một bài viết rất tốt về thiết kế dữ liệu theo định hướng có thể được tìm thấy ở đây: Data-Oriented Design (Or Why You Might Be Shooting Yourself in The Foot With OOP)

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