Cảnh báo: Câu trả lời này gắn liền với C ++ chỉ ; các quy tắc khá khác nhau trong C.
Sẽ không x
bị rò rỉ?
Không, hoàn toàn không.
Thật hoang đường khi goto
một số cấu trúc cấp thấp cho phép bạn ghi đè các cơ chế xác định phạm vi tích hợp của C ++. (Nếu có bất cứ điều gì, nó longjmp
có thể dễ bị điều này.)
Hãy xem xét các cơ chế sau đây ngăn bạn làm "điều xấu" với nhãn (bao gồm cả case
nhãn).
1. Phạm vi nhãn
Bạn không thể chuyển qua các chức năng:
void f() {
int x = 0;
goto lol;
}
int main() {
f();
lol:
return 0;
}
// error: label 'lol' used but not defined
[n3290: 6.1/1]:
[..] Phạm vi của nhãn là chức năng mà nó xuất hiện. [..]
2. Khởi tạo đối tượng
Bạn không thể chuyển qua khởi tạo đối tượng:
int main() {
goto lol;
int x = 0;
lol:
return 0;
}
// error: jump to label ‘lol’
// error: from here
// error: crosses initialization of ‘int x’
Nếu bạn quay lại quá trình khởi tạo đối tượng, thì "phiên bản" trước đó của đối tượng sẽ bị phá hủy :
struct T {
T() { cout << "*T"; }
~T() { cout << "~T"; }
};
int main() {
int x = 0;
lol:
T t;
if (x++ < 5)
goto lol;
}
// Output: *T~T*T~T*T~T*T~T*T~T*T~T
[n3290: 6.6/2]:
[..] Chuyển ra khỏi vòng lặp, ra khỏi khối hoặc quay lại một biến đã khởi tạo với thời lượng lưu trữ tự động liên quan đến việc phá hủy các đối tượng có thời hạn lưu trữ tự động nằm trong phạm vi tại điểm được chuyển từ nhưng không phải tại điểm được chuyển đến . [..]
Bạn không thể nhảy vào phạm vi của một đối tượng, ngay cả khi nó không được khởi tạo rõ ràng:
int main() {
goto lol;
{
std::string x;
lol:
x = "";
}
}
// error: jump to label ‘lol’
// error: from here
// error: crosses initialization of ‘std::string x’
... ngoại trừ một số loại đối tượng mà ngôn ngữ có thể xử lý bất kể vì chúng không yêu cầu cấu trúc "phức tạp":
int main() {
goto lol;
{
int x;
lol:
x = 0;
}
}
// OK
[n3290: 6.7/3]:
Có thể chuyển vào một khối, nhưng không phải theo cách bỏ qua khai báo với khởi tạo. Một chương trình nhảy từ một điểm mà một biến có thời lượng lưu trữ tự động không nằm trong phạm vi đến một điểm mà nó trong phạm vi không được định hình trừ khi biến có kiểu vô hướng, kiểu lớp với một hàm tạo mặc định nhỏ và một hàm hủy tầm thường, a phiên bản đủ điều kiện cv của một trong các kiểu này hoặc một mảng của một trong các kiểu trước đó và được khai báo mà không có trình khởi tạo. [..]
3. Nhảy phụ thuộc vào phạm vi của các đối tượng khác
Tương tự như vậy, các đối tượng với thời gian lưu trữ tự động là không "rò rỉ" khi bạn goto
ra khỏi phạm vi của họ :
struct T {
T() { cout << "*T"; }
~T() { cout << "~T"; }
};
int main() {
{
T t;
goto lol;
}
lol:
return 0;
}
// *T~T
[n3290: 6.6/2]:
Khi thoát khỏi một phạm vi (tuy nhiên đã hoàn thành), các đối tượng có thời gian lưu trữ tự động (3.7.3) đã được xây dựng trong phạm vi đó sẽ bị phá hủy theo thứ tự ngược lại với quá trình xây dựng của chúng. [..]
Phần kết luận
Các cơ chế trên đảm bảo rằng goto
không cho phép bạn phá vỡ ngôn ngữ.
Tất nhiên, điều này không tự động có nghĩa là bạn "nên" sử dụng goto
cho bất kỳ vấn đề nhất định nào, nhưng nó có nghĩa là nó không gần như "xấu xa" như lầm tưởng phổ biến khiến mọi người tin tưởng.