Thực tiễn tồi tệ nhất trong C ++, các lỗi phổ biến [đã đóng]


35

Sau khi đọc câu nói nổi tiếng này của Linus Torvalds , tôi tự hỏi thực sự tất cả những cạm bẫy của các lập trình viên trong C ++ là gì. Tôi rõ ràng không đề cập đến lỗi chính tả hoặc luồng chương trình xấu như được xử lý trong câu hỏi này và câu trả lời của nó , nhưng với các lỗi cấp cao hơn không được trình biên dịch phát hiện và không gây ra lỗi rõ ràng trong lần chạy đầu tiên, lỗi thiết kế hoàn chỉnh, những điều không thể thực hiện được trong C nhưng có khả năng được thực hiện trong C ++ bởi những người mới không hiểu ý nghĩa đầy đủ của mã của họ.

Tôi cũng hoan nghênh các câu trả lời chỉ ra việc giảm hiệu suất rất lớn, nơi nó thường không được mong đợi. Một ví dụ về những gì một trong những giáo sư của tôi đã từng nói với tôi về một trình tạo trình phân tích cú pháp LR (1) mà tôi đã viết:

Bạn đã sử dụng phần nào quá nhiều trường hợp thừa kế và ảo không cần thiết. Kế thừa làm cho một thiết kế phức tạp hơn nhiều (và không hiệu quả do hệ thống con RTTI (suy luận kiểu thời gian chạy), và do đó nó chỉ nên được sử dụng khi có ý nghĩa, ví dụ cho các hành động trong bảng phân tích. Bởi vì bạn sử dụng nhiều mẫu, nên thực tế bạn không cần kế thừa. "


6
Một số lỗi khó chịu hơn mà bạn có thể mắc phải trong C / C ++ chủ yếu là do di sản C ... đọc hành vi không xác định, quản lý bộ nhớ thủ công, v.v. Ngoài ra, lời khuyên của prof có vẻ không có thật / sai (đối với tôi, người không phải là một chuyên gia về C ++) - khởi tạo mẫu sẽ mang lại một lớp bình thường với vtable cho các virtualhàm, phải không?

8
Bạn đang nhớ sai những gì giáo sư của bạn nói, hoặc anh ta không biết anh ta đang nói gì. Các lớp phái sinh thường không cần sử dụng RTTI (phản xạ AKA) để tìm kiếm mọi thứ. Nếu họ đang sử dụng các phương thức ảo, mã có thể cần thực hiện tra cứu vtable cho công văn, nhưng điều đó chuyển thành một lệnh ASM duy nhất trên nhiều bộ xử lý. Do các vấn đề về bộ nhớ đệm, nó có thể làm chậm mọi thứ xuống một mức nhất định, nhưng bạn không bao giờ có thể nhận thấy chi phí trong bất kỳ trường hợp sử dụng nào đòi hỏi khắt khe nhất. Có rất nhiều lý do tốt để tránh C ++, nhưng tra cứu vtable không phải là một trong số đó.
Mason Wheeler

5
@FelixDombek: Được nêu rất hào phóng và được áp dụng trên bảng, câu nói đó từ giáo sư của bạn chỉ cho thấy một lượng lớn sự thiếu hiểu biết. Khi thiết kế của bạn cần đa hình thời gian chạy của một số loại, sử dụng các hàm ảo thường là lựa chọn tốt nhất; khi bạn không cần nó, đừng sử dụng nó: bạn không cần tất cả các phương thức là ảo chỉ vì bạn sử dụng các lớp dẫn xuất chẳng hạn.
Fred Nurk

5
@Mason Wheeler: RTTI chứa thông tin về loại, đủ để có thể xác định liệu có dynamic_castnên thành công hay không, và một số thứ khác, nhưng phản ánh bao gồm nhiều hơn, bao gồm có thể truy xuất thông tin về các thuộc tính hoặc chức năng thành viên, không phải là có mặt trong C ++.
David Rodríguez - dribeas

5
Nhận xét của giáo sư có phần lừa dối, vì tính kế thừa và chức năng ảo không phải là một thành tích lớn. Lời khuyên để sử dụng kế thừa một cách tiết kiệm là tốt, nhưng đó là vấn đề về cấu trúc chương trình hơn là hiệu quả. Kế thừa, đặc biệt là với các thành viên được bảo vệ, gần như là một khớp nối như bạn sẽ nhận được, và nếu bạn không cần nó, bạn không nên sử dụng nó.
David Thornley

Câu trả lời:


69

Torvalds đang nói về mông của mình ở đây.


OK, tại sao anh ta nói ra khỏi mông của mình:

Trước hết, lời nói của anh ta thực sự không có gì là NHƯNG. Có rất ít nội dung thực tế ở đây. Lý do duy nhất nó thực sự nổi tiếng hoặc thậm chí được tôn trọng nhẹ là bởi vì nó được tạo ra bởi Thần Linux. Lập luận chính của anh ta là C ++ là tào lao và anh ta thích chọc giận mọi người C ++. Tất nhiên không có lý do nào để đáp lại điều đó và bất cứ ai coi đó là một cuộc tranh luận hợp lý dù sao cũng là cuộc trò chuyện.

Đối với những gì có thể được lấp lánh như những điểm khách quan nhất của mình:

  • STL và Boost hoàn toàn tào lao <- sao cũng được. Bạn là một thằng ngốc.
  • STL và Boost gây ra vô số nỗi đau <- nực cười. Rõ ràng là anh ta cố tình quá mức nhưng sau đó tuyên bố thực sự của anh ta ở đây là gì? Tôi không biết. Có một số khó khăn hơn rất nhiều để tìm ra các vấn đề khi bạn khiến trình biên dịch nôn mửa trong Spirit hoặc thứ gì đó, nhưng nó không ít nhiều khó khăn để tìm ra việc gỡ lỗi UB do sử dụng sai các cấu trúc C như void *.
  • Các mô hình trừu tượng được khuyến khích bởi C ++ là không hiệu quả. <- Thích cái gì? Anh ta không bao giờ mở rộng, không bao giờ cung cấp bất kỳ ví dụ nào về ý nghĩa của anh ta, anh ta chỉ nói điều đó. BFD. Vì tôi không thể nói những gì anh ấy đề cập đến có chút điểm cố gắng "bác bỏ" tuyên bố. Đó là một câu thần chú phổ biến của C bigots nhưng điều đó không làm cho nó trở nên dễ hiểu hoặc dễ hiểu hơn.
  • Việc sử dụng đúng C ++ có nghĩa là bạn giới hạn bản thân ở các khía cạnh C. <- Trên thực tế, mã WORSE C ++ hiện có, vì vậy tôi vẫn không biết WTF mà anh ấy đang nói đến.

Về cơ bản, Torvalds đang nói ra khỏi mông của mình. Không có tranh luận dễ hiểu được đưa ra về bất cứ điều gì. Mong đợi một phản bác nghiêm trọng của những điều vô nghĩa như vậy chỉ là ngớ ngẩn đơn giản. Tôi được bảo là "mở rộng" về việc bác bỏ một thứ mà tôi dự kiến ​​sẽ mở rộng nếu đó là nơi tôi đã nói. Nếu bạn thực sự, thành thật nhìn vào những gì Torvalds nói bạn sẽ thấy rằng anh ta thực sự không nói gì.

Chỉ vì Chúa nói rằng điều đó không có nghĩa là nó có ý nghĩa hay nên được thực hiện nghiêm túc hơn so với việc một số bozo ngẫu nhiên nói điều đó. Sự thật mà nói, Thiên Chúa chỉ là một bozo ngẫu nhiên khác.


Trả lời câu hỏi thực tế:

Có lẽ cách thực hành C ++ tồi tệ nhất và phổ biến nhất là đối xử với nó như C. Tiếp tục sử dụng các hàm C API như printf, được (cũng bị coi là xấu trong C), strtok, v.v ... không chỉ không tận dụng được sức mạnh được cung cấp bởi hệ thống loại chặt chẽ hơn, chắc chắn chúng sẽ dẫn đến các biến chứng hơn nữa khi cố gắng tương tác với mã C ++ "thực". Về cơ bản, hãy làm chính xác điều ngược lại với những gì Torvalds đang khuyên.

Tìm hiểu cách tận dụng STL và Boost để phát hiện thêm các lỗi phát hiện thời gian và để làm cho cuộc sống của bạn dễ dàng hơn theo những cách khác, ví dụ (mã thông báo boost chẳng hạn là loại an toàn VÀ giao diện tốt hơn). Đúng là bạn sẽ phải học cách đọc lỗi mẫu, điều gây khó chịu lúc đầu, nhưng (theo kinh nghiệm của tôi dù sao) nó thực sự dễ dàng hơn nhiều so với việc cố gắng gỡ lỗi một cái gì đó tạo ra hành vi không xác định trong thời gian chạy, mà C api tạo ra khá dễ làm.

Không phải nói rằng C là không tốt. Tôi tất nhiên thích C ++ hơn. Lập trình viên C thích C hơn. Có sự đánh đổi và thích chủ quan khi chơi. Cũng có rất nhiều thông tin sai lệch và FUD trôi nổi xung quanh. Tôi sẽ nói rằng có nhiều FUD và thông tin sai lệch trôi nổi về C ++ nhưng tôi thiên vị về vấn đề này. Ví dụ, các vấn đề "phình to" và "hiệu suất" C ++ được cho là không thực sự là vấn đề lớn trong hầu hết thời gian và chắc chắn bị thổi phồng ra khỏi tỷ lệ của thực tế.

Đối với các vấn đề mà giáo sư của bạn đề cập đến, đây không phải là duy nhất đối với C ++. Trong OOP (và trong lập trình chung), bạn muốn thích sáng tác hơn kế thừa. Kế thừa là mối quan hệ khớp nối mạnh nhất có thể tồn tại trong tất cả các ngôn ngữ OO. C ++ thêm một thứ nữa là mạnh mẽ hơn, tình bạn. Kế thừa đa hình nên được sử dụng để thể hiện sự trừu tượng và các mối quan hệ "is-a", nó không bao giờ nên được sử dụng để tái sử dụng. Đây là lỗi lớn thứ hai mà bạn có thể mắc phải trong C ++ và nó là một lỗi khá lớn, nhưng nó khác xa với ngôn ngữ. Bạn cũng có thể tạo các mối quan hệ thừa kế quá phức tạp trong C # hoặc Java và chúng sẽ có cùng một vấn đề.


1
Trớ trêu thay, cho đến sau năm 2007, git chỉ chạy các phiên bản di động của Linux. Chà, bất kỳ hệ thống nào giống nhau. Sau đó, một lần nữa, với hoàn cảnh dẫn đến việc tạo ra git, tôi chắc chắn không giữ nó chống lại anh ta.
Chris K

9
Linus có một thời gian khó khăn để tìm bất kỳ lập trình viên C ++ giỏi nào muốn làm việc cho anh ta. Tự hỏi tại sao? Tôi nghĩ rằng đây chỉ là một loại vấn đề gà và trứng.
Bo Persson

19

Tôi đã luôn nghĩ rằng sự nguy hiểm của C ++ đã được cường điệu hóa bởi những người lập trình C thiếu kinh nghiệm.

Đúng, C ++ khó nhận hơn một thứ như Java, nhưng nếu bạn lập trình bằng các kỹ thuật hiện đại thì việc viết các chương trình mạnh mẽ là khá dễ dàng. Tôi thực sự không có điều đó nhiều khó khăn hơn trong một thời gian lập trình trong C ++ hơn tôi bằng các ngôn ngữ như Java, và tôi thường thấy mình thiếu một số C ++ trừu tượng như các mẫu và RAII khi tôi thiết kế bằng ngôn ngữ khác.

Điều đó nói rằng, ngay cả sau nhiều năm lập trình trong C ++, cứ thỉnh thoảng tôi sẽ phạm một lỗi thực sự ngu ngốc không thể có trong ngôn ngữ cấp cao hơn. Một cạm bẫy phổ biến trong C ++ là bỏ qua tuổi thọ của đối tượng: trong Java và C # bạn thường không phải quan tâm đến tuổi thọ của đối tượng *, bởi vì tất cả các đối tượng tồn tại trên đống và chúng được quản lý bởi một trình thu gom rác ma thuật.

Bây giờ, trong C ++ hiện đại, thông thường bạn cũng không cần quan tâm nhiều đến thời gian sống của đối tượng. Bạn có các hàm hủy và con trỏ thông minh quản lý vòng đời của các đối tượng cho bạn. 99% thời gian, điều này làm việc tuyệt vời. Nhưng thỉnh thoảng, bạn sẽ bị vặn bởi một con trỏ lơ lửng (hoặc tham chiếu.) Ví dụ, gần đây tôi có một đối tượng (hãy gọi nó Foo) có chứa một biến tham chiếu bên trong cho một đối tượng khác (hãy gọi nó Bar). Tại một thời điểm, tôi đã ngu ngốc sắp xếp mọi thứ để Barđi ra khỏi phạm vi trước đó Foo, nhưng Fookẻ hủy diệt cuối cùng đã gọi một chức năng thành viên Bar. Không cần phải nói, mọi thứ đã không diễn ra tốt đẹp.

Bây giờ, tôi không thể đổ lỗi cho C ++ vì điều này. Đó là thiết kế tồi của riêng tôi, nhưng vấn đề là loại điều này sẽ không xảy ra trong một ngôn ngữ được quản lý ở cấp độ cao hơn. Ngay cả với con trỏ thông minh và tương tự, đôi khi bạn vẫn cần phải có nhận thức về thời gian sống của đối tượng.


* Nếu tài nguyên đang được quản lý là bộ nhớ, đó là.


8
Không bao giờ thực sự phải quan tâm đến tuổi thọ đối tượng trong Java và C #? GC của họ chăm sóc bộ nhớ, nhưng đó chỉ là một phần nhỏ của RAII đối với tôi; ví dụ, nhìn vào các giao diện "dùng một lần" mà các ngôn ngữ có.
Fred Nurk

Phải quan tâm đến tuổi thọ của đối tượng sẽ là hiếm trong Java ngoại trừ thiết kế bất tiện của thư viện I / O của nó.
dan04

Vấn đề tham khảo lơ lửng của bạn là điều tôi quan tâm khi cố gắng giải quyết. Tôi bắt đầu một cuộc thảo luận trên blog của mình về hướng mà tôi đang hướng đến để giải quyết nó (những lời hứa về con trỏ). Về cơ bản tôi nghĩ rằng ngôn ngữ có thể sử dụng một vài con trỏ thông minh hơn. Tham gia vào cuộc thảo luận đó nếu bạn quan tâm. Không ai khác đã từng như vậy ... nhưng nếu đó là điều bạn muốn thấy đã được giải quyết ... Tôi thực sự gặp phải vấn đề hơn 10% thời gian.
Edward Strange

13

Sự khác biệt trong mã thường liên quan nhiều đến lập trình viên hơn là ngôn ngữ. Đặc biệt, cả lập trình viên C ++ giỏi và lập trình viên C đều sẽ có các giải pháp tốt tương tự (ngay cả khi khác nhau). Bây giờ, C là một ngôn ngữ đơn giản hơn (như một ngôn ngữ) và điều đó có nghĩa là có ít trừu tượng hơn và khả năng hiển thị nhiều hơn về những gì mã thực sự làm.

Một phần trong lời ca tụng của anh ta (anh ta được biết đến với những lời ca ngợi chống lại C ++) dựa trên thực tế là nhiều người sẽ sử dụng C ++ hơn và viết mã mà không thực sự hiểu những gì trừu tượng che giấu và đưa ra các giả định sai.


3
Chi phí lặp lại khi std::vector<bool>thay đổi từng giá trị là gì? for ( std::vector<bool>::iterator it = v.begin(), end = v.end(); it != end; ++it ) { *it = !*it; }? Những gì được trừu tượng hóa trong *it = !*it;?
David Rodríguez - dribeas

2
Mặc dù có thể không công bằng khi chọn những sự ghê tởm ngôn ngữ cụ thể bị chỉ trích rộng rãi là sai lầm ...
Fred Nurk

2
@Fred Nurk: std::vector<bool>là một sai lầm nổi tiếng, nhưng nó là một ví dụ thực sự tốt về những gì đang được thảo luận: trừu tượng là tốt, nhưng bạn phải cẩn thận với những gì chúng che giấu. Điều tương tự có thể và sẽ xảy ra trong mã người dùng. Để bắt đầu, tôi đã thấy cả người C ++ và Java sử dụng các ngoại lệ để thực hiện kiểm soát luồng và mã trông giống như một lệnh gọi hàm lồng nhau thực sự là một trình khởi chạy ngoại lệ bailout: được void endOperation();triển khai như throw EndOperation;. Một lập trình viên giỏi sẽ tránh được những cấu trúc đáng ngạc nhiên đó , nhưng thực tế là bạn có thể tìm thấy chúng.
David Rodríguez - dribeas

5
Một trong những điểm của Torvalds là: anh ta có thể xua đuổi người mới bắt đầu chỉ bằng cách chọn C trên C ++ (dường như có nhiều người mới bắt đầu C ++ hơn) và C ++ phức tạp hơn có đường cong học tập dốc hơn và có nhiều cơ hội vấp ngã hơn trong trường hợp góc .
David Rodríguez - dribeas

2
+1, đây chính xác là những gì Linus đang phàn nàn. Anh ta trở thành người chống C ++, nhưng thực tế không phải vậy. Anh ta chỉ chống C ++ - lập trình viên.
greyfade

13

Sử dụng quá mức các try/catchkhối.

File file("some.txt");
try
{
  /**/

  file.close();
}
catch(std::exception const& e)
{
  file.close();
}

Điều này thường bắt nguồn từ các ngôn ngữ như Java và mọi người sẽ lập luận rằng C ++ thiếu một finalizemệnh đề.

Nhưng mã này thể hiện hai vấn đề:

  • Bạn cần phải xây dựng filetrước try/catch, bởi vì bạn thực sự closekhông thể tồn tại một tệp không tồn tại catch. Điều này dẫn đến một "rò rỉ phạm vi" trong đó filecó thể nhìn thấy sau khi đã bị đóng. Bạn có thể thêm một khối nhưng ...: /
  • Nếu ai đó đến xung quanh và thêm một returnở giữa tryphạm vi, thì tệp không bị đóng (đó là lý do tại sao mọi người chê bai về việc thiếu finalizeđiều khoản)

Tuy nhiên, trong C ++, chúng ta có nhiều cách hiệu quả hơn để xử lý vấn đề này:

  • Java finalize
  • C # của using
  • Đi defer

Chúng tôi có RAII, người có tài sản thực sự thú vị được tóm tắt tốt nhất là SBRM(Quản lý tài nguyên giới hạn phạm vi).

Bằng cách tạo ra lớp để kẻ hủy diệt của nó dọn sạch các tài nguyên mà nó sở hữu, chúng tôi không đặt trách nhiệm quản lý tài nguyên cho mỗi người dùng của nó!

Đây là những tính năng tôi nhớ trong bất kỳ ngôn ngữ nào khác, và có lẽ là một trong đó là bị lãng quên nhất.

Sự thật là hiếm khi cần phải viết một try/catchkhối trong C ++, ngoài cấp độ cao nhất để tránh chấm dứt mà không cần đăng nhập.


1
Tôi không nghĩ đó là ảnh hưởng của Java nhiều như C. (Bạn có thể trực tiếp thay thế fopenfcloseở đây.) RAII là cách "đúng đắn" để làm mọi việc ở đây, nhưng nó bất tiện cho những người muốn sử dụng thư viện C từ C ++ .
dan04

Đối với loại câu trả lời này, cung cấp một ví dụ về giải pháp chính xác, sẽ phù hợp.
Claus Jørgensen

@ ClausJørgensen: Chà, thật không may, giải pháp không thực sự "sặc sỡ" vì nó liên quan đến chính nó File file("some.txt");và nó (không open, không close, không try...)
Matthieu M.

D cũng có RAII
Demi

@Demetri: Tôi không quá quen thuộc với D, bạn có thể giải thích RAII tương tác với Bộ sưu tập rác không? Tôi biết rằng trong Python bạn có thể viết phương thức "deinit", tuy nhiên tài liệu cảnh báo rằng trong trường hợp chu trình tham chiếu, một số đối tượng sẽ không thấy phương thức deinit của chúng được gọi.
Matthieu M.

9

Một lỗi phổ biến phù hợp với tiêu chí của bạn là không hiểu cách các nhà xây dựng sao chép hoạt động khi xử lý bộ nhớ được phân bổ trong lớp của bạn. Tôi đã mất số lượng thời gian tôi đã dành để khắc phục sự cố hoặc rò rỉ bộ nhớ vì một 'noob' đã đặt các đối tượng của họ vào bản đồ hoặc vectơ và không viết đúng các hàm tạo và hàm hủy.

Thật không may, C ++ có đầy đủ các gotchas 'ẩn' như thế này. Nhưng phàn nàn về điều đó cũng giống như phàn nàn bạn đã đến Pháp và không thể hiểu mọi người đang nói gì. Nếu bạn sẽ đến đó, hãy học ngôn ngữ.


1
Tôi nghĩ vấn đề với C ++ là rất dễ tự bắn vào chân mình. Chắc chắn, có những lập trình viên C ++ giỏi xung quanh, rất nhiều phần mềm tốt được viết bằng C ++. Nhưng rất khó để trở thành một nhà phát triển C ++ giỏi. Loạt bài 'Hiệu quả C ++' của Scott Meyers cho thấy ngôn ngữ có bao nhiêu sự tinh tế.
Marco Mustapic

Tôi đồng ý. Tuy nhiên, một phần của vấn đề là rất nhiều (phần lớn) các lập trình viên C ++ nghĩ rằng họ biết họ đang làm gì khi họ rõ ràng không làm. Ý bạn là "C ++ hiệu quả"?
Henry

Ít nhất điều này đang trở nên tốt hơn với các quy tắc khá hạn chế mới về việc tạo ra các hoạt động sao chép / di chuyển ngầm trong C ++ 0x. Trong nhiều trường hợp vi phạm quy tắc ba, việc tạo ra các hoạt động sao chép ngầm sẽ không được chấp nhận và sẽ đưa ra cảnh báo.
sellibitze

6

C ++ cho phép rất nhiều tính năng và phong cách lập trình, nhưng điều đó không có nghĩa đây thực sự là những cách tốt để C ++ được sử dụng. Và trên thực tế, thật dễ dàng để sử dụng C ++ không chính xác.

Nó phải được học và hiểu đúng , chỉ cần học bằng cách thực hiện (hoặc sử dụng nó như người ta sẽ sử dụng một số ngôn ngữ khác) sẽ dẫn đến mã không hiệu quả và dễ bị lỗi.


4

Chà ... Để bắt đầu, bạn có thể đọc C ++ FAQ Lite

Sau đó, một số người đã xây dựng sự nghiệp viết sách về những điều phức tạp của C ++:

Herb Sutter Scott Meyers cụ thể là.

Về phần thiếu sót của Torvalds ... hãy nói với mọi người, nghiêm túc: Không có ngôn ngữ nào khác ngoài đó có quá nhiều mực để xử lý các sắc thái của ngôn ngữ. Tất cả các sách Python & Ruby & Java của bạn đều tập trung vào viết ứng dụng ... sách C ++ của bạn tập trung vào các tính năng / mẹo / bẫy ngôn ngữ ngớ ngẩn.


1
Hmm ... javapuzzlers.com , jimbrooks.org/web/python/#Pit thác . Tôi muốn nói Accelerated C ++ (cho một ví dụ) tập trung nhiều hơn về cách viết mã hơn những làm ...
Jerry Coffin

1
Bạn đã đưa ra một vài ví dụ về các tài nguyên chỉ ra các trường hợp cạnh bằng ngôn ngữ tương ứng của họ; những thứ trông lạ và bạn không chắc chắn chúng hoạt động như thế nào (mặc dù công cụ danh sách trăn đã gần) ... C ++ có cả một ngành công nghiệp chỉ ra những thứ trông hoàn toàn hợp lệ theo cách bạn không mong đợi.
bụi bẩn đỏ

3

Tạo khuôn quá nặng có thể không dẫn đến lỗi lúc đầu. Tuy nhiên, khi thời gian trôi qua, mọi người sẽ cần phải sửa đổi mã đó và họ sẽ gặp khó khăn khi hiểu một mẫu rất lớn. Đó là khi lỗi xâm nhập - sự hiểu lầm gây ra các bình luận "Nó biên dịch và chạy", điều này thường dẫn đến mã gần như nhưng không hoàn toàn chính xác.

Nói chung nếu tôi thấy bản thân mình đang thực hiện một mẫu chung sâu ba cấp, tôi dừng lại và nghĩ làm thế nào nó có thể được giảm xuống một mẫu. Thông thường vấn đề được giải quyết bằng cách trích xuất các hàm hoặc các lớp.


8
Việc duy trì mã phức tạp khi đối mặt với các yêu cầu thay đổi luôn gây ra lỗi mà không cần nhiều nỗ lực, không có gì đặc biệt về các mẫu ở đó.
Fred Nurk

2

Cảnh báo: đây không phải là một câu trả lời gần như là một bài phê bình về cuộc nói chuyện mà "người dùng không biết" liên quan đến câu trả lời của anh ta.

Điểm chính đầu tiên của anh là "được cho là" tiêu chuẩn luôn thay đổi ". Trong thực tế, các ví dụ anh đưa ra liên quan đến những thay đổi trong C ++ trước khi có một tiêu chuẩn. Kể từ năm 1998 (khi tiêu chuẩn C ++ đầu tiên được hoàn thiện) các thay đổi về ngôn ngữ là khá tối thiểu - trên thực tế, nhiều người sẽ cho rằng vấn đề thực sự là cần phải thực hiện nhiều thay đổi hơn . Tôi khá chắc chắn rằng tất cả các mã phù hợp với tiêu chuẩn C ++ ban đầu vẫn phù hợp với tiêu chuẩn hiện tại. Mặc dù nó phần nào ít chắc chắn hơn, trừ khi có gì đó thay đổi nhanh chóng (và khá bất ngờ), điều tương tự cũng sẽ khá đúng với tiêu chuẩn C ++ sắp tới (về lý thuyết, tất cả các mã được sử dụngexport sẽ phá vỡ, nhưng hầu như không tồn tại; từ quan điểm thực tế, nó không phải là một vấn đề). Tôi có thể nghĩ ra một vài ngôn ngữ khác, HĐH (hoặc nhiều thứ khác liên quan đến máy tính) có thể đưa ra bất kỳ khiếu nại nào như vậy.

Sau đó, ông đi vào "phong cách luôn thay đổi". Một lần nữa, hầu hết các điểm của anh ấy khá gần với vô nghĩa. Anh ta cố gắng mô tả đặc điểm for (int i=0; i<n;i++)là "cũ và chán" và for (int i(0); i!=n;++i)"độ nóng mới". Thực tế là trong khi có những loại mà những thay đổi như vậy có thể có ý nghĩa, thì int, nó không có gì khác biệt - và ngay cả khi bạn có thể đạt được một cái gì đó, hiếm khi cần thiết để viết mã tốt hoặc đúng. Thậm chí là tốt nhất, anh ta đang tạo ra một ngọn núi từ một nốt ruồi.

Yêu cầu tiếp theo của ông là C ++ đang "tối ưu hóa sai hướng" - cụ thể, trong khi ông thừa nhận rằng việc sử dụng các thư viện tốt là dễ dàng, ông tuyên bố rằng C ++ "làm cho việc viết thư viện tốt gần như không thể." Ở đây, tôi tin là một trong những sai lầm cơ bản nhất của anh ấy. Trong thực tế, viết thư viện tốt cho hầu hết mọi ngôn ngữ là vô cùng khó khăn. Ở mức tối thiểu, viết một thư viện tốt đòi hỏi phải hiểu rõ một số miền có vấn đề để mã của bạn hoạt động cho vô số ứng dụng có thể có trong (hoặc liên quan đến) tên miền đó. Hầu hết những gì C ++ thực sự làm là "nâng tầm" - sau khi thấy một thư viện có thể tốt hơn bao nhiêu , mọi người hiếm khi sẵn sàng quay lại để viết loại trò chơi mà họ sẽ có.các lập trình viên thực sự giỏi viết khá nhiều thư viện, sau đó có thể được sử dụng (một cách dễ dàng, như anh ta thừa nhận) bởi "phần còn lại của chúng tôi". Đây thực sự là một trường hợp "đó không phải là một lỗi, đó là một tính năng."

Tôi sẽ không cố gắng đạt được mọi điểm theo thứ tự (sẽ mất các trang), nhưng bỏ qua trực tiếp đến điểm kết thúc của anh ấy. Ông trích dẫn Bjarne: "tối ưu hóa toàn bộ chương trình có thể được sử dụng để loại bỏ các bảng chức năng ảo và dữ liệu RTTI không sử dụng. Phân tích như vậy đặc biệt phù hợp với các chương trình tương đối nhỏ không sử dụng liên kết động."

Ông phê phán điều này bằng cách đưa ra một tuyên bố không được hỗ trợ rằng "Đây là một vấn đề thực sự khó khăn", thậm chí còn đi xa hơn khi so sánh nó với vấn đề tạm dừng. Trên thực tế, không có gì thuộc loại này - trên thực tế, trình liên kết đi kèm với Zortech C ++ (khá nhiều trình biên dịch C ++ đầu tiên cho MS-DOS, vào những năm 1980) đã làm điều này. Đúng là khó có thể chắc chắn rằng mọi bit dữ liệu ngoại lai có thể đã bị loại bỏ, nhưng vẫn hoàn toàn hợp lý để thực hiện một công việc khá công bằng.

Tuy nhiên, bất kể điều đó, điểm quan trọng hơn nhiều là điều này hoàn toàn không liên quan đến hầu hết các lập trình viên trong mọi trường hợp. Như những người trong chúng ta đã phân tách khá nhiều mã biết, trừ khi bạn viết ngôn ngữ hợp ngữ hoàn toàn không có thư viện, các tệp thực thi của bạn gần như chắc chắn chứa một lượng "công cụ" (cả mã và dữ liệu, trong trường hợp điển hình) mà bạn thậm chí có thể không biết về, không đề cập đến bao giờ thực sự sử dụng. Đối với hầu hết mọi người, hầu hết thời gian, điều đó không thành vấn đề - trừ khi bạn đang phát triển cho các hệ thống nhúng nhỏ nhất, việc tiêu thụ thêm dung lượng chỉ đơn giản là không liên quan.

Cuối cùng, sự thật là lời ca tụng này có nhiều chất hơn một chút so với sự ngốc nghếch của Linus - nhưng điều đó mang lại cho nó chính xác sự nguyền rủa với lời khen ngợi mờ nhạt mà nó xứng đáng.


1

Là một lập trình viên C đã phải viết mã trong C ++ do hoàn cảnh không thể tránh khỏi, đây là kinh nghiệm của tôi. Có rất ít thứ tôi sử dụng đó là C ++ và chủ yếu dính vào C. Lý do chính là vì tôi không hiểu rõ về C ++. Tôi đã / không có một người cố vấn để chỉ cho tôi thấy sự phức tạp của C ++ và cách viết mã tốt trong đó. Và không có hướng dẫn từ một mã C ++ rất tốt, việc viết mã tốt trong C ++ là cực kỳ khó khăn. IMHO đây là nhược điểm lớn nhất của C ++ vì các lập trình viên C ++ giỏi sẵn sàng nắm bắt những người mới bắt đầu rất khó để có được.

Một số lượt truy cập hiệu suất mà tôi đã thấy thường là do phân bổ bộ nhớ ma thuật của STL (vâng, bạn có thể thay đổi bộ cấp phát, nhưng ai làm điều đó khi anh ấy bắt đầu với C ++?). Bạn thường nghe các lập luận của các chuyên gia C ++ rằng các vectơ và mảng cung cấp hiệu suất tương tự, vì các vectơ sử dụng các mảng bên trong và sự trừu tượng là siêu hiệu quả. Tôi đã thấy điều này là đúng trong thực tế đối với truy cập véc tơ và sửa đổi các giá trị hiện có. Nhưng không đúng khi thêm một mục mới, xây dựng và phá hủy các vectơ. gprof đã chỉ ra rằng 25% thời gian cho một ứng dụng được dành cho các hàm tạo vector, hàm hủy, memmove (để di chuyển toàn bộ vectơ để thêm phần tử mới) và các toán tử vectơ quá tải khác (như ++).

Trong cùng một ứng dụng, vectơ của SomethingSmall đã được sử dụng để thể hiện một cái gì đóBig. Không cần truy cập ngẫu nhiên một cái gì đó Nhỏ trong một cái gì đóBig. Vẫn còn một vector được sử dụng thay vì một danh sách. Lý do tại sao vector được sử dụng? Bởi vì bộ mã hóa ban đầu đã quen thuộc với mảng như cú pháp của vectơ và không quen thuộc lắm với các trình vòng lặp cần thiết cho các danh sách (vâng, anh ta đến từ nền C). Tiếp tục chứng minh rằng cần có nhiều hướng dẫn từ các chuyên gia để có được C ++ ngay. C cung cấp rất ít các cấu trúc cơ bản mà hoàn toàn không có sự trừu tượng hóa, đến mức bạn có thể làm cho nó dễ dàng hơn nhiều so với C ++.



0

STL và boost là xách tay, ở cấp mã nguồn. Tôi đoán những gì Linus đang nói là C ++ thiếu ABI (giao diện nhị phân ứng dụng). Vì vậy, bạn cần phải biên dịch tất cả các thư viện mà bạn liên kết, với cùng một phiên bản trình biên dịch và với cùng các công tắc, hoặc nếu không thì giới hạn bản thân bạn trong C ABI tại các biên giới dll. Tôi cũng thấy rằng đang hoạt động .. nhưng trừ khi bạn đang tạo thư viện bên thứ 3, bạn sẽ có thể kiểm soát môi trường xây dựng của mình. Tôi thấy việc giới hạn bản thân mình vào C ABI không đáng để gặp rắc rối. Sự tiện lợi của việc có thể truyền chuỗi, vectơ và con trỏ thông minh từ dll này sang dll khác là vấn đề đáng ngại khi phải xây dựng lại tất cả các thư viện khi nâng cấp trình biên dịch hoặc thay đổi chuyển đổi trình biên dịch. Các nguyên tắc vàng tôi tuân theo là:

-Inhitit để sử dụng lại giao diện, không thực hiện

Tổng hợp -Prefer hơn thừa kế

-Prefer nơi các chức năng miễn phí có thể cho các phương thức thành viên

-Luôn sử dụng thành ngữ RAII để làm cho mã của bạn ngoại lệ mạnh mẽ an toàn. Tránh thử bắt.

-Sử dụng con trỏ thông minh, tránh con trỏ trần (không có dấu)

-Prefer giá trị ngữ nghĩa để tham khảo ngữ nghĩa

-Không phát minh lại bánh xe, sử dụng stl và boost

-Sử dụng thành ngữ Pimpl để ẩn riêng tư và / hoặc để cung cấp tường lửa trình biên dịch


-6

Không đặt một trận chung kết ;vào cuối một tuyên bố clase, ít nhất là trong một số phiên bản của VC.


4
Đây có lẽ là một lỗi rất phổ biến đối với người mới bắt đầu (hầu như bất cứ điều gì đối với một người vẫn đang học cú pháp cơ bản), nhưng có nhiều người sẽ tự gọi mình có năng lực và vẫn thấy lỗi này là đáng chú ý?
Fred Nurk

1
Đã viết nó chỉ vì trình biên dịch cung cấp cho bạn một lỗi không liên quan đến việc thiếu dấu chấm phẩy.
Marco Mustapic

2
vâng, lỗi chính xác là những gì bạn nhận được từ trình biên dịch C.
Mircea Chirea
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.