Different people have different ideas of what is simple.

— Bjarne Stroustrup

Interesting Things And Helpful Resources

Namespace

The following line is an example of how kludgey C++ can be:

using namespace std;

I find this command to be awkward. It is cryptic, not part of your train of thought when trying to get something to work and, unfortunately, hardly ever unnecessary. This line simply must be included to activate the correct name space for certain important libraries. There may be others but <iostream> and <string> are definitely some that need this command.

Although this is an annoying requirement because most of the time it is used mechanically in the case shown above, the namespace mechanism is a C++ feature that allows better organization and protection of program elements.

Comment Styling

In C, the comments are this bizarre syntax:

/* This is a classic C style comment. */

In C++ that is acceptable but there is another style that has a starting designator and runs to the end of the line. Like this:

// This is a C++ comment.
x++ // The beginning of this line is not a comment but this is.

For style tips in general, you could do worse than to emulate these guys.

Variable Types

As with C, all variables must be explicitly declared before using. The point of this relates to C and C++ and their very explicit use of memory management.

Note
Actually with the Cpp11 auto keyword (not to be confused with the K&R C auto compiler hinting) in some instances, the types of variables can be automatically deduced from context. Some more information and examples.

bool Exclusive to C++!

Boolean

1byte

keywords true or false, or integers 1 or 0

char

Character or small integer.

1byte

signed: -128 to 127 unsigned: 0 to 255

short int (short)

Short Integer.

2bytes

signed: -32768 to 32767 unsigned: 0 to 65535

int

Integer.

4bytes

signed: -2147483648 to 2147483647 unsigned: 0 to 4294967295

long int (long)

Long integer.

4bytes

signed: -2147483648 to 2147483647 unsigned: 0 to 4294967295

float

Floating point number.

4bytes

+/- 3.4e +/- 38 (\~7 digits)

double

Double precision float

8bytes

+/- 1.7e +/- 308 (\~15 digits)

long double

Long double float.

8bytes

+/- 1.7e +/- 308 (\~15 digits)

wchar_t

Wide character.

2 / 4 bytes

1 wide character

Also C enum values are always sized int whereas in C++ they are a distinct type and can have different sized values.

C++ also provides for a const qualifier. This is simple in the simple cases. Basically it makes a variable immutable. But there is all kinds of mischief that can arise from this. The most absurd to me is the mutable keyword which can make const declarations, uh, less const.

Note that C++ is more strict about type casting that C.

Variable definitions in C generally must be at the beginning of a block whereas in C++ they can be anywhere in the block.

C++ provides a way to get a runtime check of what kind of type a variable is. This is done with the typedef function and is useful when variables can be any of several types or objects.

Strings

Because memory management is such a focal point of C programming, only char which provides a single non-numeric value is really practical without going into a more elaborate mechanism. In C++ the string class is that more elaborate mechanism. Strings are generally used like this:

#include <string>
using namespace std;
int main () {
  string thestr("Initialization like an object.");
  thestr= "Assignment like a variable.";
  return 0;
}

Although this example doesn’t do anything, it doesn’t produce an error. Note that because it is a class (string) it can be initialized like a new object as an initialization function parameter. Subsequent redefinitions use the overloaded = operator to simulate the way normal people would expect a programming language to work.

Input And Output

C uses scanf and printf for input and output. C++ can use these but idiomatic C++ programs use a different mechanism not available in C.

cin>>
cout<<

These are available from include #iostream. You probably also want to turn on using namespace std; to cut down on trivial scope chatter. These are really not exactly functions but more like named pipes (in the std namespace) which get data sent in and out of them (<< and >> are overloaded operators normally used for bit shifting).

The >> operator is the "extraction" operator and reads input "items" which are expected to be delimited by whitespace (space, newline, tab).

if (fileStream.is_open())
    while (fileStream.good()) {
        string word;
        fileStream >> word;
        // Do something with "word"
        }

Here is a complete output ("insertion" operator) example:

#include <string>
#include <iostream>
using namespace std;
int main () {
  string thestr("String objects can be output like so.");
  cout << thestr << endl;
  return 0;
}

