2012-07-04 21 views
10

Tôi có một chút câu hỏi lý thuyết, tuy nhiên đó là vấn đề mà đôi khi tôi phải đối mặt khi thiết kế các lớp và tôi thấy nó được thực hiện khác khi đọc mã khác. Điều nào sau đây sẽ tốt hơn và tại sao:Thiết kế lớp: mảng vs nhiều biến

dụ 1:

class Color 
{ 
public: 
    Color(float, float, float); 
    ~Color(); 

    friend bool operator==(Color& lhs, Color& rhs); 
    void multiply(Color); 
    // ... 
    float get_r(); 
    float get_g(); 
    float get_b(); 

private: 
    float color_values[3]; 
} 

dụ 2:

class Color 
{ 
public: 
    // as above 

private: 
    float r; 
    float g; 
    float b; 
} 

Có một nguyên tắc chung ta nên làm theo trong những trường hợp như thế này hoặc là nó chỉ lên đến một lập trình viên và những gì dường như có ý nghĩa hơn?

+0

BTW bạn có lý do thực tế nào về lý do giữ bí mật các trường ở đây không? – Kos

+0

@Kos, đưa ra ví dụ, tôi có thể dễ dàng nhìn thấy bằng cách sử dụng accessors và mutators để hạn chế đầu vào, mặc dù mỗi trường có lẽ nên là 'byte' thay vì' float'. Ngoài ra, các giá trị chuỗi như '# ABCDEF' và' # ABC' có thể được sử dụng. – zzzzBov

Trả lời

4

Nó phụ thuộc, bạn có định lặp lại toàn bộ mảng không?

Trong trường hợp đó, tôi nghĩ giải pháp 1 phù hợp hơn. Rất hữu ích khi có một mảng như vậy khi bạn có các hàm hoạt động trong vòng lặp trên dữ liệu

ví dụ:

void BumpColors(float idx) 
{ 
    for (int i = 0; i < 3; ++i) 
     color_values[i] += idx; 
} 

vs

void BumpColors(float idx) 
{ 
    color_values[0] += idx; 
    color_values[1] += idx; 
    color_values[2] += idx; 
} 

Tất nhiên này là tầm thường, và tôi nghĩ rằng nó thực sự là một vấn đề ưu tiên. Trong một số dịp hiếm bạn có thể có các API mà phải mất một con trỏ đến dữ liệu mặc dù, và khi bạn có thể làm

awesomeAPI((float*)&r); 

tôi sẽ nhiều thích làm

awesomeAPI((float*)&color_values[0]); 

vì mảng sẽ đảm bảo tiếp giáp của nó trong khi bạn có thể gây rối với sự tiếp giáp bằng cách thêm nhầm lẫn một biến thành viên khác không liên quan sau float r.


Hiệu suất khôn ngoan sẽ không có sự khác biệt.

+0

Đối số lặp lại là IMHO chìa khóa, nhưng tôi nghĩ trong trường hợp của màu sắc, nó lập luận theo cách khác. Tôi không thể nghĩ ra một trường hợp sẽ có ý nghĩa để tăng từng màu bằng cùng một giá trị. –

+0

Chúng tôi không thể giả định như những gì người dùng sẽ làm với màu sắc; cho tất cả chúng ta biết rằng họ có thể sử dụng lớp Color để lưu trữ các tiêu chuẩn đỉnh được mã hóa theo hình cầu. –

1

Có quy tắc chung nào nên tuân theo trong trường hợp như thế này hay chỉ là lập trình viên và điều gì có vẻ hợp lý hơn?

Đó chắc chắn là lập trình viên và bất kỳ điều gì có ý nghĩa hơn.

Trong trường hợp của bạn, tùy chọn thứ hai có vẻ phù hợp hơn. Xét cho cùng, suy nghĩ logic, thành viên của bạn không phải là một mảng giá trị, nhưng giá trị cho r, gb.

+0

Bạn chọn "chắc chắn" đồng ý với hai lựa chọn nào? –

+0

@larsmans haha ​​- lập trình viên và điều có ý nghĩa hơn. –

0

Ưu điểm của việc sử dụng một mảng:

  • năng bảo trì: Bạn có thể sử dụng các giá trị trong mảng để lặp
  • năng bảo trì: Khi một giá trị nên được bổ sung (như vàng?) So với bạn không có để thay đổi rất nhiều mã.

Nhược điểm:

  • Độ khó: Các 'giá trị' có tên rõ ràng hơn (cụ thể là r, g, b trong trường hợp này).

Trong trường hợp của bạn, các biến r, g, b là tốt nhất, vì nó không được thêm màu và vòng lặp trên 3 phần tử có thể ít quan trọng hơn khả năng đọc.

