

Common C++ Gotchas - vickychijwani
http://vickychijwani.me/cpp-gotchas/

======
nly
#3 should really say make _public_ base class destructors virtual

#6 sometimes you actually will want an A(A&) constructor... for example, if
you have constructor overloaded with a greedy template argument.

#8 objects returned by value will also bind to an rvalue reference

#9 Remember in C++11 there's also new T{};

#10 isn't absolutely correct either, dynamic_cast can actually be used in some
circumstances where static_cast or implicit casting could have been used.

    
    
        struct A {
        };
    
        struct B: A {
        };
    
        int main() {
            auto b = new B();
            auto a = dynamic_cast<A*>(b); // fine
        }

~~~
Suncho
#3 should really say "make polymorphic public base class destructors virtual."

There are reasons why you might want to use public inheritance that have
nothing to do with run-time polymorphism.

~~~
jfoutz
I haven't touched C++ in years. Why would you ever make a non-virtual
destructor? Isn't it totally up to the user of a class if they choose to
inherit from the class?

It seems like, if you're not providing a virtual destructor, you force users
into delegation rather than inheritance, and that will catch some % of
developers who aren't on the ball. They'll have leaks...

As far as i know (old info) there's no such thing as a "final" class, so
anybody can inherit even if you don't want them to.

~~~
flebron
Having any virtual member functions, unless the compiler can devirtualize it,
will hurt both space usage (since you now need a per-object vtable) and cache
performance (since destruction now has to go through an indirection every
time, instead of being a globally visible function).

You have the `final` specifier since C++11 to state a class cannot be
inherited from, but this notion of "You really shouldn't inherit from this
class." has been around since classical object orientation has, C++ just
couldn't express it as a language.

So if your class currently isn't being inherited from, make your public dtor
nonvirtual. If and when it needs to be a base class, then the author can
modify your class to have a virtual dtor, or use composition (which is often
the superior alternative). If source code is not available, then composition
it is. I would advise against making it virtual "just in case", that's
premature pessimisation.

~~~
wfunction
> per-object vtable

You mean per-object vtable _pointer_? Or per- _type_ vtable?

~~~
flebron
Per object vtable pointer, yes.

------
wfunction
Why are you using delete so often in C++...? 'delete' should be rare in your
code.

~~~
Aldo_MX
Why shouldn't you?

You don't always have control over the code you are writing, an example that
comes to my mind quickly is ffmpeg.

~~~
vinkelhake
How does ffmpeg relate to this, isn't it written in C?

~~~
shadowfox
He may be talking about interfacing with C code of that nature. (You can still
localize most of your new/deletes in to the wrapper classes though)

------
kazinator
It's not only C++ that has problems with throws out of destructors or similar
logic. How about Common Lisp:

[http://clhs.lisp.se/Issues/iss152_w.htm](http://clhs.lisp.se/Issues/iss152_w.htm)

"The ambiguity at issue arises for the case where there are transfers of
control from the cleanup clauses of an UNWIND-PROTECT. ..."

It is not clear what exit points are visible when the cleanup-forms (morally
similar to C++ destructor calls) are invoked. Are the exit points that are
skipped torn down all the way to the control transfer's target, or is the
tear-down interleaved with the unwinding.

~~~
blt
The real answer, as far as I can tell, is "Don't use exceptions in a program
that's too important to crash."

~~~
kazinator
Right, and to that end, don't use a CPU that traps illegal instructions,
divisions by zero, pointers to unmapped pages and so on. Those are all forms
of exceptions.

------
snarfy
I wrote C++ code for about 10 years before moving over to C#. I still like
C++, but posts like this make me remember why I don't miss it. It's so
baroque.

------
Suncho
#1 should really say, "Avoid new and delete." new and delete are for experts.
The average C++ programmer should go nowhere near them. If heap memory is used
internally by a class that was written by someone who knew what they were
doing, then fine. Use your std::vector. But new and delete are huge red flags
in code.

~~~
TillE
It's fine to use new as long as you're immediately sticking the result in a
smart pointer. But yes, delete should be very very rare in modern C++ code.

~~~
hendzen
With std::make_shared, and the addition of std::make_unique in C++14, you
shouldn't ever need to use new for this purpose either.

~~~
Suncho
It depends. The trick to using new correctly is to put it in a function whose
sole purpose is to immediately store new's result in the smart handle. I.E.
You're writing a function that's analogous to the make_unique function.

You might need to do this if you're implementing your own smart handle or
writing a factory function for a class with a private constructor (make_unique
will not work).

Off the top of my head, I can't think of any good reasons to use delete.

~~~
lomnakkus
If your handles are pointer types, you should _still_ use unique_ptr, you just
need to give it a custom "deleter". (Of course you should probably still
encapsulate this in a function like make_xxx() just to avoid the potential
mistake of not supplying the correct deleter everywhere.)

------
Arelius
#5

> If you'll tolerate my hypocrisy for a moment, here's my suggestion: try to
> avoid putting the const at the beginning like that

Or you could not do that, and put const in the least ambiguous place, and
since you rarely have a 'type* const _' it works very well just using:

    
    
        const type*
    

for pointers to const types, and

    
    
        type* const
    

for const pointers to types, and entirely unambiguous.

------
santaclaus
> Option 1 is bad because: (a) it involves an unnecessary copy constructor
> invocation

Shouldn't rvalue move semantics kick in and prevent the copy?

~~~
vickychijwani
Agreed. I'll add a note. There's still the slicing though.

~~~
nostrademons
For slicing, the general rule in C++ is _whenever you have a value object,
polymorphism does not apply_. A value object is always whatever its compile-
time type is - after all, it's just a value, it can't represent some other
"thing" that's off in some other memory space. Polymorphism only applies with
pointers and references.

Remember this rule and the throws behavior makes perfect sense, as do a lot of
other C++ gotchas.

------
mpyne
I would actually revise #1 to be to always use a smart pointer for heap-
allocated objects. C++11 is 3 years old now after all.

------
plicense
Selection messed up in your website:
[http://imgur.com/YuowMEm](http://imgur.com/YuowMEm)

