C++ Programming

Index

If you notice any mistakes or have additions, click here.

C++ Tricks



// To turn a fundamental type into a (somewhat) distinct type.
#define DECLARE_DISTINCT_TYPE( CLASS, T )                           \
class CLASS                                                         \
{                                                                   \
public:                                                             \
    CLASS( T val ) : mVal(val) { }                                  \
    operator T() const { return mVal; }                             \
    CLASS& operator=( const T& val ) { mVal = val; return *this; }  \
private:                                                            \
    T mVal;                                                         \
};
DECLARE_DISTINCT_TYPE( Radian, float )
DECLARE_DISTINCT_TYPE( Degree, float )
void Rotate( Radian rad );  // these overloaded functions become possible
void Rotate( Degree deg );  // yet can be manipulated as floats

STL Code Snippets



// Vector:
vector vec;
vec.push_back( name );      // push_back() means append
vec.clear();

// Iterating thru a container:
vector files;
vector::iterator itr;
for ( itr = files.begin(); itr != files.end(); ++itr )
    cout << *itr << endl;

// Iterating by subscript using an index (N/A to associative containers):
vector vec;
for ( int i = 0; i < vec.size(); ++i )

// Building and iterating thru a map and using pairs:
map mp;
mp.insert( pair( "a", 1 ) );
mp.insert( pair( "b", 2 ) );
mp.insert( pair( "c", 3 ) );
map::iterator itr;
for ( itr = mp.begin(); itr != mp.end(); ++itr )
    cout << itr->first << " " << itr->second << endl;

// To test if a key exists in a map/set:
if ( mMap.find(key) != mMap.end() )

// Adding then removing from a container.
list l;
for ( int i = 0; i < 10; ++i )
    l.push_back( i );  // append
while ( ! l.empty() )
{   // Prints/pops oldest (head) element first,
    cout << l.front() << endl;
    l.pop_front();
}

// If no match.
if ( map.find("key") == map.end() ) cout << "not found" << endl;

// Print a container.
list con;
copy( con.begin(), con.end(), ostream_iterator(cout," ") );

// Copy a container:
copy( con1.begin(), con1.end(),
      con2 );                // WRONG/PITFALL if con2 smaller than con1

con2.clear();
copy( con1.begin(), con1.end(),
      back_inserter(con2) ); // OK if con2 smaller than con1

// Sorting using a binary predicate:
// An alternative (using the container container pointers instead
// of values) that doesn't need a binary predicate is to define
// your own operator<() which sort() uses by default.
bool BinPred( const Class& o1, const Class& o2 )
{
    return o1.val < o2.val;
}
vector vec;
sort( vec.begin(), vec.end(), BinPred );

// A function that returns a pair.
// The pair is returned by value (like a struct would be, so it isn't dangling).
std::pair
AddMul( float x, float y )
{
    std::pair res;
    res.first  = x + y;
    res.second = x * y;
    return res;
}

// Print a binary number in base 2 using bitset.
#include 
bitset<8> val = 0xff;
cout << val;

// Turn string to all upper-case.
string s;
transform( s.begin(), s.end(),
           s.end(),
           toupper );

// Distance between two iterators.
distance( itr1, itr2 )

// Getting the iterator of an item that was inserted into a map:
// (a multimap/multiset differs, adapted from gfx_share.hh)
std::pair insertion;
insertion = mMap.insert( std::make_pair( *obj, std::make_pair(copy,1) ) );
itr = insertion.first;  // insertion is assumed to succeed (bool not checked)

// Replacing/substituting chars of a string.
// string::replace() is really an overwrite, not a substitution.
char
Unix2DosDirChar( char c )
{
    return c == '/' ? '\': c;
}
string
Unix2DosDir( const string& dirname )
{
    string s = dirname;
    std::transform( s.begin(), s.end(),
                    s.begin(),
                    Unix2DosDirChar );
    return s;
}

Stream Code Snippets



// Save/restore stream flags.
std::ios::fmtflags savedFlags = cout.flags();
...
cout.flags(savedFlags);

// showbase (eg automatically print "0x" prefix for hex, etc).
cout.setf( std::ios::showbase );