3

Tôi muốn nói câu hỏi thứ hai là tốt nhất.

Đầu tiên, dữ liệu mà các biến của bạn chứa không được cho là (vật lý) nằm trong một mảng. Nếu bạn có ví dụ một lớp học với 3 sinh viên, không nhiều hơn, không ít hơn, bạn sẽ đặt chúng trong một mảng, vì chúng là một mảng sinh viên, nhưng ở đây, nó chỉ là màu sắc.

Thứ hai, ai đó đọc mã của bạn cũng có thể hiểu trong trường hợp thứ hai thực sự nhanh chóng những gì biến của bạn chứa (r là màu đỏ, v.v ...). Nó không phải là trường hợp với một mảng. Thứ ba, bạn sẽ có ít lỗi hơn, bạn sẽ không phải nhớ "oh, trong mảng của tôi, màu đỏ là 0, g là 1, b là 2" và bạn sẽ không thay thế do nhầm lẫn

return color_values[0] 

bởi

return color_values[1] 

trong mã của bạn.

+0

Đó là sự thật, nhưng vào cuối ngày nó phụ thuộc vào những gì (s) ông sẽ sử dụng màu sắc cho. Nếu cô ấy sẽ chuyển đổi từ RGB sang HSL, về cơ bản là một hoạt động vectơ, mảng có thể thuận tiện hơn. Ngoài ra, có khả năng lập chỉ mục mảng với một enum, như 'color_values ​​[red]' – deStrangis

+0

yeah, tôi đã nghĩ về enum, nhưng đó là một chút quá mức cần thiết cho một lớp dễ như vậy: D – Tuxer

+0

+1. Có nghĩa là hoàn toàn bị che khuất bởi tham chiếu mảng chung. Hơn nữa, một mảng truyền tải ý thức sai của dữ liệu. r, g, b (tên thật tệ), là những thứ khác nhau. Chúng không thể hoán đổi cho nhau trong xử lý lặp lại; không có cách nào để thiết lập đúng hoặc nhận các giá trị mà không biết ngữ cảnh. Vì vậy chúng ta không thể sử dụng lợi ích của cấu trúc mảng. – radarbob

2

Tôi nghĩ rằng bạn đúng: "Nó chỉ là lập trình viên và có vẻ hợp lý hơn". Nếu đây là chương trình của tôi, tôi sẽ chọn một hình thức này hoặc hình thức khác mà không lo lắng quá nhiều về nó, sau đó viết một số phần khác của chương trình, sau đó xem lại vấn đề sau.

Một trong những lợi ích của thiết kế hướng lớp là nó làm cho các chi tiết thực hiện nội bộ của loại riêng tư này, giúp bạn thay đổi chúng sau này một cách thuận tiện.

Tôi nghĩ rằng câu hỏi của bạn không quan trọng, chỉ có tôi nghi ngờ rằng người ta có thể trả lời tốt cho đến khi người ta viết nhiều mã hơn. Trong phần tóm tắt, chỉ có ba phần tử, và ba phần có tên - đỏ, xanh lá cây và xanh dương - vì vậy tôi nghĩ rằng bạn có thể đi theo cách này. Nếu buộc phải chọn, tôi chọn ví dụ 2.

10

Cả hai!

Sử dụng này:

class Color { 

    // ... 

private: 

    union { 
     struct { 
      float r, g, b; 
     }; 
     float c[3]; 
    }; 

}; 

Sau đó c[0] sẽ tương đương với r, vân vân.

+1

Câu trả lời thực tế tốt nhất, tận dụng tối đa cả hai thế giới! –

+1

+1 cho sự thông minh. Tôi đã không nghĩ về điều này. (Về tiêu chuẩn ngôn ngữ hợp pháp, tôi không chắc chắn 100% rằng điều này phải luôn luôn hoạt động, nhưng ngay cả khi không hợp pháp, thực tế nó vẫn hoạt động. Thật khó để tưởng tượng trình biên dịch có khả năng phá vỡ câu trả lời của bạn.) – thb

+0

@thb cũng được định nghĩa nếu 'sizeof (the_anonymous_struct_with_the_three_floats) == 3 * sizeof (float)', không phải là một giả định điên rồ. Tôi đoán rằng nếu bạn hoang tưởng, bạn có thể 'static_assert' nó. –

0

Đôi khi một lập trình viên sẽ sử dụng một mảng (hoặc cấu trúc dữ liệu) để lưu dữ liệu nhanh hơn vào đĩa (hoặc bộ nhớ) bằng cách sử dụng 1 thao tác ghi. Điều này đặc biệt hữu ích nếu bạn đang đọc và ghi nhiều dữ liệu.