Author: Milo Yip
Link: https://www.cnblogs.com/miloyip/archive/2010/09/17/behind_cplusplus.html
Thirty-one years ago (1979), a newly graduated researcher invented a new programming language to develop a software project. This researcher, named Bjarne Stroustrup, named the language C with classes, which was later renamed C++ four years later.
C++ is a general-purpose programming language that supports multiple programming paradigms, including procedural, object-oriented programming (OOP), and generic programming (GP). The templates designed for generic programming have been found and proven to be Turing complete, thus allowing C++ to also support the template metaprogramming (TMP) paradigm.
C++ inherits the characteristics of C, being both a high-level language and containing low-level language features, allowing it to serve as both a system and application programming language.
C++ is widely used in various fields, with millions of users. According to surveys over the past decade, C++ has consistently ranked around third in popularity (behind C and Java). It has undergone long-term practice and evolution to become what it is today.
In 1998, the C++ standards committee overcame numerous challenges to make C++ an ISO standard (commonly known as C++98), which included a very powerful standard template library (STL).
Subsequently, the committee submitted the first technical report regarding the standard library in 2005 (referred to as TR1) and worked towards the next standard, C++0x. Unfortunately, C++0x could not be completed in the 2000s, and there was hope for the new standard to be released in 2011.
Among popular C++ compilers, Microsoft Visual C++ 2010 has implemented some C++0x syntax and added the TR1 extension library, while gcc supports more C++0x syntax and libraries than VC2010.
Should You Choose C++?
What Programs Are Suitable for C++?
C++ is not a panacea. Based on my experience, I will outline some suitable scenarios for using C++.
-
C++ is suitable for constructing parts of programs with stable requirements, while parts with more variable requirements can use scripting languages; -
Programs that need to maximize hardware performance, particularly when performance bottlenecks are due to CPU and memory; -
Programs that frequently communicate with the operating system or hardware; -
Programs that must use C++ frameworks/libraries, such as most game engines (like Unreal/Source) and middleware (like Havok/FMOD). While some C++ libraries provide bindings for other languages, native APIs typically offer the best and latest performance; -
Some target platforms in the project only provide support for C++ compilers.
In terms of application domains, C++ is suitable for developing server software, desktop applications, games, real-time systems, high-performance computing, and embedded systems.
Use C++ or C?
The design philosophies of C++ and C are different, and their trade-offs vary, so different programmers and software projects will have different choices, making it difficult to generalize. Compared to C++, C has advantages such as faster compilation speed, ease of learning, explicit description of program details, and fewer updates to standards (the last two can also be viewed as drawbacks). At the language level, C++ includes almost all of C’s functionality (with one exception, C++ does not have the variable-length arrays (VLA) of C99), while providing OOP and GP features.
However, OOP concepts can also be implemented in C, and macros can be used to achieve a certain degree of GP. However, C++ syntax allows for a more concise and automatic implementation of OOP/GP.
C++’s RAII (Resource Acquisition Is Initialization) feature is quite unique, with no equivalent in C/C#/Java. Historically, the early C++ compilers developed by Stroustrup, Cpre/Cfront, translated C++ source code into C, which was then compiled with a C compiler.
This shows that programs written in C++ can be replaced by equivalent C programs, but C++ offers OOP/GP syntax at the language level, stricter type-checking systems, and a wealth of additional language features (such as exceptions, RTTI, etc.), and the C++ standard library is also richer.
Sometimes C++ syntax can make programs more concise, such as operator overloading and implicit conversions. On the other hand, C language APIs are often simpler than C++ ones, making them easier for programs in other languages to call.
Therefore, some C++ libraries provide C API wrappers, which can also be called by C programs. Conversely, sometimes C APIs are wrapped in C++ forms to support RAII and integration with other C++ libraries.
Why Can C++ Outperform Other Languages?
Compared to virtual machine languages (such as C#/Java), C/C++ directly compiles source programs into machine code for the target platform in a static form. Generally speaking, C/C++ programs can undergo the most extensive optimizations at compile and link time, have the fastest startup speed, and incur the least additional memory overhead at runtime. Furthermore, C/C++ reduces runtime dynamic type checking compared to dynamic languages (like Python/Lua).
Moreover, C/C++ has deterministic runtime behavior and does not have additional behaviors (e.g., C#/Java will initialize variables), nor does it have the uncertainty delays caused by garbage collection (GC), and the layout of C/C++ data structures in memory is also deterministic.
Sometimes certain features of C++ can make programs perform better than C, with inlining and templates being the most notable. These two features allow the C++ standard library’s sort() to often be many times faster than the C standard library’s qsort() (C can use macros or manual coding to solve this issue).
On the other hand, C/C++ can directly map to machine code, with no intermediate language layer, allowing for low-level optimizations, such as using intrinsic functions and embedding assembly language.
However, many of C++’s performance advantages do not come without a cost, including longer compile and link times and increased likelihood of errors, which in turn increases development time and costs. This point will be elaborated on later.
I conducted a simple global rendering performance test (512×512 pixels, 10,000 samples per pixel), where C++ took 1 hour and 36 minutes, Java took 3 hours and 18 minutes, Python took about 18 days, and Ruby took about 351 days. The evaluation method and results for other languages can be found in the blog post.
Common Questions About C++
Is C++ Source Code Cross-Platform?
C++ has decent cross-platform capabilities, but due to direct hardware mapping and performance optimizations, its cross-platform capability is not as good as Java and most scripting languages. However, it is still feasible to practice cross-platform C++ software, but attention must be paid to the following issues:
-
The C++ standard does not specify the size of primitive data types (e.g., int). When specific-sized types are needed, custom types (like int32_t) can be defined, and sizeof() should be used for any type without assuming its size; -
Byte order may vary by CPU, especially for binary input/output and reinterpret_cast; -
Address alignment for primitive data and structure types may differ; -
Some compiler or platform-specific extension instructions provided by the compiler; -
Avoid making assumptions about application binary interfaces (ABI), for example, the order of parameter values when calling functions is not defined in C/C++, and in C++ it is also not safe to assume the implementation of RTTI/vtable, etc.
In summary, cross-platform C++ software can be implemented by detecting compilers and platforms in header files, and using macros, typedefs, and custom platform-related implementations, etc. The C++ standard does not provide such assistance.
Are C++ Programs Prone to Crashing?
Compared to many languages, C/C++ offers unsafe features to optimize performance, which may lead to crashes.
However, it is important to note that many runtime errors, such as dereferencing null pointers/references, array out-of-bounds, stack overflows, etc., can also occur in other languages, which are program issues rather than language issues.
Some opinions suggest that when such runtime errors occur, they should be logged and the program should crash immediately, rather than allowing it to continue running to avoid greater impacts (e.g., continuing to overwrite files with incorrect data in memory).
If fault tolerance is desired, the program can be divided into multiple processes, like Chrome or using fork().
However, C++ has many mechanisms to reduce errors, such as using std::string instead of C strings; using std::vector or std::array (TR1) instead of raw arrays (some implementations can check bounds in debug mode); and using smart pointers to reduce some raw pointer issues.
Additionally, one of the most common bugs I encounter is failing to initialize member variables, which can sometimes lead to crashes, and the behavior in debug and release builds may differ.
Does C++ Require Manual Memory Management?
C++ provides both automatic local variables on the stack and objects allocated from the free store.
For the latter, programmers must manually release them or use different containers and smart pointers. C++ programmers often further optimize memory by customizing memory allocation strategies to improve performance, such as using object pools and custom single/double-ended stack areas.
Although C++0x has not introduced GC functionality, it is possible to write one or use existing libraries. Additionally, C/C++ can directly use memory-related features provided by the operating system, such as memory-mapped files, shared memory, etc.
Do You Often Have to Reinvent the Wheel with C++?
In the C++ projects I have participated in, we often end up reinventing many functionalities already provided by the standard library, a situation that occurs less frequently in other languages. I have tried to analyze the reasons behind this.
Firstly, the C++ standard library is relatively sparse compared to many other languages, leading developers to repeatedly create custom libraries.
From another perspective, the C++ standard library is written in C++ (many other languages write libraries in C/C++ rather than their own), so there is no essential difference in capability and performance between custom libraries and the standard library;
Additionally, the standard library is designed to be general-purpose, making trade-offs for different platforms and various usage needs, which can affect performance. For example, EA has published its own EASTL specification, describing the performance and functional requirements for STL in game development;
Moreover, using multiple C++ libraries together often leads to conflicts due to differing specifications or overlapping functionalities, so projects may need to develop their own implementations or introduce concepts or implementations from other libraries (like Boost/TR1/Loki) and rewrite them to conform to project specifications.
Is C++ Compilation Speed Very Slow?
No, it is extremely slow. I believe C++ may have the slowest compilation speed among practical programming languages. This issue is due to C++ retaining the compilation and linking methods of C while adding complex class/template declarations and inline mechanisms, which greatly increases compilation time.
Before reforms to C++ compilation methods (such as the module proposal), the following techniques can be used to improve compilation speed:
First, use the pimpl idiom, as performance loss applies to classes that are not called frequently;
Second, include only necessary header files and try to use and provide forward declaration versions of header files (like iosfwd);
Third, adopt interface-based design but be cautious of the costs of virtual function calls;
Fourth, use unity builds, combining multiple cpp files into a single compilation unit;
Fifth, use distributed build systems like IncrediBuild.
What Features Does C++ Lack?
Although C++ is already very complex, it still lacks many common features. C++0x made many improvements, such as adding lambda functions, closures, and type inference declarations at the language level, and adding regular expressions, unordered_set/unordered_map using hash tables, reference-counted smart pointers shared_ptr/weak_ptr, etc., at the library level.
However, the most noteworthy improvement in C++0x is the introduction of syntax and library functionality for multithreading, marking a significant step in C++ evolution. However, features such as modules, GC, and reflection mechanisms, although proposed, were not included in C++0x.
C++ Usage Recommendations
Select a Feature Set for Applications
I agree with Stroustrup’s response regarding the use of various C++ techniques: “Just because you can do it, doesn’t mean that you have to.” C++ is filled with rich features, but they also bring different problems, such as excessive complexity and performance losses in compilation and runtime.
Generally, consider whether to use multiple inheritance, exceptions, RTTI, and adjust the extent of template and template metaprogramming usage. Using overly complex designs and features may make it harder for some team members to understand and maintain.
Establish Programming Standards for the Team
C++ offers a high degree of coding freedom, making it easy to write code in vastly different styles, and C++ itself does not define some standard specifications. Furthermore, the physical composition of C++ source files is more complex than many other languages. Therefore, in addition to deciding on the feature set, each team should establish a set of programming standards, including source file formats (using file templates) and brace styles.
Prefer C++ Style Over C Style
Due to C++’s compatibility with C, some features can be implemented in a C style, but it is best to use the new features provided by C++.
At a minimum, try to replace macros with named constants, inline functions, and templates, using macros only for conditional compilation and special cases. Old-style C requires local variable declarations at the beginning of the scope, while C++ does not have this restriction; variables should be declared as close as possible to where they are used, and loop variables in for() loops can be declared within the parentheses.
In C++, features that enhance type safety should be used whenever possible, such as avoiding “universal” pointers (void *) and using specific or generic types; use bool instead of int for boolean values; and choose from the four C++ cast keywords instead of simple casts.
Combine with Other Languages
As mentioned earlier, C++ is not suitable for all application scenarios, and sometimes it can be mixed with other languages, including extending other languages with C++, or embedding scripting language engines in C++ programs. For the latter, in addition to using specialized APIs for various scripting languages, Boost or SWIG can be used for integration.
C++ Learning Recommendations
One of the disadvantages of C++ is its relative complexity compared to many languages, making it difficult to learn and master. Many people say that learning C only requires K&R’s “The C Programming Language,” but there are countless books on C++. I transitioned from C to C++ and relied on self-study through reading. Here I share some learning insights. Personally, I believe learning C++ can be divided into four levels:
-
First Level: C++ Basics: Choose an introductory book, such as “C++ Primer,” “C++ University Course,” or the classic “The C++ Programming Language” by Stroustrup or his newer work “C++ Programming Principles and Practice” from a year and a half ago. Most general C++ courses stop at this level. Additionally, “The C++ Standard Library” and “The C++ Standard Library Extensions” can be referenced; -
Second Level: Using C++ Correctly and Efficiently: At this level, self-study is essential. Reading books like “(More) Effective C++,” “(More) Exceptional C++,” “Effective STL,” and “C++ Coding Standards” is suitable for embarking on a professional C++ development path; -
Third Level: In-Depth Understanding of C++: For global issues, one can read “Exploring the C++ Object Model,” “Imperfect C++,” “C++ Concurrency in Action,” and for a challenge, books on templates and template metaprogramming like “C++ Templates,” “C++ Design Patterns,” and “C++ Template Metaprogramming”; -
Fourth Level: Researching C++: Read “The Design and Evolution of C++,” “The Essence of Programming” (including the mathematical foundations behind STL design), C++ standard documents like “ISO/IEC 14882:2003,” proposals and reports from the C++ standards committee, and academic literature about C++.
Since I primarily use C++, I have only reached the second and third levels. However, C++ is just one aspect of software development, and the language alone cannot address business and engineering problems.
I recommend that readers not force themselves to “master all of C++ knowledge” within a few years. By reaching around the second level, one can gain experience from practical work and gradually continue learning more advanced knowledge if interested. Although learning C++ is challenging, it is also quite interesting and rewarding.
For decades, C++ has experienced ups and downs, but it continues to thrive thanks to its users. I believe I will not part ways with it before I retire, and I hope to learn more about it and move into the future with it.
Excerpted from “The Power Behind C++” – Milo Yip – Blog Garden (cnblogs.com)
—END—