Thời gian chạy xử lý ngoại lệ C ++ được thực hiện như thế nào?


84

Tôi bị hấp dẫn bởi cách hoạt động của cơ chế xử lý ngoại lệ C ++. Cụ thể, đối tượng ngoại lệ được lưu trữ ở đâu và nó lan truyền qua nhiều phạm vi như thế nào cho đến khi bị bắt? Nó có được lưu trữ trong một số khu vực toàn cầu không?

Vì đây có thể là trình biên dịch cụ thể, ai đó có thể giải thích điều này trong ngữ cảnh của bộ trình biên dịch g ++ không?


4
Đọc bài viết này sẽ giúp bạn
Ahmed Đã nói

Tôi không biết - nhưng tôi đoán rằng thông số C ++ có một định nghĩa rõ ràng. (Mặc dù vậy tôi có thể sai)
Paul Nathan

2
Không, thông số kỹ thuật không đưa ra định nghĩa. Nó ra lệnh cho hành vi, không phải việc thực hiện. Paul, bạn có thể muốn xác định thực hiện bạn đang quan tâm.
Rob Kennedy


Câu trả lời:


49

Cách triển khai có thể khác nhau, nhưng có một số ý tưởng cơ bản tuân theo các yêu cầu.

Bản thân đối tượng ngoại lệ là một đối tượng được tạo trong một hàm, bị phá hủy trong trình gọi của nó. Do đó, thường không khả thi để tạo đối tượng trên ngăn xếp. Mặt khác, nhiều đối tượng ngoại lệ không lớn lắm. Ergo, người ta có thể tạo ví dụ như một bộ đệm 32 byte và tràn lên đống nếu thực sự cần một đối tượng ngoại lệ lớn hơn.

Đối với việc chuyển giao quyền kiểm soát thực tế, tồn tại hai chiến lược. Một là ghi lại đủ thông tin trong chính ngăn xếp để giải phóng ngăn xếp. Về cơ bản, đây là danh sách các trình hủy để chạy và các trình xử lý ngoại lệ có thể bắt ngoại lệ. Khi một ngoại lệ xảy ra, hãy chạy lại ngăn xếp đang thực thi các hàm hủy đó cho đến khi bạn tìm thấy một lệnh bắt phù hợp.

Chiến lược thứ hai di chuyển thông tin này vào các bảng bên ngoài ngăn xếp. Bây giờ, khi một ngoại lệ xảy ra, ngăn xếp cuộc gọi được sử dụng để tìm ra phạm vi nào được nhập nhưng không thoát ra. Sau đó, chúng được tra cứu trong các bảng tĩnh để xác định nơi mà ngoại lệ đã ném sẽ được xử lý và các hàm hủy nào chạy ở giữa. Điều này có nghĩa là có ít chi phí ngoại lệ hơn trên ngăn xếp; địa chỉ trả lại vẫn cần thiết. Các bảng là dữ liệu bổ sung, nhưng trình biên dịch có thể đưa chúng vào một phân đoạn được tải theo yêu cầu của chương trình.


4
AFAIR g ++ sử dụng cách tiếp cận bảng địa chỉ thứ hai, có lẽ vì lý do tương thích với C. Trình biên dịch C ++ của Microsoft sử dụng cách tiếp cận kết hợp, vì các ngoại lệ C ++ của nó được xây dựng dựa trên SEH (xử lý ngoại lệ có cấu trúc). Trong mỗi hàm C ++, MSC ++ tạo và đăng ký một bản ghi xử lý ngoại lệ SEH, bản ghi này trỏ đến một bảng có dải địa chỉ cho các khối try-catch và hàm hủy trong hàm cụ thể này. ném các gói một ngoại lệ C ++ như một ngoại lệ SEH và gọi RaiseException (), sau đó SEH trả về quyền điều khiển cho quy trình xử lý cụ thể của C ++.
Anton Tykhyy

1
@Anton: vâng, nó sử dụng cách tiếp cận bảng địa chỉ. Xem câu trả lời của tôi cho một câu hỏi khác tại stackoverflow.com/questions/307610/… để biết chi tiết.
CesarB

Cảm ơn vì câu trả lời. Bạn có thể thấy những người theo chủ nghĩa thuần túy C có thể khiếp sợ như thế nào đối với C ++ và các ngoại lệ của nó. Ý tưởng rằng một lần thử / bắt đơn giản có thể vô tình tạo ra một số đối tượng ngăn xếp trong thời gian chạy hoặc làm phình chương trình của bạn với các bảng phụ là lý do tại sao các hệ thống nhúng thường tránh chúng.
bay cao tốc