// Set float precision.
mStream.setf( ios::fixed, ios::floatfield );
mStream.precision( 20 );

// Writing to an integer to a stream using width/precision.
// Note that width/precision is discarded after every call to stream object!
std::cout << std::setw(5) << std::setfill('0') << x << ' ';
std::cout << std::setw(5) << std::setfill('0') << y << ' ';
std::cout << std::setw(5) << std::setfill('0') << z << ' ' << std::endl;

// Convert an int to a string stream for the purpose
// of passing the int as a C++ string.
int i;
ostringstream ss;
ss << i; Print( ss.str() );

// Open for reading to test if file is empty.
strm.open( fname, ios::in|ios::binary );
if ( strm.good() && strm.is_open() )
{
    // File exists.  Is it empty?
    strm.seekg(1);
    char c; strm >> c;  // a read is required to trigger EOF, seekg(1) alone won't
    if ( strm.eof() )
        cout << "File exists and is empty." << endl;
    else
        cout << "File exists and contains data." << endl;
}
else
{
    cout << "File doesn't exist." << endl;
}

// Reopen in R/W mode.
strm.close();
strm.clear();
strm.open( fname, ios::in|ios::out|ios::trunc|ios::binary );
strm.seekp(0);

Redirecting Streams

To write a class that redirects a stream to something else, define your own streambuf class, then construct an ostream with pointer to the streambuf object. An example is in Nicolais Josuttis's STL book.

A faux-pas is trying to derive from ostream (see prog/c++/streamRedirectionFauxPas.cc). One problem is that passing endl will result in a "bad cast&qout; exception (g++ 3/4).

http://shekel.jct.ac.il/cc-res/online-doc/libgPP/iostream_28.html
http://www.lysator.liu.se/c/bs-errata-1.html
http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#6



////////////////////////////////////////////////////////////////////////////////
/// @brief NopStream is a indirect way to use a disabled C++ stream.
///
/// This is a "streambuf" class to serve as the basis of an ostream.
/// Derived from Nicolai Josuttis's STL book.
///
/// @verbatim
/// Example:
///     NopStreambuf nopStreamBuf;
///     std::ostream gLog( &gNopStreambuf );
/// @endverbatim
///
class NopStreambuf : public streambuf
{
PREVENT_COPYING(NopStreambuf)
typedef streambuf Parent;
public:
    NopStreambuf( void ) { }
    ~NopStreambuf() { }

protected:
    virtual int_type    overflow( int_type c ) { return ~EOF; }
    std::streamsize     xsputn( const char* buf, std::streamsize n ) { return n; }
};

Defining operator<<()

The idea is to overload operator<<() with your user-defined type.


class Point
{
public:
    int x, y;
};

ostream&
operator<<( ostream& strm, const Point& obj )
{
    strm << "(" << obj.x << "," << obj.y << ")";
    return strm;
}

Overloaded Operators

Overloaded operators, except assignment, are inherited. But subtle compiler errors can occur.

Let's say you have two related classes that are logically different types, but are structurally equivalent (identical members).



class Vertex
{
public:
    Vertex& operator+( const Vertex& src );

    float x, y, z;
};

class WorldVertex : public Vertex
{
public:
};

Now try adding two derived objects:



void Draw( const WorldVertex& v );

WorldVertex v1, v2, v3;
v3 = v1 + v2;      // compile error
Draw( v1 + v2 );   // ok

Some compilers will give obscure errors, leading you to think that you need to duplicate all the overloaded operator code into every derived class. That wouldn't ideal.

What's happening in v3 = v1 + v2 is that Vertex::operator+() is called which returns a base Vertex object, not a derived WorldVertex object. You might think assigning a base object into a derived object is an immediate error. But the C++ compiler first tries to find the matching assignment operator such as Derived& operator=( const Base& ):



class WorldVertex : public Vertex
{
public:

    WorldVertex& operator=( const Vertex& src )
    {
        x = src.x;
        y = src.y;
        z = src.z;
        return *this;
    }
};

Because these classes are structurally equivalent, Derived& operator=( const Base& ) does make sense, as it can simply copy .x, .y, .z.

C++ Pitfalls & Traps

