Welcome, Guest: Register On Nairaland / LOGIN! / Trending / Recent / New
Stats: 3,154,762 members, 7,824,186 topics. Date: Saturday, 11 May 2024 at 03:28 AM

Common C++ Gotchas - Programming - Nairaland

Nairaland Forum / Science/Technology / Programming / Common C++ Gotchas (698 Views)

Common Misconceptions About Programmers / Nairaland Programmers That Share Language And City In Common / Basic, Fortran And Cobol Are High Level Language But Why Are The Compiler Not Common? (2) (3) (4)

(1) (Reply)

Common C++ Gotchas by naijatechworld: 8:29pm On Nov 08, 2014
Hopping between my open-source Android projects and Photoshop Elements at Adobe involves a lot of mental context-switching. A majority of that transition is necessitated by the fact that Java and C++ share similar syntax and semantics but differ in countless ways, often subtly. I often catch myself "thinking in Java" when writing C++, or the other way around, when I'm in the midst of said context-switch.

The other thing to consider is: C++ is a double-edged sword. It's powerful, but that also makes it very easy to make mistakes.

So I thought I'd compile a list of common C++ pitfalls / things to remember / things I find interesting, with references to more detailed discussions. I expect to often refer to this post myself. The format is somewhat terse, so beginners might find it confusing.

1. delete heap memory
I know you don't need me to tell you this. But sometimes I forget to call delete (or delete[]), so this item is basically a reminder for myself. It doesn't help that accidentally calling vanilla delete on new[]'ed memory causes undefined behaviour, potentially including subtle memory leaks.

2. never throw from a destructor
Yes, I hear you: "But what if my destructor fails?". Simmer down, whippersnapper. I'm calling in the cavalry on this one. Here's Marshall Cline's suggestion (Cline is author of the treasured C++ FAQ):

Write a message to a log-file. Or call Aunt Tilda. But do not throw an exception!
You at least trust him, if not me, right? You should. Here's the reason: when an exception is being processed, the program begins moving up the call stack in search of a catch block, popping off stack frames and destroying all local objects in them. Guess what happens if the destructor of one those objects throws an exception? Now there are two uncaught exceptions to deal with, and no catch in sight! At this point the program simply explodes in your face, as the realization dawns that you've committed thrown one sin exception too many in the Holy Land of C++.

3. declare base class destructors as virtual
class A {
~A() { ... }
};

class B : public A {
~B() { ... }
};

A *a = new B();
delete a; // what happens here?
The call to delete at the end will, in most cases, call A::~A(), resulting in a hard-to-detect memory leak. The standard's stance on this issue is "undefined behaviour". Making A's destructor virtual resolves the leak.

4. always catch references, not values or pointers
Spot the best code:

// option 1
try { ... }
catch (exception e) { ... }

// option 2
try { ... }
catch (exception& e) { ... }

// option 3
try { ... }
catch (exception* e) { ... }
Yes, yes, it's option 2. But why?

Option 1 is bad because: (a) it might involve an unnecessary copy constructor invocation, and (b) it can cause a subtle bug, thanks to slicing! For example, if the try block throws an instance of type derived_exception, derived from the type exception (duh), then the latter's copy constructor gets called, resulting in a hacked-off instance of the former getting sliced into the latter.

Option 3 is bad simply because naked pointers need to be manually de-allocated. This is especially bad considering the fact that when I'm writing a catch block, I already have a lot to think about: how to recover from the massive shit-storm I just got into, whether I can punt responsibility up the call stack, and so on. So no naked pointers to exceptions, thank you very much.

5. char const* const buffer
const follows a rather simple rule: it acts on whatever precedes it. So:

char const* buffer; // pointer to const char (1)
char* const buffer; // const pointer to char (2)
char const* const buffer; // const pointer to const char (3)
Much of the confusion arises because many (including me) tend to prefer placing the const before the char in the first case:

const char* buffer; // pointer to const char, same as (1)
If you'll tolerate my hypocrisy for a moment, here's my suggestion: try to avoid putting the const at the beginning like that. Another source of confusion is array declarations with const:

int main(int argc, char* const* argv); // pointer to const pointer to char
int main(int argc, char* const argv[]); // equivalent
6. copy constructor signature
What's the right signature for a copy constructor?

A::A(A) - infinite recursion
A::A(A&wink - valid but won't work if the argument is a temporary, e.g., A fn() { A a1; return a1; }; A a2 = fn(); // error
A::A(const A&wink - valid and works with temporaries (see item #cool
7. return value of operator=
Consider the overridden assignment operator for class A (our favourite class):

A::operator=(const A& other) { // guess the return type?
// assign other to this
// ...

return ; // what to return here?
}
The standard says the return type of operator= must be A& and the return value "a reference to the left hand operand", i.e., *this. Without a satisfactory reason, that's just begging to be forgotten. Here's your reason: it's so that chained assignments like a = b = c work correctly (think of it as a.operator=(b.operator=(c))).

8. temporary objects can only bind to const references
A getA() { return A(); }
const A &a = getA(); // ok
A &a = getA(); // error
You cannot assign temporaries to non-const references. You knew that right? Yes, I'm looking at you, Visual Studio 2010 user. Your compiler doesn't even emit a measly warning for decidedly non-standard behaviour. Clang and GCC are kind enough to spit out descriptive compilation errors. This has bitten me more than once, when writing cross-platform code in Visual Studio.

9. the parentheses in new A() matter more than you think they do
class A { int n; };
A a1 = new A();
A a2 = new A;
What do you think are the values of a1.n and a2.n right after construction? Answer: 0 and {insert garbage value here}. More generally, plain old datatypes are zero-initialized in the former case, but behaviour in the latter case is implementation-defined.

10. dynamic_cast works only with runtime polymorphic types
Sure, your compiler helps you out with this. But this is very easy to forget, say, in an interview. So have you thought about why at least one virtual function is needed for using dynamic_cast? Simply because it's convenient to implement dynamic_cast by piggy-backing on the virtual method infrastructure: the type hierarchy information (conventionally called "run-time type information" or "RTTI"wink is usually attached to the vtable, and a run-time check is performed, using available RTTI, to determine whether the cast is valid.

Original Post - http://vickychijwani.me/cpp-gotchas/
Re: Common C++ Gotchas by RashEngine: 12:48am On Nov 09, 2014
I can write codes, but I don't know how to solve problems with them. in other words, how to make software. please help cuz I'm having same feeling

(1) (Reply)

Whizapp Is Here! / Library Codes 2015 For VB.NET Programming Language / Is The Programming Challenge Series Dead ?

(Go Up)

Sections: politics (1) business autos (1) jobs (1) career education (1) romance computers phones travel sports fashion health
religion celebs tv-movies music-radio literature webmasters programming techmarket

Links: (1) (2) (3) (4) (5) (6) (7) (8) (9) (10)

Nairaland - Copyright © 2005 - 2024 Oluwaseun Osewa. All rights reserved. See How To Advertise. 41
Disclaimer: Every Nairaland member is solely responsible for anything that he/she posts or uploads on Nairaland.