Là khẳng định cái ác? [đóng cửa]


199

Những Gongười sáng tạo ngôn ngữ viết :

Go không cung cấp xác nhận. Chúng không thể phủ nhận tiện lợi, nhưng kinh nghiệm của chúng tôi là các lập trình viên sử dụng chúng như một cái nạng để tránh suy nghĩ về việc xử lý lỗi và báo cáo thích hợp. Xử lý lỗi thích hợp có nghĩa là các máy chủ tiếp tục hoạt động sau các lỗi không nghiêm trọng thay vì sự cố. Báo cáo lỗi đúng có nghĩa là lỗi là trực tiếp và đến điểm, lưu lập trình viên khỏi việc diễn giải một dấu vết sự cố lớn. Lỗi chính xác đặc biệt quan trọng khi lập trình viên nhìn thấy lỗi không quen thuộc với mã.

Ý kiến ​​của bạn về điều này là gì?


4
tiếp tuyến: Go là một ngôn ngữ có ý kiến ​​bất thường. Đây không hẳn là một điều xấu. Tuy nhiên, điều đó có nghĩa là bạn nên lấy ý kiến ​​của mình bằng một hạt muối lớn hơn. Điều đó cũng có nghĩa là nếu bạn không đồng ý, bạn sẽ nghiến răng khi bạn sử dụng ngôn ngữ. Bằng chứng là làm thế nào Go bám vào ý kiến ​​của nó bất chấp thực tế, xem xét rằng bạn cần phải sử dụng phép thuật phản chiếu để xác định xem hai bộ sưu tập có bằng nhau không.
mã allyour

@allyourcode Nếu bạn đang đề cập đến reflect.DeepEqual, bạn chắc chắn không cần nó. Thật tiện lợi, nhưng với chi phí hiệu năng (bài kiểm tra đơn vị là trường hợp sử dụng tốt). Mặt khác, bạn có thể thực hiện bất kỳ kiểm tra bình đẳng nào phù hợp với "bộ sưu tập" của mình mà không gặp quá nhiều khó khăn.
Igor Dubinskiy

1
Không, đó không phải là điều tôi đang nói. Không có thứ gọi là lát1 == lát2 mà không có phản xạ. Tất cả các ngôn ngữ khác có tương đương với hoạt động siêu cơ bản này. Lý do duy nhất Go không thành kiến.
mã đồng minh

Bạn có thể so sánh hai lát mà không có sự phản chiếu bằng cách sử dụng một forvòng lặp trong Go (giống như C). Nó sẽ được thực sự tốt đẹp để có hoạt động lát chung chung, mặc dù so sánh trở nên phức tạp khi con trỏ và cấu trúc có liên quan.
kbolino

Câu trả lời:


321

Không, không có gì sai với điều kiện assertbạn sử dụng nó như dự định.

Đó là, nó được cho là để bắt các trường hợp "không thể xảy ra", trong quá trình gỡ lỗi, trái với xử lý lỗi thông thường.

  • Khẳng định: Một thất bại trong chính logic của chương trình.
  • Xử lý lỗi: Một đầu vào hệ thống hoặc trạng thái lỗi không phải do lỗi trong chương trình.

109

Không, cũng không gotophải assertlà xấu xa. Nhưng cả hai có thể bị lạm dụng.

Khẳng định là để kiểm tra sự tỉnh táo. Những thứ nên giết chương trình nếu chúng không đúng. Không phải để xác nhận hoặc thay thế để xử lý lỗi.


Làm thế nào để sử dụng gotomột cách khôn ngoan?
ar2015