Here are some good notes on C++ IO.

Memory

Some C++ specific keywords, new and delete, help with memory allocation and deallocation issues.

Functions

In C it is good practice to prototype functions (describe their input and output before using them).

Unlike C, in C++ functions can be overloaded. This means that their meaning can be redefined.

Also C++ can use functions inside of structures. This is not common but it hints at the more C++ way of organizing functionality, classes, discussed in the next section.

Unlike C, C++ allows the program to define default values for function arguments.

Inline Functions

C++ discourages the use of preprocessor complexity. Instead, the idiomatic C++ way to do preprocessor functions is with the keyword inline.

References

References are a feature specific to C++ not found in C. Although the subtleties get complex, the greatest utility of references is simply as a way to pass by reference to functions easier. You can still move things around by pointers if you want but references often allow function calls to be less complicated.

  • In Unix terms, I think of pointers as symlinks and references as hard links. The reference is the object accessed differently, not a pointer to an address that may not contain anything.

  • You can also think of a reference as a constant pointer.

  • Pointers are variables that contain memory addresses while references are additional definitions of compiler tracked objects. (The compiler thinks: "Ah, he’s calling x another thing, y, now too.") Of course the compiler can use the same machinery it uses for pointers for this but references can be implemented more efficiently. That’s up to the compiler.

  • References point to objects (whose address a reference shares), while pointers can point to other pointers, ad infinitum.

  • References can (should) not normally be NULL.

  • References can not iterate using pointer arithmetic.

  • Pointers need to be dereferenced with *, or -> for class members, while references can be dereferenced with . like the referent ("automatic indirection").

Simple Demonstration Of References
#include <iostream>
using namespace std;

// Function with reference parameters.
void one2theother(int &from, int &to) { from--; to++; }
// Function with reference parameters returning a reference.
int &add2together(int &a, int &b) {
    int s; int &r2s=s; r2s= a+b; return r2s; }

int main () {
    int var= 10; int var2= 99;
    int &ref2var= var;
    cout << var << endl      // 10
       << ref2var << endl;   // 10
    ref2var++;
    cout << var << endl      // 11
       << ref2var << endl;   // 11
    var++;
    cout << var << endl      // 12
       << ref2var << endl;   // 12
    one2theother(var,var2);  // Function using references.
    cout << var << endl      // 11
       << ref2var << endl    // 11
       << var2 << endl;      // 100
    cout << add2together(var,var2) << endl; // 111
    return 0;
}

Here is a lot of expertise on the topic for those who really need it.

Classes

C++ features organization by classes.

Table 1. Special Member Functions

Default constructor

X()

Destructor

~X()

Assignment operator

void operator=(X &src)

Copy constructor

X(X &src)

Printing function

void print(ostream *os)

Example of Using Classes
// cxe- Wed May 12 12:22:10 PDT 2004
// This is a test of using inheritance to create a containable class (or array
// in this example) of heterogenous objects. The goal will be to store 2
// classes in the same array in such a way that they are usable.

#include <string>
#include <iostream>
using namespace std;

class Number {  // This is the base class.
   public:
      Number() {}
      virtual void show() { cout << "No base class data!" << endl; }
   private:
}; // end class Number

class Roman: public Number {
   public:
      Roman operator=(string p) { pretium= p; return *this; }
      void show() { cout << "Roman:" << pretium << endl; }
   private:
      string pretium;
}; // end class Roman

class Arabic: public Number {
   public:
      Arabic operator=(int v) { value= v; return *this; }
      void show() { cout << "Arabic:" << value << endl; }
   private:
      int value;
}; // end class Arabic

int main() {
   Number z; //can't do anything with z
   Arabic a; a= 19;
   Roman  r; r= "XIX";
   cout << "Objects as themselves, no container ----------" << endl;
   z.show(); a.show(); r.show();

   Number n[3]; // Array of base class objects
   n[0]= z; n[1]= a; n[2]= r;
   cout << "Objects in container of baseclass ----------" << endl;
   n[0].show(); n[1].show(); n[2].show();

   Number *p_n[3]; // Array of pointers to Numbers
   p_n[0]= &z; p_n[1]= &a; p_n[2]= &r;
   cout << "Objects in container of baseclass pointers ----------" << endl;
   p_n[0]->show(); p_n[1]->show(); p_n[2]->show();

   return 0;
} // end main

