2009-09-07 39 views
8

Tại sao mã sau đây in "xxY"? Các biến cục bộ có nên sống trong phạm vi toàn bộ hàm không? Tôi có thể sử dụng hành vi như vậy hoặc điều này sẽ được thay đổi trong tiêu chuẩn C++ trong tương lai?Câu hỏi phạm vi biến cục bộ

tôi nghĩ rằng theo tiêu chuẩn C++ 3.3.2 "Một tên tuyên bố trong một khối là địa phương để khối đó. Phạm vi tiềm năng của nó bắt đầu vào thời điểm nó khai báo và kết thúc vào cuối của khu vực khai báo của nó."

#include <iostream> 
using namespace std; 

class MyClass 
{ 
public: 
    MyClass(int) { cout << "x" << endl; }; 
    ~MyClass() { cout << "x" << endl; }; 
}; 

int main(int argc,char* argv[]) 
{ 
    MyClass (12345); 
// changing it to the following will change the behavior 
//MyClass m(12345); 
    cout << "Y" << endl; 

    return 0; 
} 

Dựa trên những phản hồi tôi có thể giả định rằng MyClass(12345); là biểu thức (và phạm vi). Điều đó có ý nghĩa. Vì vậy, tôi hy vọng rằng đoạn mã sau sẽ in "xyx" luôn:

MyClass (12345), cout << "Y" << endl; 

Và nó được phép làm thay thế như:

// this much strings with explicit scope 
{ 
    boost::scoped_lock lock(my_mutex); 
    int x = some_func(); // should be protected in multi-threaded program 
} 
// mutex released here 

//  

// I can replace with the following one string: 
int x = boost::scoped_lock (my_mutex), some_func(); // still multi-thread safe 
// mutex released here 
+2

Câu hỏi của bạn chứa câu trả lời: * Tên được khai báo ... *. Không có tên! – quamrana

+0

Trong ví dụ này: MyClass (12345) là một kiểu đúc hàm, không phải là khai báo. –

+0

Tuy nhiên, không có tên cho ví dụ – artificialidiot

Trả lời

4

Bạn đã trích dẫn chuẩn một cách chính xác. Tôi xin nhấn mạnh:

Một têntuyên bố trong một khối là địa phương để khối đó. Phạm vi tiềm năng của nó bắt đầu tại thời điểm khai báo và kết thúc vào cuối khu vực khai báo của nó.

Bạn không khai báo bất kỳ số nào tên. Bạn dòng

MyClass (12345); 

thậm chí không chứa một tuyên bố! Những gì nó chứa là một biểu thức tạo ra một thể hiện của MyClass, tính toán biểu thức (tuy nhiên, trong trường hợp cụ thể này không có gì để tính toán), và kết quả của nó thành void và hủy các đối tượng được tạo ở đó.

Một điều ít gây nhầm lẫn xin âm thanh như

call_a_function(MyClass(12345)); 

Bạn nhìn thấy nó nhiều lần và biết làm thế nào nó hoạt động, không bạn?

+0

Các thông số kỹ thuật cũng nói về phạm vi "tiềm năng". Không được bảo đảm". Vì vậy, trình biên dịch có thể phá hủy đối tượng trước đó nếu nó không được sử dụng trong phạm vi. –

+4

"phạm vi tiềm năng" có ý nghĩa chính xác: đó là phạm vi cộng với các phần mà tên bị ẩn do khai báo lại. Một triển khai không thể phá hủy một đối tượng trước đó ngay cả khi nó không được sử dụng nhiều hơn (có thể phá vỡ một số sử dụng chính của RAII btw). – AProgrammer

8

Bạn đang thực sự tạo ra một đối tượng mà không cần giữ nó trong phạm vi, vì vậy nó bị phá hủy ngay sau khi nó được tạo ra. Do đó hành vi bạn đang gặp phải.

Bạn không thể truy cập đối tượng đã tạo, vậy tại sao trình biên dịch giữ nguyên nó?

