Tại sao tôi không thể khởi tạo static
các thành viên dữ liệu trong lớp?
Tiêu chuẩn C ++ chỉ cho phép các kiểu liệt kê hoặc tích phân hằng tĩnh được khởi tạo bên trong lớp. Đây là lý do a
được phép khởi tạo trong khi những người khác thì không.
Tham khảo:
C ++ 03 9.4.2 Các thành viên dữ liệu tĩnh
§4
Nếu một phần tử dữ liệu tĩnh thuộc kiểu liệt kê const tích phân hoặc const, thì khai báo của nó trong định nghĩa lớp có thể chỉ định bộ khởi tạo hằng là biểu thức hằng tích phân (5.19). Trong trường hợp đó, phần tử có thể xuất hiện trong các biểu thức hằng số tích phân. Thành viên sẽ vẫn được xác định trong phạm vi không gian tên nếu nó được sử dụng trong chương trình và định nghĩa phạm vi không gian tên sẽ không chứa bộ khởi tạo.
Các loại tích phân là gì?
C ++ 03 3.9.1 Các kiểu cơ bản
§7
Các kiểu bool, char, wchar_t và các kiểu số nguyên có dấu và không dấu được gọi chung là kiểu tích phân.43) Từ đồng nghĩa với kiểu tích phân là kiểu số nguyên.
Chú thích cuối trang:
43) Do đó, các phép liệt kê (7.2) không phải là tích phân; tuy nhiên, các phép liệt kê có thể được thăng cấp thành int, unsigned int, long, hoặc unsigned long, như được chỉ định trong 4.5.
Cách giải quyết:
Bạn có thể sử dụng thủ thuật enum để khởi tạo một mảng bên trong định nghĩa lớp của bạn.
class A
{
static const int a = 3;
enum { arrsize = 2 };
static const int c[arrsize] = { 1, 2 };
};
Tại sao Tiêu chuẩn không cho phép điều này?
Bjarne giải thích điều này một cách khéo léo ở đây :
Một lớp thường được khai báo trong tệp tiêu đề và tệp tiêu đề thường được đưa vào nhiều đơn vị dịch. Tuy nhiên, để tránh các quy tắc liên kết phức tạp, C ++ yêu cầu mọi đối tượng phải có một định nghĩa duy nhất. Quy tắc đó sẽ bị phá vỡ nếu C ++ cho phép định nghĩa trong lớp của các thực thể cần được lưu trữ trong bộ nhớ dưới dạng đối tượng.
Tại sao chỉ static const
các kiểu tích phân & enums mới được phép Khởi tạo trong lớp?
Câu trả lời ẩn trong câu trích dẫn của Bjarne, hãy đọc kỹ,
"C ++ yêu cầu mọi đối tượng phải có một định nghĩa duy nhất. Quy tắc đó sẽ bị phá vỡ nếu C ++ cho phép định nghĩa trong lớp về các thực thể cần được lưu trữ trong bộ nhớ dưới dạng đối tượng."
Lưu ý rằng chỉ static const
số nguyên mới có thể được coi là hằng số thời gian biên dịch. Trình biên dịch biết rằng giá trị số nguyên sẽ không thay đổi bất cứ lúc nào và do đó nó có thể áp dụng phép thuật của riêng mình và áp dụng tối ưu hóa, trình biên dịch chỉ cần nội dòng các thành viên lớp đó tức là chúng không được lưu trữ trong bộ nhớ nữa, vì nhu cầu được lưu trữ trong bộ nhớ bị loại bỏ , nó cung cấp cho các biến như vậy ngoại lệ đối với quy tắc được Bjarne đề cập.
Điều cần lưu ý ở đây là ngay cả khi static const
các giá trị tích phân có thể có Khởi tạo trong lớp, thì việc lấy địa chỉ của các biến như vậy không được phép. Người ta có thể lấy địa chỉ của một thành viên tĩnh nếu (và chỉ khi) nó có một định nghĩa ngoài lớp. Điều này càng xác nhận lý do ở trên.
enums được cho phép điều này vì các giá trị của một kiểu liệt kê có thể được sử dụng khi int được mong đợi. xem trích dẫn ở trên
Điều này thay đổi như thế nào trong C ++ 11?
C ++ 11 nới lỏng hạn chế ở một mức độ nhất định.
C ++ 11 9.4.2 Các thành viên dữ liệu tĩnh
§3
Nếu một thành viên dữ liệu tĩnh có kiểu ký tự const, khai báo của nó trong định nghĩa lớp có thể chỉ định một bộ khởi tạo dấu ngoặc nhọn hoặc dấu bằng trong đó mọi bộ khởi tạo-mệnh đề là một biểu thức gán là một biểu thức hằng. Một thành viên dữ liệu tĩnh của kiểu chữ có thể được khai báo trong định nghĩa lớp với constexpr specifier;
if như vậy, khai báo của nó sẽ chỉ định một bộ khởi tạo dấu ngoặc nhọn hoặc dấu bằng trong đó mỗi bộ khởi tạo-mệnh đề là một biểu thức gánlà một biểu thức hằng. [Lưu ý: Trong cả hai trường hợp này, thành viên có thể xuất hiện trong các biểu thức không đổi. —End note] Thành viên sẽ vẫn được xác định trong phạm vi không gian tên nếu nó được sử dụng trong chương trình và định nghĩa phạm vi không gian tên sẽ không chứa trình khởi tạo.
Ngoài ra, C ++ 11 sẽ cho phép (§12.6.2.8) một thành viên dữ liệu không tĩnh được khởi tạo ở nơi nó được khai báo (trong lớp của nó). Điều này có nghĩa là ngữ nghĩa người dùng dễ dàng hơn nhiều.
Lưu ý rằng các tính năng này chưa được triển khai trong gcc 4.7 mới nhất, Vì vậy, bạn vẫn có thể gặp lỗi biên dịch.