@speedplane: Không, đó là do sự thiếu hiểu biết. Xử lý lỗi không bao giờ là miễn phí. C chỉ buộc bạn phải tự viết nó. Và chúng ta đều biết có bao nhiêu chương trình C bị thiếu a free()hoặc a fclose()trong một số đường dẫn mã hiếm khi được sử dụng.
MSalters

@MSalters Tôi không đồng ý, nó gần như hoàn toàn là sự thiếu hiểu biết. Các kỹ sư thường không hiểu cách hoạt động của các ngoại lệ và cách các ngoại lệ sẽ ảnh hưởng đến mã của họ, do đó, chính đáng, dẫn đến sự do dự khi sử dụng các ngoại lệ. Nếu việc triển khai xử lý ngoại lệ được truyền đạt rõ ràng hơn (và không có vẻ như là phép thuật), nhiều người sẽ bớt do dự khi sử dụng chúng.
bay cao tốc

20

Điều này được định nghĩa trong 15.1 Ném một ngoại lệ của tiêu chuẩn.

Cú ném tạo ra một đối tượng tạm thời.
Làm thế nào bộ nhớ cho đối tượng tạm thời này được cấp phát là không xác định.

Sau khi tạo điều khiển đối tượng tạm thời được chuyển cho trình xử lý gần nhất trong ngăn xếp cuộc gọi. mở ngăn xếp giữa điểm ném và điểm bắt. Khi ngăn xếp được mở ra, mọi biến ngăn xếp sẽ bị phá hủy theo thứ tự ngược lại của quá trình tạo.

Trừ khi ngoại lệ được ném lại, phần tạm thời sẽ bị phá hủy ở cuối trình xử lý nơi nó bị bắt.

Lưu ý: Nếu bạn bắt bằng tham chiếu, tham chiếu sẽ tham chiếu đến tạm thời, Nếu bạn bắt theo giá trị, đối tượng tạm thời được sao chép vào giá trị (và do đó yêu cầu một hàm tạo bản sao).

Lời khuyên từ S.Meyers (Catch by const reference).

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}

3
Một cái gì đó khác chưa được xác định là cách chương trình tháo ngăn xếp và cách chương trình biết "trình xử lý gần nhất" ở đâu. Tôi khá chắc chắn Borland có bằng sáng chế về một phương pháp thực hiện điều đó.
Rob Kennedy

Miễn là các đối tượng bị phá hủy theo thứ tự ngược lại của quá trình tạo, các chi tiết triển khai không quan trọng trừ khi bạn là một kỹ sư biên dịch.
Martin York

1
Đã bỏ phiếu: a) "Scott Meyers", không phải "S. Myers"; b) trích dẫn không đúng sự thật: "C ++ hiệu quả": "Mục 13: Bắt ngoại lệ bằng cách tham khảo. ". Điều này sẽ cho phép điều chỉnh / bổ sung thông tin vào đối tượng ngoại lệ.
Sebastian Mach

3
@phresnel: Đừng quên Mục 21: "Sử dụng const bất cứ khi nào có thể". Không có trường hợp tốt để điều chỉnh một ngoại lệ. Bạn nên a) "sửa chữa và loại bỏ", b) ném lại hoặc c) tạo ra một ngoại lệ mới.
Martin York

1
@phresnel: Vâng, bạn có lý do của bạn (không đồng ý với logic của bạn), tôi có lý do của tôi và mặc dù tôi sẽ không tuyên bố đã nói chuyện với họ về chủ đề cụ thể này hoặc thực sự biết tâm trí của họ (Meyers, Alexandrescu và Sutter) Tôi tin giải thích của tôi có giá trị. Nhưng nếu bạn ở khu vực Seattle thì bạn có thể nói chuyện với cả ba người vì họ là những người tham dự thường xuyên tại Nhóm người dùng C ++ North West (ít Meyers hơn những người khác).
Martin York

13

Bạn có thể xem ở đây để được giải thích chi tiết.

Nó cũng có thể hữu ích khi xem xét một thủ thuật được sử dụng trong C đơn giản để thực hiện một số loại xử lý ngoại lệ cơ bản. Điều này đòi hỏi phải sử dụng setjmp () và longjmp () theo cách sau: cái trước lưu ngăn xếp để đánh dấu trình xử lý ngoại lệ (như "catch"), trong khi cái sau được sử dụng để "ném" một giá trị. Giá trị "được ném" được xem như thể nó đã được trả về từ một hàm được gọi. "Khối thử" kết thúc khi setjmp () được gọi lại hoặc khi hàm trả về.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.