16

Đối tượng được tạo trong bạn

MyClass(12345); 

là một đối tượng tạm thời mà chỉ sống trong biểu rằng;

MyClass m(12345); 

là một đối tượng còn sống cho toàn bộ khối.

+0

Không phải * biểu thức * đó là chức năng chính? –

+1

Điều đó có vẻ đúng với tôi. Một điều khác có thể là tối ưu hóa: ngay cả khi bạn sử dụng phương thức thứ hai, trình biên dịch có thể tối ưu hóa nó thành phương thức đầu tiên. – Anna

+0

@Anna, trong trường hợp thứ hai, nó sẽ luôn in xYx. –

5

Để trả lời các câu hỏi khác của bạn. Sau đây là lời gọi của toán tử dấu phẩy.Nó tạo ra một tạm thời MyClass, bao gồm việc gọi hàm tạo của nó. Sau đó, nó sẽ đánh giá biểu thức thứ hai cout << "Y" << endl sẽ in ra Y. Sau đó, vào cuối biểu thức đầy đủ, sẽ phá hủy tạm thời, sẽ gọi hàm hủy của nó. Vì vậy, kỳ vọng của bạn là đúng.

MyClass (12345), cout << "Y" << endl; 

Để làm việc sau, bạn nên thêm dấu ngoặc đơn, vì dấu phẩy có nghĩa được xác định trước trong khai báo. Nó sẽ bắt đầu tuyên bố một hàm some_func trả về int và không có tham số và sẽ gán đối tượng scoped_lock cho x. Sử dụng dấu ngoặc đơn, bạn nói rằng toàn bộ điều là một biểu thức toán tử dấu phẩy duy nhất thay thế.

int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe 

Cần lưu ý rằng hai dòng sau là tương đương. Đầu tiên không không tạo đối tượng chưa được đặt tên tạm thời bằng cách sử dụng my_mutex làm đối số hàm tạo, nhưng thay vào đó dấu ngoặc đơn quanh tên là dư thừa. Đừng để cú pháp làm bạn bối rối.

boost::scoped_lock(my_mutex); 
boost::scoped_lock my_mutex; 

Tôi đã nhìn thấy lạm dụng phạm vi điều khoản và suốt đời.

  • Scope là nơi bạn có thể tham khảo một tên mà không hội đủ điều kiện tên của nó. Tên có phạm vi và đối tượng kế thừa phạm vi của tên được sử dụng để xác định chúng (do đó đôi khi Tiêu chuẩn cho biết "đối tượng địa phương"). Một đối tượng tạm thời không có phạm vi, bởi vì nó không có tên. Tương tự như vậy, một đối tượng được tạo bởi new không có phạm vi. Phạm vi là thuộc tính thời gian biên dịch. Thuật ngữ này thường xuyên bị lạm dụng trong tiêu chuẩn, xem this defect report, do đó, nó khá khó hiểu để tìm thấy một ý nghĩa thực sự.

  • Lifetime là thuộc tính thời gian chạy. Nó có nghĩa là khi đối tượng được thiết lập và sẵn sàng để sử dụng. Đối với một đối tượng kiểu lớp, vòng đời bắt đầu khi hàm tạo kết thúc thực thi và kết thúc khi hàm hủy bắt đầu thực hiện. Tuổi thọ thường bị nhầm lẫn với phạm vi, mặc dù hai điều này hoàn toàn khác nhau.

    Thời gian tồn tại của thời gian được xác định chính xác. Hầu hết trong số họ kết thúc cuộc đời sau khi đánh giá biểu thức đầy đủ chúng được chứa trong (như, toán tử dấu phẩy ở trên, hoặc biểu thức gán). Thời gian có thể được ràng buộc để tham chiếu const sẽ kéo dài tuổi thọ của họ. Các đối tượng bị ném vào trường hợp ngoại lệ cũng là thời gian, và cuộc đời của họ kết thúc khi không có người xử lý cho họ nữa.

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