This outputs the following.

Objects as themselves, no container ----------
No base class data!
Arabic:19
Roman:XIX
Objects in container of baseclass ----------
No base class data!
No base class data!
No base class data!
Objects in container of baseclass pointers ----------
No base class data!
Arabic:19
Roman:XIX

virtual

The virtual keyword is confusingly used for different purposes depending on context. Here are two of the uses. There may be more.

  1. A function declared virtual means that an existing overriding function in a derived class is invoked.

  2. An inheritance relationship declared using keyword virtual between classes Derived1 and Derived2 that inherits from class Base means that another class Derived3 that inherits from Derived1 and Derived2 still results in the creation of only one instance of Base during instantiation of type Derived3.

Member Initialization Lists

In class definitions the constructor can have a list of members with their correct initial values defined. These are placed after the constructor name and parameters, followed by a : and before the body of the constructor. The following are very similar. The first is preferred since it avoids unnecessary copies.

MyClass(int some_init_args):member1(5){}
MyClass(int some_init_args){member1=5}
Example of Member Initialization Lists
#include <iostream>
class Parent {
    public:
    Parent( int x ) {
        std::cout << "Parent's constructor"
        << " called with " << x << std::endl;
        }
    };

class Derived: public Parent {
    public:
    Derived() : Parent( 10 ) { // With "Member Initialization List".
        std::cout << "Derived's constructor" << std::endl;
        }
    };

class Strclass {
    public:
    Strclass() : thestr("This is the init string.") {
        std::cout << thestr << std::endl;
        }
    private:
    std::string thestr;
    };

int main() {
    Derived a_derived_instance;
    Strclass somesc;
    }

Exceptions

Recovering from errors in C is done by explicitly planning for every specific erroneous condition. In C++ there is a system of "exceptions" where (specifically) unanticipated errors can be treated reasonably.

While it is critical to understand exceptions I personally have some misgivings about them which are well documented in my Python notes. Without going into details here, I’ll sum up my personal opinion about C++ exceptions by quoting the Google C++ Style Guide which simply says, "We do not use C++ exceptions."

Here is a rough idea of how C++ exceptions work.

Example Of Exception Use
#include <iostream>
using namespace std;

void thefunction() { throw 99; }

int main (int argc, char *argv[]) {
    try {
        if (argc > 1){ thefunction();}
        else { throw '?';}   }
    catch(int e) {  // The catch is chosen based on its type.
        cout << "Exception number " << e << " has occured." << endl;
    }
    catch (...) {  // If the type sent by throw is not defined, go here.
        cout << "Unknown exception has occured." << endl;
    }
    return 0;
}

When this code is run with arguments it returns:

Exception number 99 has occured.

When this code is run without arguments it returns:

Unknown exception has occured.

Note that the catch statement is chosen based on the type specified by the throw command. In this simple example, this is not remarkable, but when defining your own types with classes, this mechanism can be very useful in organizing code to react to errors arising from different causes.

There are much fancier ways to use exceptions. You can define your own by inheriting standard exceptions. You can also catch and handle standard exceptions your own way. They can be nested such that the inner exception passes the information through to the outer ones.

Templates

IntArray::IntArray(){ /* Normal constructor. / } template<class Type> Array<Type>::Array(){ / With template. */ }

Standard Template Library

The near universal detest for STL in game development makes each studio’s code a silo of private libraries, which is unfortunate.

You don’t use STL where you need max performance, but performance isn’t the top priority for most code, even in games.

I don’t like STL much and rarely use it, but I do really like standards. The library situation with C++ is really a shame.

#ID_AA_Carmack (2014-04-05)
— John Carmack

Both C and C++ are very spartan languages and in order to do anything useful in either you pretty much need to write some tools to manage simple flexible data structures. This has been thought out in C++ with the Standard Template Library or STL.