1
@ ar2015 Tìm một trong những mô hình vô lý mà một số người khuyên nên tránh gotovì lý do tôn giáo hoàn toàn, sau đó chỉ sử dụng gotothay vì làm xáo trộn những gì bạn đang làm. Nói cách khác: nếu bạn có thể chứng minh rằng bạn thực sự cần goto, và cách duy nhất là ban hành một loạt các giàn giáo vô nghĩa mà cuối cùng cũng làm điều tương tự nhưng không làm thay đổi Cảnh sát Goto ... thì chỉ cần sử dụng goto. Tất nhiên, một điều kiện tiên quyết của điều này là một chút "nếu bạn có thể chứng minh rằng bạn thực sự cần goto". Thông thường, mọi người không. Điều đó vẫn không có nghĩa là nó vốn dĩ là một điều xấu.
underscore_d

2
gotođược sử dụng trong kernel linux để dọn mã
malat

61

Theo logic đó, điểm dừng cũng là xấu xa.

Các xác nhận nên được sử dụng như một trợ giúp gỡ lỗi, và không có gì khác. "Ác" là khi bạn thử sử dụng chúng thay vì xử lý lỗi.

Các xác nhận có mặt để giúp bạn, lập trình viên, phát hiện và khắc phục các sự cố không tồn tại và xác minh rằng các giả định của bạn là đúng.

Họ không có gì để xử lý lỗi, nhưng thật không may, một số lập trình viên lạm dụng chúng như vậy, và sau đó tuyên bố họ là "ác quỷ".


40

Tôi thích sử dụng khẳng định rất nhiều. Tôi thấy nó rất hữu ích khi tôi xây dựng ứng dụng lần đầu tiên (có lẽ cho một tên miền mới). Thay vì thực hiện kiểm tra lỗi rất lạ mắt (rằng tôi sẽ xem xét tối ưu hóa sớm) tôi viết mã nhanh và tôi thêm rất nhiều xác nhận. Sau khi tôi biết thêm về cách mọi thứ hoạt động, tôi viết lại và xóa một số xác nhận và thay đổi chúng để xử lý lỗi tốt hơn.

Bởi vì các xác nhận tôi dành ít thời gian hơn cho các chương trình mã hóa / gỡ lỗi.

Tôi cũng nhận thấy rằng các xác nhận giúp tôi nghĩ ra nhiều điều có thể phá vỡ các chương trình của tôi.


31

Là một thông tin bổ sung, go cung cấp một chức năng tích hợp panic. Điều này có thể được sử dụng thay thế assert. Ví dụ

if x < 0 {
    panic("x is less than 0");
}

panicsẽ in dấu vết ngăn xếp, vì vậy theo một cách nào đó, nó có mục đích assert.


30

Chúng nên được sử dụng để phát hiện lỗi trong chương trình. Không phải người dùng đầu vào xấu.

Nếu sử dụng đúng cách, chúng không xấu.


13

Điều này xuất hiện rất nhiều, và tôi nghĩ một vấn đề khiến cho sự bảo vệ của các xác nhận trở nên khó hiểu là chúng thường dựa trên việc kiểm tra lập luận. Vì vậy, hãy xem xét ví dụ khác nhau này khi bạn có thể sử dụng một xác nhận:

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

Bạn sử dụng một ngoại lệ cho đầu vào vì đôi khi bạn sẽ nhận được đầu vào xấu. Bạn khẳng định rằng danh sách được sắp xếp để giúp bạn tìm ra lỗi trong thuật toán của mình, theo định nghĩa mà bạn không mong đợi. Khẳng định chỉ có trong bản dựng gỡ lỗi, vì vậy mặc dù kiểm tra rất tốn kém, bạn không ngại thực hiện nó trên mỗi lần gọi của thói quen.

Bạn vẫn phải kiểm tra đơn vị mã sản xuất của mình, nhưng đó là một cách khác nhau và bổ sung để đảm bảo mã của bạn là chính xác. Kiểm tra đơn vị đảm bảo rằng thói quen của bạn tuân theo giao diện của nó, trong khi các xác nhận là một cách chi tiết hơn để đảm bảo việc triển khai của bạn thực hiện chính xác những gì bạn mong đợi.


8

