2009-07-18 36 views
6

Ai đó có thể giải thích cho tôi tại sao mã này chỉ in "42" thay vì "đã tạo \ n42"?Tại sao mã này chỉ in 42?

#include <iostream> 
#include <string> 
#include <memory> 

using namespace std; 

class MyClass 
{ 
public: 
    MyClass() {cout<<"created"<<endl;}; 
    int solution() {return 42;} 
    virtual ~MyClass() {}; 
}; 

int main(int argc, char *argv[]) 
{ 
    auto_ptr<MyClass> ptr; 
    cout<<ptr->solution()<<endl; 
    return 0; 
} 

BTW Tôi đã thử mã này với các giá trị khác nhau trong giải pháp và tôi luôn nhận được giá trị "đúng", vì vậy dường như không phải là giá trị may mắn ngẫu nhiên.

+13

Lời khuyên của tôi là tham khảo "Hướng dẫn về Thiên hà của Hitchhiker". – NoMoreZealots

Trả lời

27

Bởi vì nó thể hiện hành vi không xác định - bạn dereference một con trỏ null.

Khi bạn nói:

auto_ptr<MyClass> ptr; 

bạn tạo một autopointer mà không trỏ đến bất cứ điều gì. Đây là tương đương với nói:

MyClass * ptr = NULL; 

Sau đó, khi bạn nói:

cout<<ptr->solution()<<endl; 

bạn dereference con trỏ null này. Làm điều đó là không xác định trong C++ - để thực hiện của bạn, nó xuất hiện để làm việc.

+1

Đó là một giao dịch thú vị của câu trả lời được chấp nhận, haha. : P – GManNickG

+0

Nhưng tôi là người đầu tiên :-) –

+0

Ồ tôi không phàn nàn :) Tôi đã bổ sung bạn sau khi tôi gửi, tôi đã chậm vẽ. – GManNickG

2

Vì bạn không biết câu hỏi cho câu trả lời xD

Có vẻ như bạn không gọi nhà xây dựng, phải không?

+0

Đó là những gì tôi đã suy nghĩ ... – CalebHC

21

std::auto_ptr sẽ không tự động tạo đối tượng cho bạn. Tức là, ptr trong chính vì nó là viết tắt được khởi tạo là null. Dereferencing đây là hành vi không xác định, và bạn chỉ xảy ra để được may mắn và nhận được kết quả là 42.

Nếu bạn thực sự tạo ra các đối tượng:

int main(int argc, char *argv[]) 
{ 
    auto_ptr<MyClass> ptr(new MyClass); 

    cout << ptr->solution() << endl; 

    return 0; 
} 

Bạn sẽ nhận được kết quả bạn mong đợi.

+3

Tôi luôn biết rằng 42 là câu trả lời cho mọi thứ, nhưng không bao giờ nghĩ rằng con trỏ null cũng biết điều đó. Cảm ơn! BTW tại sao điều này không gây ra lỗi phân đoạn? – rlazo

+2

Bởi vì đó là những gì hành vi không xác định, nó có thể xuất hiện để làm việc, hoặc nó có thể định dạng lại máy tính của bạn. Có lẽ lý do nó làm việc là bởi vì bạn đã không thực sự hoạt động trên các thành viên của lớp. Trình biên dịch thấy bạn đang truy cập 'MyClass :: solution'. Nó đặt trong 0 cho con trỏ 'this', bởi vì đó là những gì nó, đi vào hàm, nhận được 42 kết quả, và trả về. Cho clsas của bạn một thành viên private 'int answer', đặt nó là 42 trong hàm khởi tạo và trả về trong' solution() 'và bạn sẽ thấy một sự cố, vì bây giờ bạn đang thực sự cố gắng sử dụng con trỏ null này . – GManNickG

+1

@rlazo: nó không ném một segfault bởi vì chức năng giải pháp của bạn không truy cập bất kỳ biến thành viên, do đó, con trỏ "này" đi không sử dụng bên trong chức năng. –

2

Bạn không tạo lại thể hiện đối tượng.
Bạn chỉ đang tạo con trỏ thông minh.

Khi bạn gọi phương thức bạn đang tham chiếu đến con trỏ NULL, do đó, Neil đã đề cập đến bạn hiện đang ở trong hành vi không xác định. Nhưng vì mã của bạn không thử và truy cập vào bất kỳ biến thành viên nào, nó không may mắn.

Hãy thử điều này:

auto_ptr<MyClass> ptr(new MyClass); 
+0

Tôi sẽ nói "không may mắn" thay vì "may mắn". –

1

ptr chưa được định hình và bạn may mắn. Trước tiên, bạn nên gọi số new cho số điện thoại:

auto_ptr<MyClass> ptr(new MyClass); 
1

Bạn không gặp sự cố vì phương pháp "giải pháp" không cần sử dụng thành viên lớp học. Nếu bạn đã trả lại một thành viên hoặc một cái gì đó, có thể bạn sẽ gặp sự cố.

3

Trước tiên, hãy nhớ rằng toán tử -> của auto_ptr về bản chất được chuyển tiếp đến con trỏ chứa.Vì vậy, để thảo luận này, mã của bạn trong main trở nên tương đương với:

MyClass* ptr = NULL; 
cout << ptr->solution() << endl; 

Sau đó, lưu ý rằng trình biên dịch có xu hướng để thực hiện chức năng thành viên theo cách mà hành động rất nhiều chức năng như thể chúng là phi thành viên với con trỏ this thông qua như là một đối số hàm. Vì vậy, từ thời điểm biên dịch hiện tại của bạn xem, mã của bạn trong main hành vi như thể nó là:

MyClass* ptr = NULL; 
cout << solution(ptr) << endl; 

với giải pháp viết như sau:

int solution(MyClass* this) { return 42; } 

Trong trường hợp này, nó trở nên rõ ràng lý do tại sao không có một tai nạn.


Tuy nhiên như những người khác đã đề cập, đây là chi tiết nội bộ về cách trình biên dịch triển khai C++, không được chỉ định theo tiêu chuẩn ngôn ngữ. Vì vậy, trong lý thuyết mã này có thể làm việc như mô tả ở đây trên một trình biên dịch nhưng sụp đổ hoặc làm điều gì đó hoàn toàn khác trên trình biên dịch khác.

Nhưng trong thực tế, ngay cả khi tiêu chuẩn không đảm bảo hành vi này, bất kỳ trình biên dịch cụ thể nào đều có thể đảm bảo nếu chúng muốn. Ví dụ: vì MFC dựa vào hành vi này, rất khó có khả năng Visual Studio sẽ ngừng hỗ trợ nó. Tất nhiên, bạn sẽ phải nghiên cứu từng trình biên dịch cụ thể nơi mã của bạn có thể được sử dụng để đảm bảo rằng chúng thực sự đảm bảo hành vi này.

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