I’ve been programming in C++ for a while now, but mostly things centered around the low-level features of C++. By that I mean mostly working with pointers and raw arrays. I think this behavior is known as using C++ as C with classes. Despite this, I only having tried C recently for the first time. I was pleasantly surprised how languages like C# and Java hide these details away in convenient standard library classes like Dictionaries and Lists.
I’m aware that the C++ standard library has many containers like vectors, maps and strings as well and C++11 only adds to this by having std:: array and ranged loops.
How do I best learn to make use of these modern language features and which are suitable for which moments? Is it correct that software engineering in C++ nowadays is mostly free of manual memory management?
Lastly, which compiler should I use to make the most of the new standard? Visual Studio has excellent debugging tools, but even VS2012 seems to have terrible C++11 support.
3
First, some rules of thumb:
-
Use
std::unique_ptr
as a no-overhead smart pointer. You shouldn’t need to bother with raw pointers all that often.std::shared_ptr
is likewise unnecessary in most cases. A desire for shared ownership often betrays a lack of thought about ownership in the first place. -
Use
std::array
for static-length arrays andstd::vector
for dynamic. -
Use generic algorithms extensively, in particular:
<algorithm>
<numeric>
<iterator>
<functional>
-
Use
auto
anddecltype()
wherever they benefit readability. In particular, when you want to declare a thing, but of a type that you don’t care about such as an iterator or complex template type, useauto
. When you want to declare a thing in terms of the type of another thing, usedecltype()
. -
Make things type-safe when you can. When you have assertions that enforce invariants on a particular kind of thing, that logic can be centralised in a type. And this doesn’t necessarily make for any runtime overhead. It should also go without saying that C-style casts (
(T)x
) should be avoided in favour of the more explicit (and searchable!) C++-style casts (e.g.,static_cast
). -
Finally, know how the rule of three:
- Destructor
- Copy constructor
- Assignment operator
Has become the rule of five with the addition of the move constructor and move assignment operator. And understand rvalue references in general and how to avoid copying.
C++ is a complex language, so it’s difficult to characterise how best to use all of it. But the practices of good C++ development haven’t changed fundamentally with C++11. You should still prefer memory-managed containers over manual memory management—smart pointers make it easy to efficiently do this.
I would say that modern C++ is indeed mostly free of manual memory management—the advantage to C++’s memory model is that it’s deterministic, not that it’s manual. Predictable deallocations make for more predictable performance.
As for a compiler, G++ and Clang are both competitive in terms of C++11 features, and rapidly catching up on their deficiencies. I don’t use Visual Studio, so I can speak neither for nor against it.
Finally, a note about std::for_each
: avoid it in general.
transform
, accumulate
, and erase
–remove_if
are good old functional map
, fold
, and filter
. But for_each
is more general, and therefore less meaningful—it doesn’t express any intent other than looping. Besides that, it’s used in the same situations as range-based for
, and is syntactically heavier, even when used point-free. Consider:
for (const auto i : container)
std::cout << i << 'n';
std::for_each(container.begin(), container.end(), [](int i) {
std::cout << i << 'n';
});
for (const auto i : container)
frobnicate(i);
std::for_each(container.begin(), container.end(), frobnicate);
10
As a starting point:
- Stop using
char*
for strings. Usestd::string
orstd::wstring
and just watch your code get shorter, more readable, and safer - Stop using C-style arrays (things declared with
[ ]
) and usestd::vector
or some other appropriate container class. The nice things aboutstd::vector
are that it knows its own length, it cleans up its contents when it goes out of scope, it is easy to iterate over, and it makes itself bigger when you add more items. But there are other collections that might work even better for your circumstances. - Use
std::unique_ptr
– and learnstd::move
almost immediately. Since this may result in some noncopyable objects, laziness may occasionally send you towardsstd::shared_ptr
– and you may have some genuine use cases forstd::shared_ptr
as well - Use
auto
when declaring iterators and types that depend on earlier declarations (eg earlier you declared a vector of something, now you’re declaring a something, useauto
) - Use algorithms and
for_each
over a “raw for” whenever you can since it spares others from reading your loop carefully to conclude that you’re in fact iterating over the whole collection etc. If your compiler supports “range for” then use it overfor_each
. Learn trivial algorithm calls likeiota
,generate
,accumulate
,find_if
and so on. - Use lambdas – they are the easy way to leverage algorithms. They also open the door to so much more.
Don’t get too worked up about what compiler to use. The “terrible, awful” lack of C++ 11 support in VS2012 is that there isn’t variadic templates (yeah right, you were just about to use variadic templates) and the {}
initializer isn’t there. I want that too but I’m hardly going to stop using a useful development tool over it.
The second thing to do, after embracing std::
, is to start thinking RAII. Anytime you have
- starting action
- series of actions with something you got from starting action
- cleanup action that needs to happen even in the case of exceptions
Then what you have is a constructor, a number of member functions, and a destructor. Write a class that takes care of that for you. You might not even have to write the ctor and the dtor. Putting a shared_ptr
as a member variable of a class is an example of RAII – you don’t write memory management code, but when your instance goes out of scope, the right things will happen. Expand that thinking to cover things like closing files, releasing handles, locks etc and code will just get simpler and smaller (while eliminating leaks) before your eyes.
If you’re feeling really confident, purge printf
in favour of cout
, get rid of macros (#define
stuff), and start learn some “advanced idioms” like PIMPL. I have a whole course on this at Pluralsight which you can probably watch using their free trial.
3
How do I best learn to make use of these modern language features and which are suitable for which moments?
By programming. Experience is the best way to learn.
C++11 has lots of new features (auto, rvalue, new smart pointers – just to name few). The best start is to just start using them, and read about them whenever you can, and whenever you find an interesting article.
Is it correct that software engineering in C++ nowadays is mostly free of manual memory management?
That depends what you need to do. Most applications can get away with smart pointers, and forget about memory management. There are still applications that can not get away so easily (for example if they need placement new, or a custom memory allocator for whatever reason).
If you need to use Qt, you’ll have to use their rules for memory management.
which compiler should I use to make the most of the new standard?
Whatever you have at hand that supports the latest standard :
- gcc has good support for c++11 features
- vs has list of features
but no compiler supports all features.
My university is still using C++ for teaching. I have programmed with C++ for 5 years and now I am a graduate student. Of course, now I am using a lot Java, Ruby etc.
I really recommend you don’t too hurry about those features in language like Java. In my experience and opinion, after the low level features of C++. You should look into topics like generic programming with C/C++ like making template class, template functions, operator overritten, virtual methods, smart pointers.
For the Object Oriented Design Part, there are lots of features C++ have and Java does not, like multi-inheritance. Inheritance is powerful but dangerous too. The implementation level of object oriented design in C++ is a good topic too.
Inter-process communication, threadings, are important in C++ too.
For the compiler and debugger. I know visual studio is awesome. But I really recommend you learn GDB, Valgrind, and Make still, and be good at these tools. Microsoft is awesome, but it did too many things for you. As a student, you really need to learn those things which Microsoft did too you. For the compiler, G++ is good from GNU.
After all, after so many years, I really feel, the most important things are the low level features like raw array. Vector is really just a dynamic array.
These are my recommendations, something maybe too subjective, just take what you think is right.
1