2011-09-01 37 views
37

Tôi đã thực hiện một số nghiên cứu sau khi học new, không giống như malloc() mà tôi đã sử dụng, không trả về NULL cho phân bổ không thành công và thấy có hai cách riêng biệt kiểm tra xem liệu mới có thành công hay không. Những hai cách là:Mới (std :: nothrow) so với New trong khối try/catch

try 
{ 
    ptr = new int[1024]; 
} 
catch(std::bad_alloc& exc) 
{ 
    assert(); 
}; 

ptr = new (std::nothrow) int[1024]; 
if(ptr == NULL) 
    assert(); 

Tôi tin rằng hai cách đạt được cùng một mục tiêu, (! Đúng cho tôi nếu tôi sai dĩ nhiên), vì vậy câu hỏi của tôi là thế này:

là tùy chọn tốt hơn để kiểm tra xem new có thành công hay không, hoàn toàn dựa trên khả năng đọc, bảo trì và hiệu suất, trong khi bỏ qua quy ước lập trình C++ không thực tế.

+2

Sử dụng ngoại lệ. Chúng làm cho mã của bạn có ý nghĩa hơn, cục bộ, có thể duy trì và có thể mở rộng. –

+3

Về khả năng đọc chỉ, chắc chắn là không có xử lý ngoại lệ. Về hiệu suất, có thể cũng là 'nothrow'. Nhưng tôi chắc chắn rằng những người vượt trội sẽ tranh cãi. Nhưng nếu bạn chỉ muốn một lỗi xác nhận đơn giản, bạn cũng có thể sử dụng biến thể ném và bỏ qua trình xử lý ngoại lệ;) –

+4

Điểm của mã đó là gì? Bạn đang bắt một ngoại lệ cụ thể, có ý nghĩa và sau đó tạo ra một cái vô nghĩa thông qua 'assert()' ... – Blindy

Trả lời

47

Hãy xem xét những gì bạn đang làm. Bạn đang cấp phát bộ nhớ. Và nếu vì lý do nào đó, việc cấp phát bộ nhớ không thể hoạt động, bạn assert. Đó là nhiều hơn hoặc ít hơn chính xác những gì sẽ xảy ra nếu bạn chỉ cần để cho các std::bad_alloc tuyên truyền trở lại main. Trong bản phát hành bản phát hành, trong đó assert là chương trình không hoạt động, chương trình của bạn sẽ bị lỗi khi cố gắng truy cập vào bộ nhớ. Vì vậy, nó cũng giống như để cho các trường hợp ngoại lệ bong bóng lên: tạm dừng ứng dụng.

Vì vậy, hãy tự hỏi mình một câu hỏi: Bạn có thực sự cần phải quan tâm điều gì xảy ra nếu bạn hết bộ nhớ? Nếu tất cả những gì bạn đang làm là khẳng định, thì phương pháp ngoại lệ là tốt hơn, bởi vì nó không làm lộn xộn mã của bạn với ngẫu nhiên assert s. Bạn chỉ để cho ngoại lệ rơi trở lại main.

Nếu thực tế bạn có một bản mã đặc biệt trong trường hợp bạn không thể cấp phát bộ nhớ (nghĩa là bạn có thể tiếp tục hoạt động), ngoại lệ có thể hoặc không phải là cách để đi, tùy thuộc vào hệ thống . Nếu codepath chỉ là một switch được thiết lập bằng cách có một con trỏ là null, thì phiên bản nothrow sẽ đơn giản hơn.Nếu thay vào đó, bạn cần phải làm một cái gì đó khá khác nhau (kéo từ một bộ đệm tĩnh, hoặc xóa một số thứ, hoặc bất cứ thứ gì), sau đó bắt std::bad_alloc là khá tốt.

+3

Đây là một lý lẽ khá thuyết phục, tôi không biết ngoại lệ có thể leo lên ngăn xếp cuộc gọi để tìm một khối catch. ... Tôi không biết nhiều ngoại lệ có thể làm. Có thể là thời gian cuối cùng tôi cũng học được sau tất cả những năm này, aha –

10

Tùy thuộc vào ngữ cảnh phân bổ đang diễn ra ở đâu. Nếu chương trình của bạn có thể tiếp tục ngay cả khi phân bổ không thành công (có thể trả lại mã lỗi cho người gọi), hãy sử dụng phương thức std::nothrow và kiểm tra NULL. Nếu không, bạn sẽ sử dụng các ngoại lệ cho luồng điều khiển, đó là cách thực hành không tốt. Mặt khác, nếu chương trình của bạn hoàn toàn cần phải có bộ nhớ được phân bổ thành công để có thể hoạt động, hãy sử dụng try-catch để bắt (không nhất thiết ở vùng lân cận của new) một ngoại lệ và thoát ra một cách duyên dáng từ chương trình.

+0

Tôi không cân nhắc điều đó, tôi cho rằng việc trộn và kết hợp với tình huống sẽ không phải là kiểu xấu trong trường hợp đó. Rõ ràng hai cách tồn tại vì lý do đó. Cảm ơn! –

+4

Đó là một câu trả lời hay. Nó tóm tắt toàn bộ triết lý ngoại lệ: Sử dụng ngoại lệ cho các tình huống ngoại lệ và bắt chúng ở nơi chúng có thể được xử lý một cách có ý nghĩa. Mặt khác, đối với một tình huống thời gian chạy không đặc biệt mà bạn có thể và muốn xử lý cục bộ, không sử dụng các ngoại lệ. –

