2012-07-04 29 views
7

thể trùng lặp:
Calling class method through NULL class pointerTại sao phương thức gọi thông qua con trỏ null "làm việc" trong C++?

#include <iostream> 
using namespace std; 
class test 
{ 
    int i; 
public: 
    test():i(0){ cout << "ctor called" << endl;} 
    void show() 
    { 
     cout<<"show fun called"<<endl; 
    } 
}; 

int main(int argc , char *argv[]) 
{ 
    test *ptr = NULL; 
    ptr->show(); 
    return 0; 
} 

rõ ràng, không có ctor sẽ được gọi. Đây có phải là tiêu chuẩn này không? hoặc chỉ một số tối ưu hóa trình biên dịch vì con trỏ này không được sử dụng trong hàm thành viên show()?

+5

Dereferencing một con trỏ null là UB. – chris

+2

Thêm 'i = 1;' bên trong 'show()' và cố gắng chạy nó. –

+1

chris, UB có nghĩa là lên đến thực hiện trình biên dịch? và tôi đang sử dụng g ++ 4.6.3. Jesse Good, tất nhiên, seg lỗi, không có nghi ngờ. Tôi tự hỏi nếu trình biên dịch sẽ tạo ra mã mà không có điều này cho chức năng thành viên mà không cần nó. – bbc

Trả lời

22

Con trỏ không cần thiết để gọi phương thức. Loại con trỏ được biết, do đó, mã cho phương thức này được biết. Phương pháp này không sử dụng this, do đó, nó chạy mã tốt. Đó là hành vi không xác định, nhưng nó hiệu quả hơn không để kiểm tra nếu con trỏ là NULL, do đó, nó chạy.

+0

(Làm cách nào) một phương pháp ảo ảnh hưởng đến hành vi được quan sát của anh ta? –

+2

Tôi mong đợi một cuộc gọi phương thức ảo thất bại, vì nó cần tra cứu phương thức trong vtable, nó sẽ tìm thấy ở đầu kia của con trỏ, nhưng con trỏ là NULL. –

+0

lỗi seg cho ảo với g + +, không chắc chắn điều này cũng phụ thuộc thực hiện. trình biên dịch sẽ tạo ra vptr và vtblr mà không có đối tượng của lớp? đoán không vì lý do hiệu quả? – bbc

1

Nó không hợp lệ, hành vi không xác định và kết quả thực tế phụ thuộc vào trình biên dịch của bạn.

+0

Vâng, nó hợp lệ. Hàm __thiscall không truy cập con trỏ 'this' này chỉ là một hàm tĩnh. Chỉ cần thêm "tĩnh" - nó là tốt nhất bạn có thể làm trong mã đó là tĩnh theo định nghĩa :) Và nó buộc các chức năng để sử dụng __cdecl, do đó, không có yêu cầu con trỏ này. Lưu ý rằng việc thêm ảo sau đó sẽ làm cho việc chuẩn bị phức tạp hơn đòi hỏi dereferencing con trỏ, và sau đó sẽ segfault. –

-1

Vâng, trước hết, nó không hợp lệ vì nó gọi hành vi không xác định. Bạn đang thực sự hỏi tại sao trình biên dịch lại cho phép nó, và câu trả lời là bởi vì đó là mã đầu xương mà đơn giản sẽ không xảy ra trong một ứng dụng thực sự, vậy tại sao lại bận tâm? Vấn đề thực sự xảy ra khi con trỏ được thực hiện không hợp lệ khi chạy theo cách không thể dự đoán được bằng phân tích mã tĩnh.

Trình biên dịch không có ở đó để giữ tay bạn, nó ở đó để biên dịch mã của bạn theo tiêu chuẩn. Nếu nó cung cấp cảnh báo hữu ích sau đó tuyệt vời, nhưng không có gì là cú pháp hoặc ngữ nghĩa bất hợp pháp ở đó, bạn chỉ đơn giản là viết mã mà nguyên nhân hành vi không xác định.

+0

cảm ơn lời khuyên. – bbc

8

Nếu bạn nhìn vào lắp ráp (cho ít nhất một trình biên dịch), bạn có thể thấy lý do tại sao nó chạy (mặc dù nó là hành vi không xác định như nhiều người đã chỉ ra). Đối với hai dòng sau:

test *ptr = NULL; 
ptr->show(); 

lắp ráp này được tạo ra (trong một trình biên dịch Tôi chỉ cố gắng):

00000004: C7 45 FC 00 00 00 mov   dword ptr [ebp-4],0 
      00 
0000000B: 8B 4D FC   mov   ecx,dword ptr [ebp-4] 
0000000E: E8 00 00 00 00  call  [email protected]@@QAEXXZ 

Nó đẩy NULL (0) trên stack và gọi phương thức từ địa chỉ của phương thức này độc lập với cá thể đối tượng thực tế.

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