STL provides templates that allow programs to easily create simple but powerful data structures for all kinds of pragmatic uses. This is the kind of stuff that higher level languages like Javascript and Python take for granted.

Containers
  • deque

  • list

  • map

  • set

  • string

  • vector

Container Adaptors
  • priority_queue

  • queue

  • stack

Vector

The following sample program shows the basic STL container usage with a "vector" template.

#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<int> V1; // Define a vector of integers.
    V1.push_back(9); V1.push_back(6); V1.push_back(3); // Load vector.
    for (int i= 0; i<V1.size(); i++) { cout << V1[i] << endl; } // Display values.
    vector<int>::size_type v_st= V1.max_size(); // Note namespace to use size_type.
    cout << "Max number of elements in a Vector<int>: " << v_st << endl;
}

Reserved Keywords

asm, auto, bool, break, case, catch, char, class, const, const_cast,
continue, default, delete, do, double, dynamic_cast, else, enum,
explicit, export, extern, false, float, for, friend, goto, if, inline,
int, long, mutable, namespace, new, operator, private, protected,
public, register, reinterpret_cast, return, short, signed, sizeof,
static, static_cast, struct, switch, template, this, throw, true, try,
typedef, typeid, typename, union, unsigned, using, virtual, void,
volatile, wchar_t, while

Random

Newish C++ has a civilized library for random number generation. To use this fine library, I needed to compile with -std=gnu++11.

#include <random>
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0,255);
int rand_red_value= distribution(generator);

This library seems pretty comprehensive. It features different PRNG engines and a bunch of distributions. See the official documentation for details.

Makefile

Here’s a very simple Makefile that can be used on very simple C++ programming tasks.

ALLSRCS:= $(wildcard *.cc)
ALLEXEC:= $(ALLSRCS:.cc=)
.PHONY:all clean
all: $(ALLEXEC)
%: %.cc
    g++ -o $@ $<
clean:
    rm $(ALLEXEC)

Profiling

Wondering what the heck is taking so long in your program? You can get hints about what your program spends its time doing.

Do this
    sudo apt-get install linux-tools
    sudo bash -c "echo 0 > /proc/sys/kernel/kptr_restrict"
    perf record ./yourprog --your-opts
    perf report
Get a nice report like this
 18.28%  yourprog  yourprog       [.] Plist::querypoint()                    ▒
 13.85%  yourprog  yourprog       [.] shallowanglecull(Llist*, Tlist*, float,▒
  8.70%  yourprog  yourprog       [.] Llist::querypointA()                   ◆
  7.00%  yourprog  yourprog       [.] Llist::querypointB()                   ▒
  6.73%  yourprog  yourprog       [.] Vector::compareto(Vector)              ▒
  5.76%  yourprog  yourprog       [.] searchlinelist(Plist*, Plist*, Llist*) ▒
  5.69%  yourprog  yourprog       [.] operator==(Vector, Vector)             ▒
  5.55%  yourprog  yourprog       [.] Vector::~Vector()                      ▒
  3.92%  yourprog  yourprog       [.] Tlist::BBinterfere(Llist*)             ▒
  3.59%  yourprog  yourprog       [.] searchtrilist(Plist*, Plist*, Plist*, T▒
  2.96%  yourprog  yourprog       [.] Tlist::querylink()                     ▒
  2.88%  yourprog  yourprog       [.] Llist::queryA()                        ▒
  2.26%  yourprog  yourprog       [.] BBinthelper(float, float, float, float,▒
  1.89%  yourprog  yourprog       [.] Vector::gg_valX()                      ▒
  1.25%  yourprog  yourprog       [.] Llist::querylink()                     ▒
  0.92%  yourprog  yourprog       [.] Tlist::querypointC()                   ▒
  0.91%  yourprog  yourprog       [.] find_matching_pnum(unsigned int, Plist*▒
  0.90%  yourprog  yourprog       [.] Vector::Vector()                       ▒
                ...

Or if that isn’t good enough check out gprof.

Actually this hack is quite clever too. See the other suggestions there too (e.g. Valgrind).