Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Debunking C++ Myths

You're reading from   Debunking C++ Myths Embark on an insightful journey to uncover the truths behind popular C++ myths and misconceptions

Arrow left icon
Product type Paperback
Published in Dec 2024
Publisher Packt
ISBN-13 9781835884782
Length 226 pages
Edition 1st Edition
Arrow right icon
Authors (2):
Arrow left icon
Ferenc Deak Ferenc Deak
Author Profile Icon Ferenc Deak
Ferenc Deak
Alexandru Bolboaca Alexandru Bolboaca
Author Profile Icon Alexandru Bolboaca
Alexandru Bolboaca
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Preface 1. Chapter 1: C++ Is Very Difficult to Learn 2. Chapter 2: Every C++ Program Is Standard-Compliant FREE CHAPTER 3. Chapter 3: There’s a Single C++, and It Is Object-Oriented 4. Chapter 4: The Main() Function is the Entry Point to Your Application 5. Chapter 5: In a C++ Class, Order Must There Be 6. Chapter 6: C++ Is Not Memory-Safe 7. Chapter 7: There’s No Simple Way to Do Parallelism and Concurrency in C++ 8. Chapter 8: The Fastest C++ Code is Inline Assembly 9. Chapter 9: C++ Is Beautiful 10. Chapter 10: There Are No Libraries For Modern Programming in C++ 11. Chapter 11: C++ Is Backward Compatible ...Even with C 12. Chapter 12: Rust Will Replace C++ 13. Index 14. Other Books You May Enjoy

The hard parts of C++ and how to grasp them

Is C++ as easy to learn as Java, C#, PHP, JavaScript, or Python? Despite all the language improvements, the answer is: most likely not. The important question is: Should C++ be as easy to learn as all these other languages?

The demise of C++ has been predicted for a very long time. Java, then C#, and nowadays Rust were in turn touted as complete replacements for our venerable subject of debate. Instead, each of them seems to carve their own niche while C++ is still leading in programs that require careful optimization or work in constrained environments. It helps that millions of lines of C++ exist today, some of them decades old. While some of them can be turned into cloud-native, serverless, or microservices architectures, there will always be problems better fit for the engineering style serviced by C++.

We conclude, therefore, that C++ has its own purpose in the world of development, and any new programming language faces a steep uphill battle to displace it. This observation comes with its consequence: specific parts of C++ will necessarily be more difficult to grasp than other languages. While Java or C# will spare you from thinking of memory allocation and what happens with the memory when you pass arguments to another method, C++ needs to take these issues head-on and give you the option to optimize your code as your context dictates.

Therefore, if you want to understand C++, you can’t escape memory management. Fortunately, it’s much less of an issue than it used to be.

Let’s analyze the differences by looking at how different languages manage memory allocation and release. Java uses a full object-oriented (OO) approach, in which every value is an object. C# designers decided to use both value types that include the typical numeric values, chars, structs, and enums, and reference types that correspond to the objects. In Python, every value is an object, and the type can be established later in the program. All these three languages feature a garbage collector that deals with memory release. The Python language uses a reference counting mechanism in addition to the garbage collector, thus allowing it to be optionally disabled.

The C++ 98 standard didn’t provide any built-in mechanism for pointer release, instead providing the full power and responsibility for memory management to the programmer. Unfortunately, this led to problems. Suppose that you initialize a pointer and allocate a large area of memory for a value. You then pass this pointer to other methods. Who is responsible for releasing the memory?

See, for example, the following simple code sample:

BigDataStructure* pData = new pData();
call1(pData);
call2(pData);
call3(pData);

Should the caller release the memory allocated in pData? Should call3 do it? What if call3 calls another function with the same pData instance? Who is responsible for releasing it? What happens if call2 fails?

The responsibility for memory release is ambiguous and, therefore, needs to be specified for every function or for every scope, to be more precise. The complexity of this problem increases with the complexity of programs and data flows. This would make most programmers using the other mainstream languages scratch their heads or completely ignore the responsibility and end up either with memory leaks or with calls to memory areas that have been already released.

Java, C#, and Python solve all these issues without asking the programmer to be careful. Two techniques are helpful: reference counting and garbage collection. Reference counting works as follows: upon every call to copy the value, the reference count is increased. When getting out of scope, the reference count is decreased. When the reference count gets to 0, release the memory. Garbage collectors work similarly, only they run periodically and check also for circular references, ensuring that even convoluted memory structures get released correctly, albeit with a delay.

Even back in the 2000s, nothing was stopping us from implementing reference counting in C++. The design pattern is known as smart pointers and allows us to think less about these issues.

