Đối với một số ngôn ngữ (tức là C ++) Rò rỉ tài nguyên không phải là lý do
C ++ dựa trên RAII.
Nếu bạn có mã mà có thể thất bại, trả lại hoặc ném (có nghĩa là, hầu hết các mã bình thường), sau đó bạn nên có con trỏ của bạn được bao bọc bên trong một con trỏ thông minh (giả sử bạn có một rất tốt lý do gì để không có đối tượng của bạn tạo ra trên stack).
Mã trả lại dài hơn
Chúng dài dòng và có xu hướng phát triển thành một cái gì đó như:
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
Cuối cùng, mã của bạn là một tập hợp các hướng dẫn được nhận dạng (tôi đã thấy loại mã này trong mã sản xuất).
Mã này cũng có thể được dịch thành:
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
Việc tách biệt rõ ràng mã và xử lý lỗi, đó có thể là một điều tốt .
Mã trả lại giòn hơn
Nếu không phải là một số cảnh báo khó hiểu từ một trình biên dịch (xem bình luận của "phjr"), chúng có thể dễ dàng bị bỏ qua.
Với các ví dụ trên, giả sử hơn ai đó quên xử lý lỗi có thể xảy ra (điều này xảy ra ...). Lỗi được bỏ qua khi "trả về", và có thể sẽ phát nổ sau đó (tức là con trỏ NULL). Vấn đề tương tự sẽ không xảy ra ngoại lệ.
Lỗi sẽ không được bỏ qua. Đôi khi, bạn muốn nó không phát nổ, nhưng bạn phải lựa chọn cẩn thận.
Mã trở lại đôi khi phải được dịch
Giả sử chúng ta có các chức năng sau:
- doSomething, có thể trả về một int được gọi là NOT_FOUND_ERROR
- doSomethingElse, có thể trả về bool "false" (không thành công)
- doSomethingElseAgain, có thể trả về đối tượng Lỗi (với cả __LINE__, __FILE__ và một nửa các biến ngăn xếp.
- doTryToDoSomethingWithAllThisMess ... Sử dụng các hàm trên và trả về mã lỗi thuộc loại ...
Kiểu trả về của doTryToDoSomethingWithAllThisMess là gì nếu một trong các hàm được gọi của nó bị lỗi?
Mã trở lại không phải là một giải pháp phổ biến
Các nhà khai thác không thể trả lại mã lỗi. Các hàm tạo C ++ cũng không thể.
Mã trả lại có nghĩa là bạn không thể chuỗi các biểu thức
Hệ quả của điểm trên. Nếu tôi muốn viết:
CMyType o = add(a, multiply(b, c)) ;
Tôi không thể, vì giá trị trả về đã được sử dụng (và đôi khi, không thể thay đổi được). Vì vậy, giá trị trả về trở thành tham số đầu tiên, được gửi dưới dạng tham chiếu ... Hoặc không.
Ngoại lệ được nhập
Bạn có thể gửi các lớp khác nhau cho từng loại ngoại lệ. Các ngoại lệ Ressources (tức là hết bộ nhớ) phải nhẹ, nhưng bất kỳ thứ gì khác cũng có thể nặng đến mức cần thiết (Tôi thích Java Exception cho tôi toàn bộ ngăn xếp).
Mỗi lần đánh bắt sau đó có thể được chuyên biệt hóa.
Đừng bao giờ sử dụng catch (...) mà không ném lại
Thông thường, bạn không nên ẩn lỗi. Nếu bạn không ném lại, ít nhất, hãy ghi lại lỗi trong một tệp, mở hộp thư, bất cứ điều gì ...
Ngoại lệ là ... NUKE
Vấn đề với ngoại lệ là lạm dụng chúng sẽ tạo ra mã đầy thử / bắt. Nhưng vấn đề nằm ở chỗ khác: Ai thử / bắt mã của anh ấy / cô ấy bằng cách sử dụng STL container? Tuy nhiên, những vùng chứa đó có thể gửi một ngoại lệ.
Tất nhiên, trong C ++, đừng bao giờ để một ngoại lệ thoát khỏi hàm hủy.
Ngoại lệ là ... đồng bộ
Hãy chắc chắn nắm bắt chúng trước khi chúng đưa chuỗi của bạn ra ngoài hoặc lan truyền bên trong vòng lặp thông báo Windows của bạn.
Giải pháp có thể là trộn chúng?
Vì vậy, tôi đoán giải pháp là ném khi điều gì đó không nên xảy ra. Và khi điều gì đó có thể xảy ra, hãy sử dụng mã trả về hoặc một tham số để cho phép người dùng phản ứng với nó.
Vì vậy, câu hỏi duy nhất là "điều gì không nên xảy ra?"
Nó phụ thuộc vào hợp đồng của chức năng của bạn. Nếu hàm chấp nhận một con trỏ, nhưng chỉ định con trỏ phải không phải là NULL, thì có thể ném một ngoại lệ khi người dùng gửi một con trỏ NULL (câu hỏi là, trong C ++, tác giả hàm đã không sử dụng tham chiếu thay thế khi nào của con trỏ, nhưng ...)
Một giải pháp khác là hiển thị lỗi
Đôi khi, vấn đề của bạn là bạn không muốn có lỗi. Sử dụng ngoại lệ hoặc mã trả về lỗi rất tuyệt, nhưng ... Bạn muốn biết về nó.
Trong công việc của tôi, chúng tôi sử dụng một loại "Khẳng định". Tùy thuộc vào giá trị của tệp cấu hình, nó sẽ tùy thuộc vào các tùy chọn biên dịch gỡ lỗi / phát hành:
- ghi lại lỗi
- mở hộp tin nhắn với thông báo "Này, bạn gặp sự cố"
- mở một hộp thư với thông báo "Này, bạn gặp sự cố, bạn có muốn gỡ lỗi không"
Trong cả quá trình phát triển và thử nghiệm, điều này cho phép người dùng xác định chính xác vấn đề khi nó được phát hiện chứ không phải sau khi (khi một số mã quan tâm đến giá trị trả về hoặc bên trong một lệnh bắt).
Nó rất dễ dàng để thêm vào mã kế thừa. Ví dụ:
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
dẫn một loại mã tương tự như:
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(Tôi có các macro tương tự chỉ hoạt động khi gỡ lỗi).
Lưu ý rằng trên sản xuất, tệp cấu hình không tồn tại, vì vậy khách hàng không bao giờ nhìn thấy kết quả của macro này ... Nhưng có thể dễ dàng kích hoạt nó khi cần.
Phần kết luận
Khi bạn viết mã bằng mã trả lại, bạn đang chuẩn bị cho sự thất bại và hy vọng pháo đài kiểm tra của bạn đủ an toàn.
Khi bạn viết mã bằng cách sử dụng ngoại lệ, bạn biết rằng mã của bạn có thể bị lỗi và thường đặt chốt chặn phản công ở vị trí chiến lược đã chọn trong mã của bạn. Nhưng thông thường, mã của bạn thiên về "những gì nó phải làm" rồi "những gì tôi sợ sẽ xảy ra".
Nhưng khi bạn viết mã, bạn phải sử dụng công cụ tốt nhất theo ý của mình, và đôi khi, đó là "Không bao giờ ẩn lỗi, và hiển thị nó càng sớm càng tốt". Cái vĩ mô mà tôi đã nói ở trên tuân theo triết lý này.