6

Từ góc độ hiệu suất thuần túy, điều đó rất quan trọng. Có chi phí vốn có với xử lý ngoại lệ, mặc dù chi phí này thường đáng để giao dịch trong khả năng đọc và bảo trì ứng dụng. Lỗi phân bổ bộ nhớ của bản chất này không được trong trường hợp 99% của đơn đăng ký của bạn, vì vậy điều này sẽ xảy ra không thường xuyên.

Từ góc độ hiệu suất, bạn thường muốn tránh trình phân bổ chuẩn do hiệu suất tương đối kém. Tất cả điều này nói rằng, tôi thường chấp nhận phiên bản ném ngoại lệ vì thông thường các ứng dụng của chúng tôi ở trạng thái nếu phân bổ bộ nhớ không thành công, chúng ta có thể làm việc khác ngoài thoát một cách duyên dáng với thông báo lỗi thích hợp. không yêu cầu NULL kiểm tra tài nguyên mới được phân bổ của chúng tôi bởi vì theo định nghĩa, lỗi phân bổ sẽ di chuyển phạm vi ra khỏi vị trí quan trọng.

+0

Vì vậy, các hình phạt hiệu suất của trường hợp ngoại lệ sẽ ít hơn kiểm tra bình đẳng của con trỏ, giả sử không có gì sai? Nếu đúng như vậy, tôi không thấy lý do gì để không sử dụng ngoại lệ ngay bây giờ. (Và vâng, tôi cố gắng bọc tất cả các nhu cầu phân bổ cho một bộ phân bổ đối tượng nhỏ, giữ tất cả các bộ nhớ trong một chỗ gọn gàng) –

+0

Vẫn còn một số hình phạt khi sử dụng ngoại lệ (ngôn ngữ của bạn), chẳng hạn như cài đặt lên ngăn xếp một cách thích hợp, xử lý SEH, vv Nói chung tôi sẽ không mong đợi có một sự khác biệt có thể đo lường giữa hai trong một trường hợp mà "không có gì sai". Trong trường hợp thất bại mã ngoại lệ sẽ được nhiều hơn nữa tham gia, nhưng nó làm sạch mã bằng cách không yêu cầu kiểm tra 'NULL'. Nó thực sự là một vấn đề sở thích cá nhân bởi điểm đó mặc dù. – Chad

+2

Trong việc thực hiện GCC, ngoại lệ không chi phí cho bạn bất cứ điều gì (ngoài việc có lẽ là một địa phương bộ nhớ hơi tệ hơn) nếu chúng không được sử dụng - bạn chỉ trả tiền khi ngoại lệ thực sự bị ném. –

-3

new được sử dụng để tạo đối tượng, không cấp phát bộ nhớ, do đó ví dụ của bạn có phần nhân tạo.

Trình tạo đối tượng thường ném nếu chúng không thành công. Có bước qua thực hiện new trong Visual Studio nhiều hơn một vài lần, tôi không tin rằng mã bắt bất kỳ trường hợp ngoại lệ. Do đó, việc tìm kiếm các ngoại lệ khi tạo đối tượng là rất hợp lý.

I nghĩstd::bad_alloc chỉ được ném nếu phần cấp phát bộ nhớ không thành công. Tôi không chắc chắn điều gì sẽ xảy ra nếu bạn vượt qua std::nothrow đến new nhưng trình tạo đối tượng sẽ ném - có sự mơ hồ trong tài liệu tôi đã đọc.

Sự khác biệt về hiệu suất giữa 2 phương pháp tiếp cận có lẽ không liên quan vì hầu hết thời gian xử lý có thể dễ dàng được chi tiêu trong hàm tạo đối tượng hoặc tìm kiếm vùng heap.

Quy tắc không phải lúc nào cũng phù hợp. Ví dụ, các hệ thống thời gian thực thường hạn chế phân bổ bộ nhớ động, vì vậy new, nếu có, có lẽ sẽ bị quá tải. Trong trường hợp đó, nó có thể sử dụng một con trỏ null trả về và xử lý thất bại cục bộ.

+0

Nếu đối tượng chính của bạn có subobject, thì những người đó cũng có thể ném bad_alloc nếu họ cấp phát bộ nhớ ... vì vậy bạn sẽ không biết chắc chắn nó có phải là phân bổ chính thất bại. Tuy nhiên, bạn không quan tâm vì đối tượng không thể sử dụng được và trình biên dịch sẽ xử lý việc dọn dẹp các đối tượng được xây dựng một phần. ** Tôi khuyên bạn nên học ngôn ngữ đầu tiên ** trước khi đưa ra câu trả lời tùy ý gây hiểu nhầm. Đặc biệt, đề xuất của bạn để tìm ngoại lệ khi tạo đối tượng là xấu nếu nó có nghĩa là đặt thử/bắt xung quanh mọi phân bổ. – Phil1970

+0

Tôi sẽ đề xuất bạn đọc ** Trên cả C++ ** và các sách tương tự khác trước khi viết bất kỳ mã nào được sử dụng trong các ứng dụng chuyên nghiệp. – Phil1970

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