2013-06-12 32 views
20

Giả sử rằng tôi có một lớp đòi hỏi một vài hằng số để hoạt động. Một số hàm thành viên yêu cầu sử dụng các hằng số này. Sử dụng #define bị cau mày vì nó có thể gây ra va chạm. Hằng số là các mẫu thập lục phân 8 hoặc 16 bit và được lưu trữ dưới dạng uint8_t hoặc uint16_t. Các hằng số này cũng không thay đổi từ ví dụ thành thể hiện của lớp, và do đó bộ nhớ (mặc dù rất ít bộ nhớ) có thể được lưu bằng cách chỉ có một bản sao của các hằng số.C++ Sử dụng Biến Thành viên Const tĩnh

Có điều gì không đúng, hoặc có lẽ cách tốt hơn của việc hoàn thành ở trên thay vì chỉ đơn giản là làm một cái gì đó như sau:

// mycode.h 
// ....... 
class myclass { 
private: 
    static const uint16_t kMyClassConstant_ = 0xBEEF; 
// ....... 
}; 

Cảm ơn trước sự giúp đỡ.

+0

Không, không có gì sai với điều đó. (Hy vọng rằng một số chuyên gia C++ sẽ không phải sửa tôi. :)) – xxbbcc

+0

Bạn có thể nhận được câu trả lời tốt hơn tại http://codereview.stackexchange.com. –

+2

Có, đó là cách thông thường để khai báo một hằng số toàn cầu. –

Trả lời

38

Với mô tả của bạn về tình hình, tôi muốn nói sử dụng static const thành viên là một cách tiếp cận tốt. Trong C++ 11 bạn có thể muốn thay đổi nó thành static constexpr để nhấn mạnh nó là một hằng số biên dịch-thời gian, mặc dù không có gì có hiệu quả sẽ thay đổi như là kết quả của điều đó.

Nếu bạn tham khảo myclass::kMyClassContant_ ở đâu đó trong mã theo cách có liên quan theo quy tắc một định nghĩa (odr), đặc biệt. trong các ngữ cảnh yêu cầu tham chiếu (bao gồm tham chiếu const), trình biên dịch sẽ phàn nàn rằng không có định nghĩa nào về hằng số. Chỉ khai báo và khởi tạo nó bên trong lớp là không đủ trong trường hợp này. Điều này có thể buộc bạn phải tách khai và định nghĩa:

// mycode.h 
class myclass { 
private: 
    static const uint16_t kMyClassConstant_; 
}; 

// mycode.cpp 
const uint16_t myclass::kMyClassConstant_ = 0xBEEF; 

Để tránh những rắc rối của việc duy trì khai báo và định nghĩa riêng biệt, một số người thích tuyên bố một chức năng inline constexpr thay vì một biến thực tế:

// mycode.h 
class myclass { 
private: 
    static constexpr uint16_t kMyClassConstant_() 
    { return 0xBEEF; } 
}; 

này là một công việc chính xác cho nhiều vấn đề liên quan đến odr, và nó không gây ra bất kỳ tổn thất nào về hiệu năng. Cho dù nó thực sự hữu ích phụ thuộc vào bao nhiêu của một gánh nặng nó là để duy trì khai báo riêng biệt và định nghĩa của một hằng số tĩnh bình thường. Nếu bạn mong đợi các hằng số của bạn không bao giờ thay đổi khi mã của bạn phát triển, sử dụng hằng số tĩnh bình thường với các định nghĩa riêng biệt là thích hợp hơn. Nhưng nếu bạn thay đổi định nghĩa hằng số, phải biên dịch lại tệp định nghĩa và liên kết lại nó với tất cả các phần có liên quan của dự án, bạn có thể xem xét giải pháp dựa trên chức năng ở trên như một giải pháp thay thế tốt hơn.

Nhận xét cuối cùng về kiểu dữ liệu: Buộc nó vào 16 bit sử dụng std::uint16_t có thể hữu ích nếu bạn cần lưu trữ nhiều giá trị này dưới dạng nén. Nếu không, kích thước thực tế có thể không thực sự quan trọng, trong trường hợp này, std::uint_fast16_t (có thể lớn hơn 16 bit) có thể tốt hơn.

+7

Tôi đã chú ý đến câu trả lời của bạn trong SO, và theo như tôi có thể nói họ là tốt nhất về làm rõ và chính xác. Điều này trái ngược với một số cá nhân có "danh tiếng" cao, những người liên tục hấp dẫn sự huyền bí hơn là trả lời các câu hỏi. Xin chúc mừng (+1) – Ayrosa

+1

@ 411165 Cảm ơn lời khen ngợi :) Tôi đánh giá cao sự thông suốt của bạn trong việc nghiên cứu bài viết SO của tôi (và của người khác)! – jogojapan

+0

Câu trả lời này khá hữu ích, nhưng tôi không hiểu lý do về biên dịch lại. Tôi sẽ nghĩ rằng sử dụng các hàm thành viên nội tuyến (trong định nghĩa lớp và do đó trong tệp tiêu đề), mọi thay đổi đối với các giá trị sẽ yêu cầu _recompilation_ của tất cả các đơn vị dịch thuật khách hàng (bao gồm tệp tiêu đề), mặc dù chúng không thể truy cập trực tiếp vào các tệp đó các hàm thành viên. Mặt khác với các khai báo và định nghĩa riêng biệt, việc thay đổi các giá trị chỉ thay đổi mô-đun triển khai, yêu cầu một trình biên dịch đơn và chỉ _relinking_ của các máy khách. Đó là ít hơn, không nhiều hơn. –

5

Bạn có thể sử dụng những đặc điểm loại để thực hiện điều này:

#include <type_traits> 

class myclass { 
private: 
    typedef std::integral_constant<uint16_t , 0xBEEF> kMyClassConstant; 

    // ... 
}; 

sử dụng như myclass::kMyClassConstant::value.

Điều này cho thấy mục đích của việc thực hiện một hằng số không thể thiếu và ngăn không cho bạn vô tình lấy địa chỉ của hằng số.