In fact, C++ had from the very beginning yet another, more elegant way, to deal with this problem: pass-by-reference. There’s a good reason why pass-by-reference is the default way to pass objects around in Java, C#, and Python: it’s very natural and convenient. It allows you to create an object, allocate its memory, pass by reference, and the best part: its memory will automatically get released upon exiting the scope. Let’s look at a similar example to the one using pointers:

BigDataStructure data{};
call1(data);
call2(data);
call3(data);
...
void call1(BigDataStructure& data){
    ...
}

This time, it doesn’t really matter what happens in call1; the memory will be released correctly after exiting the scope in which data is initialized. The only limitation of reference types is that the memory allocated for the variable cannot be reallocated. Personally, I see this as a big advantage, given that modifying data can get messy very quickly; in fact, I prefer to pass every value with const& if possible. There are, however, limited applications for highly optimized polymorphic data structures that are enabled through memory reallocation.

Looking at the preceding program, if we ignore the & sign from call1 and rename the functions to fit their corresponding conventions, we could also read Java or C#. So, C++ could have been close to these languages from the beginning. Why isn’t it still similar enough?

Well, you can’t escape memory management in C++. The preceding code would not make a Java or C# programmer think of anything more; we established that C++ is different, though. The standardization committee realized that there are situations when we need to allocate memory in one function and release it in another and that it would be ideal to avoid using pointers to do that. Enter move semantics.

Note

Move semantics is a key feature introduced in C++11 to enhance performance by eliminating unnecessary copying of objects. It allows resources to be transferred from one object to another without creating a copy, which is especially beneficial for objects that manage dynamic memory, file handles, or other resources. To utilize move semantics, you need to implement a move constructor, which initializes a new object by transferring resources from a rvalue (temporary object) to the new object, and a move assignment operator, which transfers resources from a rvalue to an existing object for your class. The std::move function is a utility that casts an object to a rvalue reference, enabling move semantics. To help, the compiler creates the move constructor in certain conditions.

See in the following example how we might use move semantics to move the scope of a variable to the function process:

BigDataStructure data{};
process(data);
...
void process(BigDataStructure&& data){
}

Not much seems different, other than using two ampersand signs. The behavior is, however, very different. The scope of the data variable moves into the called function, and process, and the memory gets released upon exiting it.

Move semantics allows us to avoid copying big data values and to transfer the responsibility for releasing the memory into called functions. This is a unique mechanic between the languages we’ve discussed until now. To my best knowledge, the only other programming languages to implement these mechanics are the other contenders for systems programming: Rust and Swift.

This proves to us that, as much as C++ resembles Java or C# nowadays, it does require programmers to understand in more detail the way memory allocation and release work. We may have gotten over the exam questions that focused on minor syntax differences with big effects, but we haven’t gotten over the need to learn more than for the other languages.

Memory management, while a big part of the conversation, is not the only thing that makes things more difficult when learning C++. A few things are different and can be a bit annoying for newcomers:

  • The need for #ifndef preprocessor directives or the non-standard but often supported #pragma once to ensure that files are only included once
  • Separate .h files along with arbitrary rules of what goes in .h and what goes in .cpp
  • The very weird way to define interfaces with virtual methodName()=0

While we can ensure we use all these contraptions with rules and guidelines automatically applied by modern IDEs, their presence begs the question: Why are they still needed?

Barring the aforementioned, it is much more difficult to get over the fact that there’s no easy way to build a program and add external references. Java, with all its faults, has a single compiler, and Maven/Gradle as standard tools for dependency management that allow the download and integration of a new library with a simple command. C#, although fraught with the same issue for a long time, has pretty much standardized the community-created NuGet command for getting external libraries. Python features the standard pip command for managing packages.

With C++, you need to work more. Unlike Java and C#, which count on a virtual machine, your C++ programs need to be compiled for every supported target, and each target matched with the right libraries. Of course, there are tools for that. The two package managers I’ve heard mentioned the most are Conan and vcpkg. For build systems, CMake seems quite popular. The trouble is that none of these tools are standard. While it’s true that neither Java’s Maven/Gradle nor C#’s NuGet have started as a standard, their integration in tools and fast adoption means that they are the de facto standard today. C++ has a little bit more to go until this part of the language matures. We’ll talk more about these issues in a separate chapter, but it’s obvious that part of the C++ confusion is also generated by this complexity in trying out simple programs.

We looked at various complications in C++ compared to other languages, and we saw that while the language has gotten easier, it’s still not as easy as Java or C#. But the core question is: Is C++ very difficult to learn? To examine this, let’s look at three methods beginners can use to learn C++.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image