Gần đây mình có nhận được câu hỏi của một bạn sinh viên rằng, “Liệu có nên đầu tư công sức để học chuyên sâu về ngôn ngữ C++ hay không?“ Thực tế để trả lời câu hỏi này thì cần phải bàn về nhiều vấn đề liên quan, và mặc dù C++ là một ngôn ngữ mạnh mẽ tuyệt vời thì bạn sinh viên đó cũng nên cân nhắc đến các yếu tố như: lĩnh vực muốn làm sau này, sở thích… và thậm chí cũng cần để ý xem thị trường có tuyển dụng nhiều vị trí yêu cầu kỹ năng đó không nữa!
Có một bạn khác thì cho rằng “Ngôn ngữ gì ko quan trọng, quan trọng là ý tưởng tư duy code thôi“. Còn bạn thì bạn nghĩ sao? Lĩnh vực bạn đang làm có sử dụng nhiều C++ không? Thị trường tuyển dụng kỹ năng C++ tại Việt Nam có nhiều không? Nếu bạn đang là sinh viên thì bạn có đầu tư học chuyên sâu về C++ hay không? Bạn hãy dành chút thời gian cho biết quan điểm của mình ở phần bình luận phía dưới nhé!
Trước khi bạn đi vào bài viết này, mình xin trích lại một câu nói nổi tiếng của Bjarne Stroustrup về ngôn ngữ C++, và nếu bạn chưa biết Bjarne là ai thì trong nội dung bài viết sẽ cho bạn biết điều đó nhé! :)
“Làm việc với C thì rất dễ bắn vào bàn chân của bạn. Làm việc với C++ thì còn khó hơn, và nếu không cẩn thận thì nó sẽ thổi bay cặp giò của bạn!” ~ Bjarne Stroustrup
Liệu tôi có nên đầu tư công sức để thật thành thạo ngôn ngữ C++?
Tôi yêu C++
C++ đã dạy tôi làm thế nào để thực sự viết code.
Nhớ lại những ngày tôi đã học những điều phức tạp của ngôn ngữ này, nào là Standard Template Library, và tất cả những kỹ thuật của việc quản lý bộ nhớ và các tính toán về con trỏ. Nghiêm túc mà nói thì đó là một khoảng thời gian tươi đẹp. Tôi nhớ là mình đã đọc đi đọc lại bộ sách Effective C++ của tác giả nổi tiếng Scott Meyers. Mỗi lần đọc thì tôi lại học thêm được một điều mới hoặc biết nhiều hơn về cách làm thế nào để sử dụng C++. Tôi đang nói tất cả những điều này chỉ để cho bạn biết rằng tôi không ghét C++. Tôi yêu C++. Có rất nhiều lập trình viên xuất sắc tôi biết ngày nay vẫn đang sử dụng C++, dạy những người khác làm thế nào để sử dụng nó và không có điều gì sai trái với điều đó cả. Thế thì vấn đề ở đây là gì?
Cái thông điệp mà người ta vừa gửi đi mới sai
C++11 vừa mới ra mắt gần đây và dường như đang có một sự hồi sinh lớn trong mối quan tâm phát triển C++.
Bạn đừng hiểu sai ý tôi lúc này. C++11 thì rất tuyệt vời! Tôi cũng tán thành 100% với tất cả những thay đổi mà người ta vừa làm. C++ rõ ràng đã trở nên dễ sử dụng hơn và nó thậm chí trở nên mạnh mẽ hơn. Tuy nhiên có một thứ vẫn không được cải thiện - và đấy mới là điều quan trọng nhất - nó không đơn giản hơn. Dường như có rất nhiều lập trình viên dày dạn kinh nghiệm đã quên mất rằng tại sao chúng ta đã ngừng sử dụng C++ để chuyển sang Java, C# và những ngôn ngữ hiện đại hơn. Nhiều lập trình viên trẻ tuổi mới vào nghề thực sự không biết về lịch sử này và đang rất bối rối về thông điệp hiện tại của C++ cùng với sự hồi sinh của nó. Mọi người vẫn đang hỏi tôi rằng liệu họ có cần phải học C++ hay không, nhưng cũng như câu trả lời của tôi một vài năm trước đây, và nó vẫn giống hệt như ngày hôm nay là - KHÔNG! Vâng, mặc dù chữ “KHÔNG” viết in hoa thì hơi thô lỗ một chút. Vì thế có lẽ câu trả lời tốt hơn nên là “tại sao”?
Tại sao bạn lại muốn học C++?
Chỉ có khoảng 3 lý do được xem là hợp lý để học C++ ngày nay mà tôi có thể nghĩ ra.
- Bạn muốn cải thiện từng chút tốc độ thực thi của phần mềm bạn viết và bạn muốn làm điều đó với một ngôn ngữ có hỗ trợ các lớp trừu tượng hướng đối tượng (OO).
- Bạn đang viết code giao tiếp trực tiếp với phần cứng. (Ví dụ: bạn đang viết một phần mềm driver ở mức thấp.)
- Tác vụ của bạn yêu cầu kiểm soát bộ nhớ và thời gian một cách tuyệt đối, vì thế bạn phải quyết định hành vi hệ thống và kiểm soát bộ nhớ triệt để. (Liên quan đến một hệ điều hành nhúng thời gian thực đang điều khiển một số thao tác di chuyển của robot.)
Có thể là tôi đã bỏ sót một vài trường hợp nào đó, nhưng có lẽ bất kỳ trường hợp nào khác nhìn chung sẽ rơi vào trong 3 thể loại nói trên. Khoan! Đợi đã!Thế còn về tính đa nền tảng thì sao? Không! Làm ơn đừng học C++ vì nghĩ rằng bạn sẽ xây dựng phần mềm có thể chạy bất kỳ đâu. Hiện nay có rất nhiều tùy chọn khác để làm điều đó và C++ thì không đa nền tảng như nhiều người luôn tin hoặc đồn đại. Mức trừu tượng thấp hơn thì càng khác nhau trong mỗi hệ điều hành và nền tảng. Và làm ơn cũng đừng nói với tôi rằng C++ mang lại cho bạn hiều sức mạnh và kiểm soát để làm chính xác điều mà bạn cần. Hầu hết công việc phát triển phần mềm là về quản lý sự phức tạp chứ không phải về việc có khả năng thao tác đến từng bit tại một mức thấp hơn. Trong hầu hết các trường hợp của việc phát triển những phần mềm lớn và hiện đại thì chính sự trừu tượng ở mức cao hơn, chứ không phải là sự trừu tượng ở mức thấp hơn mới là điều mà bạn muốn.
Nhưng, dù sao đi nữa tôi cũng muốn học C++
Vâng, tôi biết bạn nghĩ rằng Windows 8 thì rất mới mẻ và tất cả những lũ nhóc đang học C++ để viết các ứng dụng Windows 8 của chúng, nhưng cũng như bạn nên biết có cái gì trong bột trái cây Kool-aid trước khi bạn uống nó. Viết code trong C++ thì không đơn thuần là một cuộc dã ngoại. Thậm chí cùng với tất cả sự thay đổi trong C++11, thì C++ vẫn là một ngôn ngữ rất khó để học và thậm chí còn khó hơn để có thể tinh thông nó. Bạn có thể đã nghe thấy câu nói nổi tiếng này về C++:
“Làm việc với C thì rất dễ bắn vào bàn chân của bạn. Làm việc với C++ thì còn khó hơn, và nếu không cẩn thận thì nó sẽ thổi bay cặp giò của bạn!” ~ Bjarne Stroustrup
Và nếu bạn không biết Bjarne là ai thì, vâng, ông ta chính là cha đẻ của ngôn ngữ C++. Vì thế nếu ông ta đã nói về ngôn ngữ đó như vậy, thì tôi chắc chắn rằng bạn có thể đã đưa ra được kết luận cho riêng mình. Tôi vẫn nghe thấy rằng hiện nay C++11 làm cho mọi thứ trở nên dễ dàng hơn và rằng C++11 đã khắc phục được rất nhiều vấn đề của C++98. Tôi không nghi ngờ về điều đó. Cái mà nó vẫn chưa sửa được chính là kích thước và phạm vi của ngôn ngữ đó - nó thực ra đã tăng lên. Chắc chắn là, bạn có thể sử dụng chỉ một phần nhỏ của C++. Chắc chắn là, bạn có thể sử dụng tính năng mới smart pointers trong C++ để tránh việc bạn phải thao tác bằng tay để quản lý bộ nhớ. Bạn có thể sử dụng biểu thức lambda để khai báo các hàm in-line thay vì truyền các con trỏ hàm. Dạng suy luận tự động là điều cần thiết để giúp bạn có thể hít thở trong một bầu không khí trong lành. Vấn đề ở đây là bạn sẽ vẫn phải biết những cách cũ để làm mọi thứ và bạn phải hiểu chính xác điều gì đang thực sự diễn ra khi bạn đang debug một chương trình C++ mà rất dễ có khả năng stomping bộ nhớ (đây là điều mà bạn có thể chưa bao giờ nghe thấy nếu bạn được sinh ra trong thế giới của managed code.) Bạn sẽ bắt gặp những đoạn code C++ từ 20 năm về trước và trông nó sẽ như là một ngôn ngữ hoàn toàn khác vậy.
Sau đây là danh sách một số câu hỏi phỏng vấn thực tế mà tôi đã sử dụng để hỏi những lập trình viên C++ trong các buổi phỏng vấn tuyển dụng cho công việc mà tôi đã thuê.
- Có bao nhiêu cách để khởi tạo một kiểu dữ liệu nguyên thủy trong C++ và đó là những cách nào?
- Tại sao bạn nên khai báo một hàm hủy là ảo (virtual)?
- Nó có nghĩa gì khi nói rằng C++ hỗ trợ overloading (nạp chồng)?
- Nêu ra một số ví dụ về overloading trong C++?
- Khái niệm name mangling trong C++ có nghĩa là gì và tại sao nó lại được sử dụng?
- Một lớp trừu tượng cơ sở (abstract base) có nghĩa là gì?
- RTTI có nghĩa là gì?
- Làm thế nào để bạn có thể truy cập một biến mà nó bị “che khuất” bởi một biến khác có cùng tên?
- Khái niệm namespace có nghĩa là gì và làm thế nào để sử dụng nó?
- Điểm khác nhau giữa một lớp và một cấu trúc (struct) trong C++ là gì, và so sánh với ngôn ngữ C thì như thế nào?
- Khái niệm template là gì? Làm thế nào để sử dụng chúng?
- Một hàm tạo sao chép (copy constructor) có nghĩa là gì và khi nào thì nó được sử dụng, đặc biệt là khi so sánh với toán tử =
- Điểm khác nhau giữa một sao chép “shallow” và “deep” là gì?
- Hằng toán tử (const operator) nghĩa là gì và nó được sử dụng như thế nào?
- Điểm khác nhau giữa truyền tham chiếu, truyền tham trị, và truyền bởi con trỏ trong C++ là gì?
- Khi nào thì nên và khi nào thì không nên trả về một giá trị bởi tham chiếu trong C++?
- Điểm khác nhau giữa một biến tạo ra trên ngăn xếp (stack) và một biến tạo ra trên heap là gì?
- Làm thế nào để bạn giải phóng bộ nhớ đã cấp phát động cho một mảng?
- Đa kế thừa là gì? Khi nào thì nó nên được sử dụng?
- Một hàm ảo thuần túy (pure virtual) là gì?
- Từ khóa mutable có ý nghĩa gì?
- Từ khóa volatile có ý nghĩa gì?
- Khái niệm STL có nghĩa là gì?
- Khái niệm Vector có nghĩa là gì?
- Cái gì được chứa strong phần header <algorithms>?
- Điểm khác nhau giữa #include <iostream.h> và #include <iostream> là gì?
- Điểm khác nhau giữa “++i” and “i++” là gì?
- Ước lượng short circuit có nghĩa là gì? Nó được sử dụng như thế nào? Tại sao việc sử dụng nó có thể gây nguy hiểm?
- Toán tử ‘,’ có ý nghĩa gì?
- Toán tử tam nguyên có nghĩa là gì? Cách sử dụng nó như thế nào?
- Hàm hằng thành viên (const member) có nghĩa là gì và nó được sử dụng như thế nào?
- Làm thế nào để sử dụng try/catch trong C++?
- Tại sao bạn đừng bao giờ ném ra một ngoại lệ trong một hàm hủy?
- Từ khóa explicit có ý nghĩa gì?
- Đâu là cách đúng để thực thi một ép kiểu trong C++?
- Khái niệm inline có nghĩa là gì?
Một số trong những câu hỏi trên có cùng câu trả lời đối với C# hoặc Java, nhưng bạn có thể thấy từ danh sách này rằng C++ thì rất, rất sâu. Có một số lượng rất lớn kiến thức để biết về ngôn ngữ này. Phát triển trong C# và Java thì gọi là học về ngôn ngữ đó, nhưng thực ra phần lớn là học về cách sử dụng các thư viện. Còn phát triển trong C++ thì phần nhiều là học về mọi ngóc ngách và xó xỉnh của ngôn ngữ này.
C++ là hướng đi sai lầm cho tương lai
Vấn đề lớn nhất đó là các ngôn ngữ lập trình thực ra cần phải trở nên ngày càng đơn giản hơn và gia tăng mức độ trừu tượng chứ không phải là giảm đi. Sẽ luôn luôn cần những đoạn code ở mức thấp, nhưng phần lớn code mà chúng ta viết ngày nay thì đa phần là ở mức cao nhiều hơn. Lần đầu tiên tôi đã nhảy khỏi con thuyền C++ là nhiều năm về trước khi mà tôi cuối cùng có thể không còn tạo ra lý lẽ rằng mình có thể phát triển các ứng dụng nhanh hơn trong C++ so với C#.
Tôi đã giữ trong một thời gian dài một niềm tin rằng tất cả những đầu tư công sức của mình đã làm với C++ sẽ không uổng phí, nhưng sau đó nhận ra rằng C# đã làm đơn giản mọi thứ tới một mức độ tuyệt vời, và rằng những tính năng bổ sung của C++ thì không còn mang lại cho tôi chút giá trị nào nữa. Ngay khi tôi vừa dành ra chút thời gian giải lao khi đang viết bài này, thì tôi đã tình cờ lướt qua một dự án vừa được hồi sinh từ Microsoft Research gọi là touchdevelop (dự án lập trình chỉ bằng touch và kéo thả chứ không phải viết code như hiện nay nữa). Đây mới là hướng đi mà chúng ta cần đi. Chúng ta cần các ngôn ngữ lập trình ngày càng phải trở nên đơn giản hơn để sử dụng, chứ không phải ngày càng phức tạp hơn. Đừng hiểu sai ý tôi, bạn có thể không đánh giá cao đối với dự án touchdevelop, nhưng tôi tin rằng một cái gì đó tương tự như vậy sẽ là tương lai của chúng ta.
Dường như Microsoft đang làm một việc khá lịch sự là tạo ra lực đẩy để mang C++ quay trở lại “sân khấu” bằng việc hỗ trợ C++ trong Windows 8 và đang thử xóa sổ XNA, nhưng tôi nghĩ rằng có một sự nhận thức sai của cộng đồng phát triển và dường như đã khắc sâu vào tâm trí của các nhà phát triển hệ điều hành Windows rằng các lập trình viên đang muốn sử dụng C++. Tôi chỉ không nhìn thấy mình quay trở lại tới một ngôn ngữ khó hơn khi mà tôi có thể viết code C# để làm mọi thứ trên mọi nền tảng ngay lúc này. C# thì không phải là một ngôn ngữ hoàn hảo, nhưng nó thì rất mạnh mẽ và dễ dàng sử dụng.
Một vài lời bàn về C++
Cùng với tất cả những điều mà tôi đã nói ở trên, tôi vẫn luôn tin rằng có một giá trị trong việc học C++. Không, tôi không cố gắng mâu thuẫn với chính mình ở đây. Hãy cho phép tôi giải thích thêm. Nếu bạn có thể lập trình trong C++ thì bạn có thể lập trình trong bất cứ ngôn ngữ lập trình nào khác. Nếu bạn hiểu về bộ nhớ ngăn xếp (stack) và heap hoạt động, con trỏ và tham chiếu và tất cả những chi tiết ở mức thấp đã làm cho C++ trở nên phức tạp, thì nó sẽ giúp bạn khi làm việc với các lớp trừu tượng ở mức cao hơn và hiểu được cách thức máy tính làm việc như thế nào.
Quan điểm của tôi trong bài viết này thì không đánh vào C++ hoặc đánh vào những người đang sử dụng C++ hay đang dạy C++, nhưng đúng hơn là chỉ làm “cùn” đi cái thông điệp dường như đang được tạo ra bởi cộng đồng những người ủng hộ C++ rằng C++ đang quay trở lại.
Mọi người sẽ không trở thành một lập trình viên C++ và họ không cần thiết phải như vậy. Trong khi C++ có thể có khả năng làm cho chương trình của bạn hoạt động hiệu suất hơn (trong một số trường hợp nhất định), nhưng nó lại không chắc sẽ làm cho bạn hiệu quả hơn trong việc tạo ra chương trình của mình (ngoại trừ trong một số trường hợp đặc biệt). Vì vậy, mặc dù tôi lấy làm vui mừng vì C++ đã nhận được rất nhiều sự sửa chữa bổ sung cần thiết, nhưng tôi không nghĩ nó sẽ quay trở lại trong thời gian sắp tới, và đó lại là một điều tốt.