Why Is C++ So Powerful?

Thirty-one years ago (1979), a researcher who had just obtained his PhD invented a new programming language to develop a software project. This researcher was Bjarne Stroustrup, and the language was named C with classes, 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 generics were discovered and proven to be Turing complete, which allows C++ to also support the template metaprogramming (TMP) paradigm.

C++ inherits the features of C, serving as both a high-level language and possessing low-level language capabilities, making it suitable for both system and application programming.

C++ is widely used across various fields, with millions of users. According to surveys over the past decade, C++ has consistently ranked third in popularity (after C and Java). C++ has evolved through long-term practice and development 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 on 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 200x years, and there was hope that the new standard would 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 has more support for C++0x syntax and libraries than VC2010.

Should You Choose C++?

What Programs Are Suitable for C++?

C++ is not a panacea, but based on my experience, I can outline some suitable scenarios for using C++.

  • C++ is suitable for constructing stable parts of programs, while parts with more variable requirements can use scripting languages;
  • Programs must maximize hardware performance, especially when performance bottlenecks lie in the CPU and memory;
  • Programs need to frequently communicate with the operating system or hardware;
  • Programs must use C++ frameworks/libraries, such as most game engines (like Unreal/Source) and middleware (like Havok/FMOD). Although some C++ libraries provide bindings for other languages, native APIs usually perform best and are the most up-to-date;
  • A certain target platform in the project only supports C++ compilers.

In terms of application domains, C++ is suitable for developing server software, desktop applications, games, real-time systems, high-performance computing, embedded systems, and more.

Should I Use C++ or C?

The design philosophies of C++ and C are different, and their trade-offs vary, making it difficult to generalize which to choose for different programmers and software projects. Compared to C++, C has advantages such as faster compilation speed, easier learning curve, explicit description of program details, and fewer updates to standards (the latter two can also be seen as drawbacks). At the language level, C++ includes almost all of C’s functionality (one exception is that C++ does not have C99’s variable-length arrays, VLA) and offers features of OOP and GP.

However, OOP concepts can also be implemented in C, and macros can be used to achieve some level of GP; it’s just that 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 corresponding functionality in C/C#/Java. Historically, Stroustrup’s early C++ compiler Cpre/Cfront translated C++ source code into C, which was then compiled by a C compiler.

From this, it can be seen that programs written in C++ can be replaced by equivalent C programs, but C++ provides OOP/GP syntax, stricter type-checking systems, and many additional language features (such as exceptions, RTTI, etc.) at the language level, and the C++ standard library is also more comprehensive.

Sometimes, C++ syntax can make programs more concise, such as operator overloading and implicit conversions. On the other hand, C language APIs are usually simpler than C++ APIs, making them easier for programs written in other languages to call.

Therefore, some C++ libraries provide C API wrappers for use in C programs. Conversely, sometimes C APIs are wrapped into C++ forms to support RAII and integration with other C++ libraries.

Why Can C++ Perform Better Than Other Languages?

