views:

1858

answers:

13

What are some C++ related idioms, misconceptions, and gotchas that you've learnt from experience?

An example:

class A
{
  public: 
  char s[1024];
  char *p;

  A::A()
  {
    p = s;
  }

  void changeS() const
  {
    p[0] = 'a';
  }

};

Even know changeS is a const member function, it is changing the value of the object. So a const member function only means that it will treat all variables as const, and it does not mean that it will actually keep all members const. (why? the const keyword on the member function treats char *p; as char * const p; And not as const char *p;

Which therefore means that p can't point to something else. And not that you can't change p's data.

+15  A: 

I've liked this since the time i've discovered it in some code:

assert(condition || !"Something has gone wrong!");

or if you don't have a condition at hand, you can just do

assert(!"Something has gone wrong!");

The following is attributed to @Josh (see comments). It uses the comma operator instead:

assert(("Something has gone wrong!", condition));
Johannes Schaub - litb
Hugo
I use a comma: assert(("Something has gone wrong!", condition));
Eclipse
Josh, ah that's sweet too!
Johannes Schaub - litb
Cute, also like Josh's and Hugo's ways.
j_random_hacker
+4  A: 

A few things that usually trip people up:

std::cout << a << a++ << --a;
i = ++i;

The above lines are both undefined.

void foo(bar* b1, bar* b2);

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}

The above may leak memory.

int* arr = new int[10];
arr + 11;

This results in undefined behavior.

As for idioms, my favorite is RAII. Allocate objects on the stack, which guarantees that the destructor is called when the object goes out of scope, preventing resource leaks.

jalf
You need to explain why the call to foo() may leak. It's not obvious to the uninitiated.
Martin York
Are you sure arr+11 leads to undefined behavior? I was under the impression that this only happened when you tried to de-reference the illegal pointer.
Martin York
Martin: Imagine this order of execution: new bar(), new bar(), shared_ptr(), shared_ptr(). If there is an acception during the construction of the second bar object, the first bar object isn't freed.
Leon Timmermans
Martin: Yep, even creating the pointer is undefined behavior, whether or not it is dereferenced. I'm not sure if it makes a practical difference anywhere, but I suspect it's to avoid accidental overflows.
jalf
To be clear: `arr + 10` is well-defined and legal, but cannot be dereferenced; `arr + 11` is undefined.
Michael Burr
Interesting, I didn't know about arr + 11 being undefined.
j_random_hacker
+51  A: 

You don't need to know C++'s complicated function typedef declaration syntax. Here's a cute trick I found.

Quick, describe this typedef:

typedef C &(__cdecl C::* const CB )(const C &) const;

Easy! CB is a pointer to a member function of class C accepting a const reference to a C object and returning a non-const reference to a C object. Oh, and it’s a const member function. Oh, and the function pointer itself is const… (Right?)

The C++ function declaration specification syntax is notoriously obtuse and hard to remember. Yes, there are tricks seasoned C++ veterans may use to decipher such horrors, but that’s not what this tip is about. This tip is about how you don’t need to remember this horrible syntax and still be able to declare such function pointer typedefs (e.g. in case you’re interacting with some legacy API that never heard of boost::function). Instead of breaking a mental sweat, let the compiler do the work for you. Next time you’re trying to create a typedef to a member function that looks like this:

struct C {
     const C& Callback(const C&) const  { }
};

Instead of struggling to manually come up with the complex syntax above, induce an intentional compilation error which will force the compiler to name the beast.

For example:

char c = &C::Callback;

The compiler happily spews this helpful error message:

“… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'”

Which is what we’re looking for. :)

Assaf Lavie
Gosh, THAT is great! Thanks! :)
Wow, I love this.
Flame
Very nice. Too bad it doesn't also work the other way round!
MiniQuark
Brilliant! It never occurred to me.
greyfade
Linked from http://ridiculousfish.com/blog/archives/2009/11/12/cdecl/
+11  A: 

Never waste time on trying to implement the copy operations on classes when we don't know if it will be required later. Many objects we handle are just entities, and copying them hardly make any sense. Make them non-copyable, and implement the copy/duplication later if the need really arises.

Luc Hermitte
Inheriting (privately) from an `uncopyable` class as suggested by Scott Meyers in "Effective C++", 3rd Ed. - Item 6 is a nice, easy way to do this.
Michael Burr
As is inheriting privately from boost::noncopyable.
CesarB
If you do implement copying sooner rather than later, you only end up constantly having to remember to ammend the copy code as you update the class!
xan
For completeness, please mention the techniques for achieving non-copyability (e.g. declaring the copy ctor private and not implementing it).
j_random_hacker
+7  A: 

One seldom used, but handy C++ idiom is the use of the ?: operator during the constructor chain.

class Sample
{  
    const char * ptr;
    const bool  freeable;

    Sample(const char * optional):
        ptr( optional ? optional : new char [32]),
        freeable( optional ? false : true ) {}
    ~Sample( )  { if (freeable) delete[] ptr; }
}