Khẳng định không xấu xa nhưng chúng có thể dễ dàng bị lạm dụng. Tôi đồng ý với tuyên bố rằng "các xác nhận thường được sử dụng như một cái nạng để tránh suy nghĩ về việc xử lý và báo cáo lỗi thích hợp". Tôi đã thấy điều này khá thường xuyên.

Cá nhân, tôi thích sử dụng các xác nhận vì chúng ghi lại các giả định mà tôi có thể đã thực hiện trong khi viết mã của mình. Nếu các giả định này bị hỏng trong khi duy trì mã, vấn đề có thể được phát hiện trong quá trình thử nghiệm. Tuy nhiên, tôi thực hiện quan điểm tước bỏ mọi khẳng định từ mã của mình khi thực hiện xây dựng sản xuất (nghĩa là sử dụng #ifdefs). Bằng cách loại bỏ các xác nhận trong bản dựng sản xuất, tôi loại bỏ nguy cơ bất cứ ai lạm dụng chúng như một cái nạng.

Ngoài ra còn có một vấn đề khác với các xác nhận. Các xác nhận chỉ được kiểm tra tại thời điểm chạy. Nhưng nó thường là trường hợp kiểm tra bạn muốn thực hiện có thể đã được thực hiện tại thời gian biên dịch. Tốt nhất là phát hiện một vấn đề tại thời gian biên dịch. Đối với các lập trình viên C ++, boost cung cấp BOOST_STATIC_ASSERT cho phép bạn làm điều này. Đối với các lập trình viên C, bài viết này ( văn bản liên kết ) mô tả một kỹ thuật có thể được sử dụng để thực hiện các xác nhận tại thời điểm biên dịch.

Tóm lại, quy tắc ngón tay cái tôi tuân theo là: Không sử dụng các xác nhận trong bản dựng sản xuất và, nếu có thể, chỉ sử dụng các xác nhận cho những thứ không thể được xác minh tại thời gian biên dịch (nghĩa là phải được kiểm tra trong thời gian chạy).


5

Tôi thừa nhận đã sử dụng các xác nhận trong khi không xem xét báo cáo lỗi thích hợp. Tuy nhiên, điều đó không làm mất đi rằng chúng rất hữu ích khi được sử dụng đúng cách.

Chúng đặc biệt hữu ích nếu bạn muốn tuân theo nguyên tắc "Sụp đổ sớm". Ví dụ: giả sử bạn đang thực hiện một cơ chế đếm tham chiếu. Tại một số vị trí nhất định trong mã của bạn, bạn biết rằng số tiền phải là 0 hoặc 1. Và cũng giả sử rằng nếu số lần trả lại sai, chương trình sẽ không bị sập ngay lập tức nhưng trong vòng lặp thông báo tiếp theo, tại thời điểm đó, sẽ rất khó để tìm ra lý do tại sao mọi thứ đã sai. Một khẳng định sẽ có ích trong việc phát hiện lỗi gần với nguồn gốc của nó.


5

Tôi thích tránh mã làm những việc khác nhau trong gỡ lỗi và phát hành.

Việc đột nhập vào trình gỡ lỗi với một điều kiện và có tất cả thông tin về tệp / dòng là hữu ích, cũng là biểu thức chính xác và giá trị chính xác.

Có một khẳng định rằng "chỉ đánh giá điều kiện trong gỡ lỗi" có thể là tối ưu hóa hiệu suất và như vậy, chỉ hữu ích trong 0,0001% chương trình - nơi mọi người biết họ đang làm gì. Trong tất cả các trường hợp khác, điều này có hại, vì biểu thức thực sự có thể thay đổi trạng thái của chương trình:

assert(2 == ShroedingersCat.GetNumEars()); sẽ làm cho chương trình làm những việc khác nhau trong gỡ lỗi và phát hành.

Chúng tôi đã phát triển một tập hợp các macro xác nhận sẽ đưa ra một ngoại lệ và thực hiện nó trong cả phiên bản gỡ lỗi và phát hành. Chẳng hạn, THROW_UNLESS_EQ(a, 20);sẽ đưa ra một ngoại lệ với thông điệp what () có cả tệp, dòng và giá trị thực của a, v.v. Chỉ có một macro sẽ có sức mạnh cho việc này. Trình gỡ lỗi có thể được cấu hình để phá vỡ 'ném' loại ngoại lệ cụ thể.


4
90% phần trăm số liệu thống kê được sử dụng trong các đối số là sai.
João Portela

5

Tôi không thích khẳng định mãnh liệt. Tôi sẽ không đi xa như nói rằng họ là ác mặc dù.

Về cơ bản, một khẳng định sẽ làm điều tương tự như một ngoại lệ không được kiểm soát, ngoại lệ duy nhất là khẳng định (thông thường) không nên được giữ cho sản phẩm cuối cùng.

Nếu bạn tự xây dựng một mạng lưới an toàn trong khi gỡ lỗi và xây dựng hệ thống, tại sao bạn lại từ chối mạng lưới an toàn này cho khách hàng của bạn, hoặc bàn trợ giúp của bạn, hoặc bất kỳ ai sẽ sử dụng phần mềm mà bạn hiện đang xây dựng. Sử dụng ngoại lệ dành riêng cho cả khẳng định và tình huống đặc biệt. Bằng cách tạo một hệ thống phân cấp ngoại lệ phù hợp, bạn sẽ có thể nhận ra rất nhanh cái này đến cái khác. Ngoại trừ lần này, khẳng định vẫn còn và có thể cung cấp thông tin có giá trị trong trường hợp thất bại sẽ bị mất.

Vì vậy, tôi hoàn toàn hiểu những người tạo ra Go bằng cách loại bỏ hoàn toàn các xác nhận và buộc các lập trình viên sử dụng các ngoại lệ để xử lý tình huống. Có một lời giải thích đơn giản cho điều này, ngoại lệ chỉ là một cơ chế tốt hơn cho công việc tại sao lại gắn bó với các khẳng định cổ xưa?


Đi không có ngoại lệ. Lý do thông thường để sử dụng một xác nhận chứ không phải là một ngoại lệ là vì bạn muốn nó được triển khai vì lý do hiệu suất. Khẳng định không phải là cổ xưa. Tôi xin lỗi vì nghe có vẻ gay gắt, nhưng câu trả lời này không hữu ích cho câu hỏi ban đầu, cũng không chính xác.
Nir Friedman


3

Gần đây tôi đã bắt đầu thêm một số xác nhận vào mã của mình và đây là cách tôi đã thực hiện:

Tôi tinh thần phân chia mã của tôi thành mã biên và mã nội bộ. Mã ranh giới là mã xử lý đầu vào của người dùng, đọc tệp và nhận dữ liệu từ mạng. Trong mã này, tôi yêu cầu đầu vào trong một vòng lặp chỉ thoát khi đầu vào hợp lệ (trong trường hợp đầu vào của người dùng tương tác) hoặc ném ngoại lệ trong trường hợp dữ liệu bị hỏng tệp / mạng không thể phục hồi.

Mã nội bộ là tất cả mọi thứ khác. Chẳng hạn, một hàm đặt một biến trong lớp của tôi có thể được định nghĩa là

void Class::f (int value) {
    assert (value < end);
    member = value;
}

và một chức năng nhận đầu vào từ một mạng có thể đọc như sau:

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

Điều này cho tôi hai lớp kiểm tra. Bất cứ điều gì mà dữ liệu được xác định tại thời điểm chạy luôn có một ngoại lệ hoặc xử lý lỗi ngay lập tức. Tuy nhiên, kiểm tra thêm đó Class::fbằng assertcâu lệnh có nghĩa là nếu một số mã nội bộ từng gọi Class::f, tôi vẫn kiểm tra độ tỉnh táo. Mã nội bộ của tôi có thể không vượt qua một đối số hợp lệ (vì tôi có thể đã tính valuetừ một số hàm phức tạp), vì vậy tôi muốn có xác nhận trong hàm cài đặt để ghi lại rằng bất kể ai đang gọi hàm, valuekhông được lớn hơn hoặc tương đương để end.

Điều này dường như phù hợp với những gì tôi đang đọc ở một vài nơi, rằng các xác nhận đó không thể vi phạm trong một chương trình hoạt động tốt, trong khi các trường hợp ngoại lệ nên dành cho các trường hợp đặc biệt và sai lầm vẫn có thể xảy ra. Bởi vì về lý thuyết tôi đang xác nhận tất cả các đầu vào, nên không thể kích hoạt xác nhận của tôi. Nếu có, chương trình của tôi là sai.


2

Nếu các xác nhận mà bạn đang nói có nghĩa là chương trình nôn ra và sau đó tồn tại, các xác nhận có thể rất tệ. Điều này không có nghĩa là chúng luôn là thứ sai để sử dụng, chúng là một cấu trúc rất dễ bị lạm dụng. Họ cũng có nhiều lựa chọn thay thế tốt hơn. Những thứ như thế là ứng cử viên tốt để được gọi là ác.

Ví dụ, một mô-đun bên thứ 3 (hoặc bất kỳ mô-đun nào thực sự) gần như không bao giờ thoát khỏi chương trình gọi. Điều này không cung cấp cho lập trình viên cuộc gọi bất kỳ quyền kiểm soát nào đối với rủi ro mà chương trình nên thực hiện tại thời điểm đó. Trong nhiều trường hợp, dữ liệu rất quan trọng đến nỗi thậm chí lưu dữ liệu bị hỏng còn tốt hơn mất dữ liệu đó. Các xác nhận có thể buộc bạn mất dữ liệu.

Một số lựa chọn thay thế để khẳng định:

  • Sử dụng trình gỡ lỗi,
  • Bảng điều khiển / cơ sở dữ liệu / ghi nhật ký khác
  • Ngoại lệ
  • Các loại xử lý lỗi khác

Một số tài liệu tham khảo:

Ngay cả những người ủng hộ khẳng định cũng nghĩ rằng họ chỉ nên được sử dụng trong phát triển chứ không phải trong sản xuất:

Người này nói rằng các xác nhận nên được sử dụng khi mô-đun có khả năng dữ liệu bị hỏng vẫn tồn tại sau khi ném ngoại lệ: http://www.advogato.org/article/949.html . Đây chắc chắn là một điểm hợp lý, tuy nhiên, một mô-đun bên ngoài sẽ không bao giờ quy định mức độ quan trọng của dữ liệu đối với chương trình gọi (bằng cách thoát "cho" chúng). Cách thích hợp để xử lý vấn đề này là bằng cách đưa ra một ngoại lệ cho thấy rõ rằng chương trình hiện có thể ở trạng thái không nhất quán. Và vì các chương trình tốt chủ yếu bao gồm các mô-đun (với một ít mã keo trong tệp thực thi chính), các khẳng định hầu như luôn luôn là điều sai.


1

assert là rất hữu ích và có thể giúp bạn tiết kiệm rất nhiều quay lui khi xảy ra lỗi không mong muốn bằng cách tạm dừng chương trình ở những dấu hiệu rắc rối đầu tiên.

Mặt khác, nó rất dễ lạm dụng assert.

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

Phiên bản phù hợp, chính xác sẽ là một cái gì đó như:

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

Vì vậy, ... về lâu dài ... trong bức tranh lớn ... tôi phải đồng ý rằng assertcó thể bị lạm dụng. Tôi làm nó suốt.


1

assert đang bị lạm dụng để xử lý lỗi vì nó ít gõ hơn.

Vì vậy, là nhà thiết kế ngôn ngữ, họ nên thấy rằng việc xử lý lỗi thích hợp có thể được thực hiện bằng cách gõ ít hơn. Không bao gồm khẳng định vì cơ chế ngoại lệ của bạn dài dòng không phải là giải pháp. Đợi đã, Go cũng không có ngoại lệ. Quá tệ :)


