2010-08-17 39 views
11
class Foo { 
public: 
static const int kType = 42; 
}; 

void Func() { 
Foo *bar = NULL; 
int x = bar->kType; 
putc(x, stderr); 
} 

Hành vi được xác định này? Tôi đọc qua tiêu chuẩn C++ nhưng không thể tìm thấy bất cứ điều gì về truy cập một giá trị const tĩnh như thế này ... Tôi đã kiểm tra lắp ráp được sản xuất bởi GCC 4.2, Clang ++, và Visual Studio 2010 và không ai trong số họ thực hiện một dereference của NULL con trỏ, nhưng tôi muốn chắc chắn.C++ truy cập tĩnh const thông qua con trỏ NULL

+1

Hầu hết các trình biên dịch sẽ cho bạn một cảnh báo về việc truy cập các thành viên static/const thông qua một con trỏ cá thể. – cHao

Trả lời

10

Bạn có thể sử dụng con trỏ (hoặc biểu thức khác) để truy cập thành viên tĩnh; Tuy nhiên, làm như vậy thông qua một con trỏ NULL không may là hành vi chính thức undefined. Từ 9.4/2 "thành viên tĩnh":

Một thành viên tĩnh của lớp X có thể gọi bằng cách sử dụng trình độ-id biểu hiện X :: s; không cần thiết để sử dụng cú pháp truy cập thành viên của lớp (5.2.5) để chỉ một thành viên tĩnh. Một thành viên tĩnh có thể được giới thiệu bằng cách sử dụng cú pháp truy cập thành viên lớp , trong trường hợp biểu thức đối tượng là được đánh giá.

Dựa trên những ví dụ mà sau:

class process { 
public: 
    static void reschedule(); 
}; 

process& g(); 

void f() 
{ 
    process::reschedule(); // OK: no object necessary 
    g().reschedule();  // g() is called 
} 

Mục đích là để cho phép bạn để đảm bảo rằng chức năng sẽ được gọi trong kịch bản này.

+4

Tôi sẽ chia tóc và chỉ ra rằng "đánh giá biểu thức đối tượng" không nhất thiết ngụ ý * dereferencing con trỏ * - nó chỉ có nghĩa là tìm giá trị con trỏ - nhưng nhìn vào §5.2.5, nó xuất hiện rằng nó * không *, bởi vì "biểu thức đối tượng" trong trường hợp này được định nghĩa là "' * bar' ", không chỉ là" 'bar'". Vì vậy, yer phải. – zwol

+0

Đây có phải là chủ đề tranh luận không? Khái niệm đánh giá một đối tượng, như chỉ đơn giản là chỉ định nó, là khá zen. Mặt khác, ví dụ dưới văn bản đó hiển thị rõ ràng một cuộc gọi hàm. Rõ ràng chúng có nghĩa là biểu thức trước "." hoặc "->" được đánh giá. – Potatoswatter

+2

@Potatoswatter: Tôi tin rằng điều này giải thích của tôi là chính xác, nhưng tôi chắc chắn không coi mình là một cơ quan. Trong khi tôi nghĩ rằng khả năng gọi một hàm thành viên tĩnh thông qua một con trỏ NULL thích hợp có khả năng làm việc như bạn mong đợi, thật dễ dàng để thực hiện cuộc gọi bằng tên kiểu chứ không phải là một con trỏ (mặc dù tôi nghĩ cho các mẫu điều đó có thể không phải lúc nào cũng đúng), vì vậy bạn có thể không nên sử dụng con trỏ cho điều này. –

3

Tôi tin rằng giá trị thực tế của các loại không được sử dụng ở tất cả khi gọi

bar->kType 

từ kType là tĩnh và thanh là loại Foo nó cũng giống như gọi

Foo::kType 

mà bạn thực sự nên làm dù sao cho rõ ràng.

Gọi bar->kType cảnh báo trình biên dịch trên hầu hết các nền tảng vì lý do này.

+0

+1 "mà bạn thực sự nên làm dù sao cho rõ ràng" – leedm777

+0