Relative to languages running on virtual machines (like C#/Java), C/C++ directly compiles source programs into machine code for the target platform in a static manner. Generally speaking, C/C++ programs can undergo the richest optimizations during compilation and linking, start up the fastest, and incur the least additional memory overhead at runtime. Compared to dynamic languages (like Python/Lua), C/C++ also reduces runtime dynamic type checking.

Furthermore, the runtime behavior of C/C++ is deterministic, with no additional behaviors (for example, C#/Java will inevitably initialize variables), and there is no uncertainty delay caused by garbage collection (GC), and the layout of data structures in memory is also deterministic.

Sometimes, certain features of C++ can make its performance superior to C, particularly inlining and templates, which allow the C++ standard library’s sort() to be many times faster than the C standard library’s qsort() (C can resolve this issue using macros or manual coding).

On the other hand, C/C++ can directly map to machine code without an intermediate language layer, allowing for low-level optimizations, such as using intrinsic functions and embedded assembly language.

However, many of C++’s performance advantages are not free lunches; the costs include longer compilation and linking times and increased likelihood of errors, which can raise development time and costs. This point will be elaborated later.

I conducted a simple global rendering performance test (512×512 pixels, 10,000 samples per pixel): 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 are detailed in the blog post.

Common Questions About C++

Is C++ Source Code Cross-Platform?

C++ has decent cross-platform capabilities, but due to its direct hardware mapping and performance optimizations, its cross-platform abilities are not as strong as Java and most scripting languages. However, practical cross-platform C++ software is still feasible, but the following issues must be noted:

  • The C++ standard does not specify the size of primitive data types (such as int). When specific-sized types are needed, custom types (like int32_t) should be defined, and the sizeof() operator should be used on any type without assuming its size;
  • Byte order varies by CPU, especially to be cautious with binary input/output and reinterpret_cast;
  • Address alignment of primitive data and structure types varies;
  • Some compiler or platform-specific extension instructions provided by the compiler;
  • Avoid making assumptions about the application binary interface (ABI), for example, the order of parameter passing in function calls is not defined in C/C++, and one should not casually assume the implementation of RTTI/vtables, etc.

In summary, cross-platform C++ software can use macros in header files to detect compilers and platforms, and then implement cross-platform practices with macros, typedefs, and custom platform-related implementations. The C++ standard does not provide such assistance.

Are C++ Programs Prone to Crashing?

Compared to many languages, C/C++ provides unsafe features to optimize performance, which can lead to crashes.

However, it is important to note that many runtime errors, such as dereferencing null pointers/references, array out-of-bounds, and stack overflow, will also result in errors or exceptions in other languages. These are program issues, not issues with the language itself.

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 causing more significant impacts (such as continuing to overwrite files with incorrect data in memory).

If fault tolerance is needed, 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-style strings; using std::vector or std::array (TR1) instead of raw arrays (some implementations can check for out-of-bounds in debug mode); and using smart pointers can mitigate 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 between debug and release versions 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 need to manually release memory or use different containers and smart pointers. C++ programmers often optimize memory further by customizing memory allocation strategies to improve performance, such as using object pools or custom single/double stack areas.

Although C++0x has not yet introduced GC functionality, one can write their own or use existing libraries. Additionally, C/C++ can directly use memory-related functionalities provided by the operating system, such as memory-mapped files and shared memory.

Do You Often Have to Reinvent the Wheel in C++?

In the C++ projects I have participated in, we often end up reinventing many features already provided by the standard library, a situation less common in other languages. I tried to analyze the reasons behind this.

Firstly, the C++ standard library is relatively lacking compared to many languages, leading developers to repeatedly create custom libraries.

From another perspective, the C++ standard library is written in C++ (many other languages do not use their own language but rather C/C++ to write libraries), and there is no essential difference in capability and performance between custom libraries and standard libraries;

Additionally, the standard library is designed to be generic, making trade-offs for different platforms and various usage needs, which can impact performance. For example, EA has published its own EASTL specification, describing the performance and functional requirements of 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 bring in 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 arises from C++’s inheritance of C’s compilation and linking methods, combined with the complexity of class/generic declarations and inlining mechanisms, which significantly increase compilation time.

Before C++ reformulates its compilation methods (like the module proposal), the following techniques can be used to improve:

First, use the pimpl idiom, applying performance loss to classes that are not called often;

Second, include only necessary header files and try to use and provide forward declaration versions of header files (like iosfwd);

Third, adopt an interface-based design, but be cautious of the cost of virtual function calls;

Fourth, use unity builds, which combine multiple cpp files into a single compilation unit;

Fifth, use distributed build systems like IncrediBuild.

What Features Does C++ Lack?

Although C++ is already quite complex, it still lacks many common features. C++0x has made many improvements, such as introducing Lambda functions, closures, type inference declarations, etc., at the language level, while the library side has added regular expressions, hashed containers like unordered_set/unordered_map, and reference-counted smart pointers like shared_ptr/weak_ptr.

However, the most notable addition is the introduction of multi-threading syntax and library functionality in C++0x, marking a significant step in the evolution of C++. However, features like modules, GC, and reflection mechanisms have been proposed but not included in C++0x.

C++ Usage Recommendations

Select Feature Sets for Applications

I agree with Stroustrup’s response regarding the use of various techniques in C++: “Just because you can do it, doesn’t mean that you have to.” C++ is filled with rich features, but it also brings different problems, such as excessive complexity and performance loss during compilation and runtime.

Generally, consider whether to use multiple inheritance, exceptions, RTTI, and adjust the extent of template and template metaprogramming usage. 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 high coding freedom, making it easy to write code in vastly different styles, and C++ itself does not define standard specifications. Additionally, the physical composition of C++ source files is more complex than many languages. Therefore, besides deciding on feature sets, each team should establish a set of programming standards, including source file formats (using file templates) and bracket styles.

Try to Use C++ Style Instead of C Style

Because C++ carries the burden of 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 generics, using macros only for conditional compilation and special cases. Old-style C requires local variable declarations at the beginning of the scope, but C++ does not have this restriction. Variables should be declared as close as possible to where they are used, and loop variables in for() can be declared within the parentheses of the for loop.

Features that enhance type safety in C++ should be used as much as possible, such as avoiding “universal” pointers (void *) and using specific or generic types; using bool instead of int to represent boolean values; and selecting one of the four C++ cast keywords instead of simple casts.

Combining with Other Languages

As mentioned earlier, C++ is not suitable for all application contexts, 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 also be used for integration.

C++ Learning Recommendations

One of the drawbacks of C++ is that it is relatively complex compared to many languages and is difficult to learn and master. Many people say that learning C can be accomplished with just one book, K&R’s The C Programming Language, but there are countless books on C++. I entered C++ from C, relying on self-study through reading. Here, I would like to share some learning insights. I believe learning C++ can be divided into four levels:

  • First Level, C++ Basics: Choose an introductory book, such as C++ Primer, C++ Programming: A University Course, or Stroustrup’s classic The C++ Programming Language or his newer work Principles and Practice Using C++. General C++ courses usually stop here. Additionally, The C++ Standard Library and The C++ Standard Library Extensions can be referenced;
  • Second Level, Using C++ Correctly and Efficiently: This level requires self-study, and after reading (More) Effective C++, (More) Exceptional C++, Effective STL, and C++ Coding Standards, it is suitable to enter the professional C++ development path;
  • Third Level, In-Depth Understanding of C++: For global issues, read Exploring C++ Object Models, Imperfect C++, C++ Concurrency in Action, and for a challenge, books on templates and template metaprogramming like C++ Templates, Design Patterns, and Modern C++ Design;
  • 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 ISO/IEC 14882:2003, proposals and reports from the C++ standards committee, and academic literature on C++.

Since I mainly apply C++, I have only reached the second and third levels. However, C++ is just one part of software development; relying solely on the language cannot address business and engineering issues.

I recommend that readers not pressure themselves to “master all knowledge of C++” within a few years. Instead, reach around the second level and gain experience from practical work, and continue to learn higher-level knowledge at a comfortable pace. Although learning C++ is challenging, it is also quite interesting and rewarding.

For decades, C++ has had its ups and downs, but it relies on its users for its resilience, and I believe I will not part with it before I retire, hoping to further understand it and walk into the future with it.

Excerpted from The Power Behind C++ – Milo Yip – Blog Garden (cnblogs.com)

Leave a Comment