1
Không quá tệ :-) Ngoại lệ hay không, khẳng định hay không, người hâm mộ Go vẫn đang nói về độ ngắn của các mã.
Moshe Revah

1

Tôi cảm thấy như đá vào đầu tác giả khi thấy điều đó.

Tôi sử dụng khẳng định tất cả thời gian trong mã và cuối cùng thay thế tất cả khi tôi viết thêm mã. Tôi sử dụng chúng khi tôi chưa viết logic cần thiết và muốn được cảnh báo khi tôi chạy mã thay vì viết một ngoại lệ sẽ bị xóa khi dự án gần hoàn thành hơn.

Các ngoại lệ cũng hòa trộn với mã sản xuất dễ dàng hơn mà tôi không thích. Một khẳng định là dễ nhận thấy hơnthrow new Exception("Some generic msg or 'pretend i am an assert'");


1

Vấn đề của tôi với những câu trả lời này bảo vệ khẳng định là không ai chỉ định rõ ràng điều gì làm cho nó khác với một lỗi nghiêm trọng thông thường và tại sao một khẳng định không thể là một tập hợp con của một ngoại lệ . Bây giờ, với điều này đã nói, nếu ngoại lệ không bao giờ bị bắt thì sao? Điều đó làm cho nó một khẳng định bằng danh pháp? Và, tại sao bạn lại muốn áp đặt một hạn chế trong ngôn ngữ mà một ngoại lệ có thể được nêu ra mà / nothing / có thể xử lý?