Chính xác, các thành viên tĩnh được lưu trữ độc lập với các cá thể đối tượng, do đó bản thân đối tượng không bao giờ bị bỏ qua. – casablanca

+0

@casablanca: Đó là chi tiết triển khai. Nếu bạn có tài liệu chứng minh rằng đây là hành vi của phiên bản trình biên dịch cụ thể của bạn thì tuyệt vời! Nếu không, đừng làm thế. Chắc chắn, về một câu hỏi về cỗ máy trừu tượng có tên C++, đây không phải là câu trả lời "đúng" tôi sợ. –

1

Ngoài các vấn đề về truy cập thông qua con trỏ NULL, có một vấn đề tế nhị trong các mã

$ 9.4.2/2 - "Việc kê khai của một thành viên dữ liệu tĩnh trong định nghĩa lớp của nó không phải là một định nghĩa và có thể là một loại không đầy đủ khác với khoảng trống đủ điều kiện cv. Định nghĩa cho một thành viên dữ liệu tĩnh sẽ xuất hiện trong một phạm vi không gian tên kèm theo định nghĩa lớp của thành viên. "

$ 9.4.2/4- "Nếu một thành viên dữ liệu tĩnh có dạng tách rời hoặc kiểu liệt kê, khai báo của nó trong định nghĩa lớp có thể chỉ định bộ khởi tạo không đổi sẽ là biểu thức hằng số không thể tách rời (5.19). trường hợp, thành viên có thể xuất hiện trong các biểu thức hằng số tích phân. Thành viên vẫn phải đượ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 không chứa bộ khởi tạo. "

class Foo { 
public: 
static const int kType = 42; 
}; 

int const Foo::kType; 

void Func() { 
Foo *bar = NULL; 
int x = bar->kType; 
putc(x, stderr); 
} 

Vì vậy, một lý do nữa cho UB trong mã OP.

1

Thậm chí nếu nó hoạt động thì đó là mã khủng khiếp.

Trong chương trình nghiêm túc, bạn không chỉ mã cho chính mình mà còn cho những người khác sẽ duy trì mã của bạn. Các thủ đoạn chơi như thế này phải tránh, bởi vì bạn tôn trọng đồng nghiệp của mình.

Một hệ quả của mã này: cho dù con trỏ là NULL hay không thậm chí không có vấn đề, nhưng nó ngụ ý rằng thành viên này kType có thể không phải là thành viên không tĩnh của lớp. Đôi khi các lớp học rất lớn (đây cũng là điều xấu) và người ta không thể luôn kiểm tra lại định nghĩa của từng biến.

Hãy nghiêm khắc. Và gọi tất cả các thành viên tĩnh của bạn chỉ theo cách này:

Foo::kType 

Một khả năng khác là làm theo một quy ước mã hóa cho phép biết rằng thành viên là tĩnh, ví dụ, một tiền tố s_ cho tất cả các lớp học thành viên tĩnh:

Foo::s_kType 
-1

Có quy tắc cao hơn để nói về cơ bản nói - thậm chí không nghĩ đến việc biên dịch những thứ không được sử dụng. Lập trình mẫu nâng cao phụ thuộc vào điều này rất nhiều, do đó, ngay cả khi nó có thể là một chút xám-zonish khi một trình biên dịch thấy rõ ràng rằng kết quả của một cấu trúc không được sử dụng nó chỉ sẽ loại bỏ nó. Đặc biệt là khi nó được chứng minh là an toàn như trong trường hợp này.

Bạn có thể muốn thử một vài biến thể nếu bạn muốn - giống như làm cho con trỏ một tham số của hàm, kết quả của hàm, để lại con trỏ chưa được khởi tạo (cơ hội tốt nhất để kích hoạt khiếu nại trình biên dịch), thực hiện một đoạn thẳng 0 cơ hội tốt nhất để trở thành conplaint-free).

+0

Thật ngớ ngẩn. Tất cả các cách tối ưu hóa có thể dẫn đến cuộc gọi chức năng của bạn thậm chí không xảy ra, hoặc [những thứ điên rồ khác] (http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx). Tôi nghiêm túc. Điều này _can_ xảy ra trong thực tế. –

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