C++ doesn't allow const values to be changed inside the body of the constructor, so this avoids const-casts.

Procedural Throwback
`freeable(optional == 0)`
avakar
+2  A: 

If you have a class which does not have value semantics, make sure that all of the following constructs are explicitly declared in order to prevent headaches down the road.

  • Default Constructor
  • Copy Constructor
  • Assignment Operator

In many cases you only need to declare a subset of these constructs. However it can get really tricky in some cases as to which are needed and which are not. It's much safer to declare all 3 private and be done with the matter.

It's also very helpful to add a comment to the top explaining that this is not a copy safe class.

This will save you time down the road.

JaredPar
What about "address of" operator whiich is the last part of the cononical set of c++ operators.
Rob Wells
JaredPar
+14  A: 

Here is another one i caught some day:

char int2hex(int x) {
     return "-0123456789abcdef"[(x >= 0 && x < 16) ? (x + 1) : 0];
}

it's just indexing the char array instead of doing a switch. If it's outside the range, it returns '-'.

Johannes Schaub - litb
Wow! Didn't even know that you coud use [] with "" cool :)
AntonioCS
I would have shoved the '-' at the end and ended in ... ? x : 16];
Flame
originally they just did "0123456789abcdef"[x]. i didnt want ppl think i encourage buffer overflows :p
Johannes Schaub - litb
definitely, a cool trick.
Comptrol
j_random_hacker
+4  A: 

Since we're all ignoring the OP and instead posting our favourite cool tricks...

Use boost (or tr1) shared_ptr to maintain a class invariant at runtime (kind of obvious, but I haven't seen anyone else do it):

#include <cassert>
#include <functional>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;

class Foo
{
public:
    Foo() : even(0)
    {
        // Check on start up...
        Invariant();
    }

    void BrokenFunc()
    {
        // ...and on exit from public non-const member functions.
        // Any more is wasteful.
        shared_ptr<Foo> checker(this, mem_fun(&Foo::Invariant));

        even += 1;
        throw runtime_error("didn't expect this!");
        even += 1;
    }

private:
    void Invariant() { assert(even % 2 == 0); }
    int even;
};
fizzer
That's a good way to terminate your program immediately. Throwing an exception while another exception is active will execute terminate().
Jere.Jones
wtf are you talking about?
fizzer
Interesting. I guess I would have declared a friend subclass called "Invariant" with checks in the ctor and dtor, and just declared an instance of that on the stack at the start of each public member function. Your code is slightly shorter, but it only checks at function exit.
j_random_hacker
That's intentional. You don't need to check on entry, because the invariant held on the last exit.
fizzer
I see. Well, you may want to check out Loki's ScopeGuard, which does much the same thing but without the dynamic memory allocation done by shared_ptr: you simply call "ON_BLOCK_EXIT(" Link: http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer
j_random_hacker
A: 

I can't say that I am an experienced C++ programmer but I have recently learned how hard it is to pass and array of arrays as a function parameter. Try to avoid this at all cost :(

If you know the size at compile its simple. Even if you know one of the dimensions at compile time. If you simply don't know... you might be looking at something like this

m[i*dim2+j]

Being i the iterator for the rows, dim2 the number of cols and j the iterator for the cols.

AntonioCS
You must know all except one of the sizes... In any case, use std::vector. We're talking C++ here, not C.
Arafangion
+3  A: 

Since I learned about RAII (one of the worst acronyms ever) and smart pointer, memory and resource leaks have almost completely disappeared.

Ferruccio
Another name i heard for this in some irc channel is SBRM (Scope Bound Resource Management) which is a better name for this.
Johannes Schaub - litb
SBRM sounds like an acronym for some sex act... no thanks
Assaf Lavie
+3  A: 

You can often hide way more stuff in source files than you think. Don't make everything private if you don't have to - it's often better to leave it in an anonymous namespace in the source file. It actually makes things easier to process, I find, because then you aren't revealing implementation details, yet get inspired to make lots of tiny functions rather than monolithic ones.

coppro
+6  A: 

Sometimes, headers are polluted with not behaving macro names like

#define max(a, b) (a > b ? a : b)

Which will render code invalid that uses a max function or function object called that way. An infamous example is windows.h which does exactly that. One way around it is putting parentheses around the call, which stops it from using the macro and makes it use the real max function:

void myfunction() {
    ....
    (max)(c, d);
}

Now, the max is in parentheses and it is not counted as a call to the macro anymore!

Johannes Schaub - litb
Shit. (And why does windows.h need to define this anyway?)
Arafangion
A: 

Nothing I actually came across while programming, but a friend wanted an explanation on why the code worked. It took me a while to figure it out. Maybe it is obvious to you guys, but I am not an experienced programmer.

#include <iostream>
using namespace std;

int& strangeFunction(int& x){return x;}


int main(){
        int a=0;
        strangeFunction(a) = 5;               //<------- I found this very confusing
        cout << a <<endl;
        return 0;
}
Lucas
Very common in C++, and is the normal idiom for many of the standard template library classes.
Arafangion
Daniel Earwicker