Nếu bạn nhìn vào câu trả lời của tôi. Cách sử dụng của tôi là để phân biệt 'ngoại lệ' (khẳng định) rằng tôi muốn loại bỏ việc sử dụng để gỡ lỗi so với ngoại lệ mà tôi giữ. Tại sao tôi muốn thoát khỏi chúng? bởi vì không có họ làm việc thì nó sẽ không hoàn thành Ví dụ là nếu tôi xử lý 3 trường hợp và thứ 4 là việc cần làm. Tôi có thể dễ dàng tìm kiếm khẳng định để tìm thấy chúng trong mã và biết nó chưa hoàn chỉnh thay vì sử dụng một ngoại lệ có thể vô tình bị bắt (bởi một lập trình viên khác) hoặc khó biết liệu tôi có phải là ngoại lệ hoặc kiểm tra logic mà tôi nên giải quyết trong mã không.

Trong mắt tôi, đây là một ý tưởng tồi, ở cùng cấp độ với các lớp "niêm phong" và chỉ với cùng một lý do. Bạn đang giả sử các ngoại lệ bạn muốn giữ có thể chấp nhận được đối với việc sử dụng mã của bạn mà bạn chưa biết. Tất cả các ngoại lệ đều đi qua cùng một kênh và nếu người dùng không muốn bắt chúng, anh ta có thể chọn không. Nếu anh ta làm, anh ta cũng nên có khả năng. Dù bằng cách nào, bạn chỉ đang đưa ra các giả định hoặc đẩy lùi thực hành của bạn với một khái niệm như một khẳng định.
Evan Carroll