C++ has a zillion pitfalls, this lists some of the worst.

  • Reusing or abusing assignment operator=() in a copy constructor:
  • This pitfall exists with more complex classes whose members aren't fundamental types. Assignment operators should free members before reassigning them. If an operator=() that frees resources is called in a copy constructor, it will try to free garbage. A solution is have separate Copy() and Free() methods.

    
    class Class
    {
    public:
        Class( const Class& src )
        {
           *this = src;      // the temptation to write terse code leads to a pitfall
        }
        Class& operator=( const Class& src )
        {
            delete mObj;     // free members
            mObj = src.Obj;  // reassign members
            return *this;
        }
    private:
        Class2*   mObj;
    };
    

  • Method overriding will fail if you forget to write virtual by a method in base class or the function signatures differ.
  • Calling a virtual method from a base constructor.
  • Think about the order of construction: the derived object hasn't been constructed yet.

  • Default copy constructors or assignment operators may cause trouble.
  • Safegaurds writing a dummy default copy constructor as private and/or with assert(0).

  • A constructor with a single arg might be misinterpreted as a conversion operator.
  • Write explicit if conversion isn't desired.

    
    Class( int );
    int n;
    Class obj = n; // converts an int to a Class obj !!
    
  • Temporary objects and reference args
  • 
    void Byref( int& x )
    {
        ...
        x = y;
    }
    Byref( a + 2 );   // oops, result went nowhere into a temp
    
  • operator bool()
  • operator bool() is seductive for tersely testing if an object is valid:

    
    class Data
    {
    public:
        operator bool() const { return mValid; }
    private:
        string mData;
    };
    
    void Process( Data& data )
    {
        // Valid data?
        if ( data )
        {
        }
    }
    
    Let's say two objects are valid but their values (members) differ.
    
    if ( data0 == data1 ) return;
    
    You'd think return won't happen. But it will. This is what's compiled:
    
    if ( bool(data0) == bool(data1) ) return;  // true == true
    
    The pitfall is implicit conversion. The class doesn't define operator==(). But the compiler doesn't supply a default memberwise comparison as you might assume. Rather, the compiler implicity converts both operands to bools. Because that's precisely what operator bool() is for.

STL Pitfalls & Traps

  • end()
  • 
    for ( itr = files.begin(); itr < files.end(); ++itr )  // WRONG
    for ( itr = files.begin(); itr != files.end(); ++itr ) // ok
    

  • reserve() vs. resize()
  • resize() expands the container -- reserve() doesn't!

  • Incrementing iterators
  • 
    while ( itr++ != files.end() ) // WRONG, itr incremented past end
    

  • Short destination containers
  • This could be a bug if vec2 is shorter than vec.
    copy() won't extend the destination container.

    
      copy( vec.begin(), vec.end(),
            vec2.begin() );
    

    One solution is: vec2.resize( vec1.size() ).
    Another is to use an insert iterator (insertor):

    
      copy( vec.begin(), vec.end(),
            back_inserter(vec2) );
    
  • Catenating a string with a char
  • 
      char baseName[] = "myfile";
      string suffix = "txt";
      string fileName = baseName + '.' + suffix;   // WRONG
    

    The above wrong because the compiler misinterprets this as C's way of adding an integer to a pointer rather than as C++ string catenation. That is, the compiler's intepretation is:

    
      string fileName = &baseName[ int('.') ] + suffix;
    
  • Auto-insertion of missing key into STL map:
  • 
      if ( mMap[key] == needle )
    

    The above is wrong because map::operator[] will automatically insert a missing key!

    
      if ( mMap.find(key) != mMap.end()
        && mMap[key] == needle )
    

C Preprocessor Tricks

  • Macro to expand a unique name:
  • 
    // Arcane indirect trick to give a variable a unique name.
    // Subtlety: If written on the same line, UNIQUE_NAME() expands the same name.
    #define UNIQUE_NAME__(NAME,LINE) NAME##LINE
    #define UNIQUE_NAME_(NAME,LINE)  UNIQUE_NAME__(NAME,LINE)
    #define UNIQUE_NAME(NAME)        UNIQUE_NAME_(NAME,__LINE__)
    

home
© 2009 Jim Brooks
Last modified: Thu May 24 14:11:14 EDT 2007