Tôi quyết định kịch bản ví dụ là tốt nhất. Đây là một đơn giản. int func (int i) {if (i> = 0) {console.write ("Số là dương {0}", i); } other {assert (false); // để lười làm ATM phủ định} return i * 2; <- Làm thế nào tôi có thể làm điều này mà không cần khẳng định và là một ngoại lệ thực sự tốt hơn? và hãy nhớ rằng, mã này sẽ được thực hiện trước khi phát hành.

Chắc chắn ngoại lệ là tốt hơn, giả sử tôi lấy đầu vào của người dùng và gọi func()với số âm. Bây giờ, đột nhiên với những lời khẳng định của bạn, bạn đã kéo tấm thảm ra khỏi tôi và không cho tôi cơ hội phục hồi, thay vì lịch sự nói cho tôi biết những gì tôi yêu cầu không thể thực hiện được. Không có gì sai khi cáo buộc chương trình bị xử lý sai và đưa ra một trích dẫn: vấn đề là bạn đang làm mờ hành động thi hành luật và kết án tội phạm.
Evan Carroll

Đó là điểm, bạn NÊN nói những gì người dùng muốn làm không thể được thực hiện. Ứng dụng sẽ chấm dứt với thông báo lỗi và bất kỳ ai đang gỡ lỗi sẽ nhớ một cái gì đó NÊN LÀM nhưng không được. Bạn KHÔNG muốn nó được xử lý. Bạn muốn chấm dứt và được nhắc nhở bạn đã không xử lý trường hợp đó. và cực kỳ dễ dàng để xem những trường hợp còn lại vì tất cả những gì bạn cần làm là tìm kiếm để xác nhận mã. Xử lý lỗi sẽ là sai vì mã sẽ có thể làm điều đó một khi đã sẵn sàng để sản xuất. Cho phép một lập trình viên bắt nó và làm một cái gì đó khác là sai.

1

Vâng, khẳng định là xấu xa.

Thường thì chúng được sử dụng ở những nơi nên sử dụng xử lý lỗi thích hợp. Làm quen với việc viết xử lý lỗi chất lượng sản xuất phù hợp ngay từ đầu!

Thông thường họ có được trong cách viết bài kiểm tra đơn vị (trừ khi bạn viết một xác nhận tùy chỉnh tương tác với khai thác thử nghiệm của bạn). Điều này thường là do chúng được sử dụng khi sử dụng xử lý lỗi thích hợp.

Hầu hết chúng được biên dịch từ các bản dựng phát hành, điều đó có nghĩa là không có "thử nghiệm" nào của chúng khả dụng khi bạn đang chạy mã mà bạn thực sự phát hành; cho rằng trong các tình huống đa luồng, các vấn đề tồi tệ nhất thường chỉ xuất hiện trong mã phát hành, điều này có thể xấu.

Đôi khi chúng là một cái nạng cho các thiết kế bị hỏng; tức là thiết kế mã cho phép người dùng gọi nó theo cách không nên gọi và khẳng định "ngăn chặn" điều này. Sửa thiết kế!

Tôi đã viết về điều này nhiều hơn trên blog của tôi vào năm 2005 tại đây: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html


0

Không quá ác như thường phản tác dụng. Có một sự tách biệt giữa kiểm tra lỗi và gỡ lỗi vĩnh viễn. Khẳng định làm cho mọi người nghĩ rằng tất cả các gỡ lỗi nên là vĩnh viễn và gây ra vấn đề lớn về khả năng đọc khi được sử dụng nhiều. Xử lý lỗi vĩnh viễn phải tốt hơn mức cần thiết và vì khẳng định gây ra lỗi của chính nó nên đó là một thực tế đáng nghi ngờ.


5
khẳng định là tốt cho việc khai báo các điều kiện trước ở đầu hàm và nếu được viết rõ ràng, hoạt động như một phần của tài liệu của hàm.
Peter Cordes

0

tôi không bao giờ sử dụng assert (), các ví dụ thường hiển thị như thế này:

int* ptr = new int[10];
assert(ptr);

Điều này thật tệ, tôi không bao giờ làm điều này, nếu trò chơi của tôi phân bổ một loạt quái vật thì sao? Tại sao tôi nên làm hỏng trò chơi, thay vào đó bạn nên xử lý các lỗi một cách duyên dáng, vì vậy hãy làm một cái gì đó như:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}

14
newKhông bao giờ trở lại nullptr, nó ném.
David Stone

Lưu ý rằng bạn có thể sử dụng std :: nothrow cho điều đó.
markand
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.