C++ Crash Course

Benjamin Navarro

What's in this course?

  1. Introduction what is C++ ?
  2. Type system: fundamental types, custom types
  3. Variables: value/reference/pointer, type conversions
  4. Functions: syntax, template, call
  5. Classes: methods, attributes, visibility, template
  6. Scopes: block, function, class, namespace
  7. Naming convention(s)

What is C++?

Wikipedia says:

C++ is a general-purpose programming language. It has imperative, object-oriented and generic programming features, while also providing facilities for low-level memory manipulation.

What is C++?

I would add the following important points:
  • Compiled to machine code: no interpreter (e.g Python) or virtual machine (e.g Java)
  • No garbage collector: no pauses imposed to free allocated resources
  • Statically-typed: all types must be known at compile-time (≠ Python or Matlab)
  • Evolutive: a new standard is published every 3 years

Hello, world!


#include <iostream>

int main() {
    std::cout << "Hello, world!" << std::endl;
}

What's a type?

A type answers the question: what is that thing?

  • A number?: integer, floating point, complex
  • A function?: what gets in, what gets out
  • An object?: what is it made of, what I can do with it
  • ...


A type tells what you can/can't do with something

Fundamental types

C++ offers some fundamental types to use out of the box:
  • void: nothing or incomplete type
  • bool: a boolean, can be either true or false
  • char: a character (e.g letter A)
  • char/short/int/long: integer numbers (different sizes)
  • float/double: floating point numbers (single/double precision)

Custom types

But other types can be created using:
  • arrays: basic containers
  • enum: a value restricted to a range of values
  • union: holds only one of its data members at a time
  • function: defined by its inputs and output
  • struct/class: can pack data and/or functions together

Custom types

enum Colors {                         // type is 'Colors'
    Red, Green, Blue                  // type is 'Colors'
};

union IntBytes {                      // type is 'IntBytes'
    int value;                        // type is 'int'
    unsigned char bytes[sizeof(int)]; // type is 'unsigned char[4]'
};

int findTheAnswer();                  // type is 'int (*)()'

class Robot {                         // type is 'Robot'
public:
    Robot(size_t dofs);
    void move(double distance);       // type is 'void (Robot::*)(double)'
private:
    double velocity_;                 // type is 'double'
};

What's a variable?

A variable provides named storage for a given type

int answer = 42;
double golden_ratio = 1.618;
float pi = 3.14f;
char a, b;
a = 'A'; // single quotes to create a character
b = 'B';
char hello[] = "Hello, World!" // double quotes to create a C-style string

/***    From C++11      ***/
auto age = 28; // age has type int
auto name1 = "benjamin"; // name1 has type const char*
auto name2 = std::string("benjamin"); // name2 has type std::string
auto name3 = "benjamin"s; // same as name2, using std::string_literals
auto car; // ILLEGAL
    

Values, references and pointers


int answer = 42;    // 'answer' is a value
int foo = 32;       // 'foo' is a value

int* ptr = &answer; // 'ptr' is a pointer
                    // 'ptr' contains the memory
                    // address of 'answer'
                    
ptr = std::addressof(answer); // C++11 style

int& ref = answer;  // 'ref' is a reference
                    // 'ref' is bound to 'answer'

ptr = &foo;         // 'ptr' now points to 'foo'
*ptr = -57;         // 'foo' now equals -57

ref = foo;          // 'answer' now equals -57
        

Type conversions

Some types can be converted to other types thanks to implicit or explicit conversions:

int a = 2048;                     // no conversion here, 'int's on both sides
double b = a;                     // implicit conversion from 'int' to 'double'
void* p1 = &a;                    // implicit conversion from 'int*' to 'void*'
int*  p2 = p1;                    // INVALID CONVERSION FROM 'void*' TO 'int*'
int*  p2 = static_cast<int*>(p1); // explicit conversion from 'void*' to 'int*'

/***    Narrowing conversions   ***/
std::uint8_t byte1 = a;          // allowed, but only the first LSB of 'a' is copied
std::uint8_t byte2{a};           // C++11 syntax, still allowed but generates a warning
    

Function declaration


/***        Syntax       ***/
return_type name(parameter_list);

auto name(parameter_list) -> return_type; // C++11

/***        Basic examples       ***/
void hello();
void addOne(int& x);

/***        Overloading          ***/
double sum(double x, double y);
double sum(std::vector<double> values);

/***        Compile-time (C++11)        ***/
constexpr int fibonacci(int n);

/***        Template        ***/
template<typename ValueT>
ValueT add(ValueT x, ValueT y);
    

Function calling


int main() {
    hello();

    int x = 3;
    addOne(x);                       // now 'x' = 4
    auto s = sum(2.5, -0.5);         // 's' = 2.
    int f1 = fibonacci(12);          // compile-time evaluation
    int f2 = fibonacci(x);           // run-time time evaluation
    auto res1 = add<float>(40.f, 2); // ValueT is forced to 'float'
    auto res2 = add(40, 2);          // ValueT is deduced to 'int'
    auto res3 = add(40., 2);         // ValueT cannot be deduced
}

Argument passing

When calling a function in C++, all arguments (i.e inputs) are passed by value (i.e copy), unless otherwise specified.

With languages such as Python or Java, all arguments are passed by reference, exept primitive types (int, bool, etc) that are passed by value.

Argument passing


void print(std::string a, std::string b) {
    a.append(b);
    std::cout << a << std::endl;
}

void concat(std::string& a, std::string& b) {
    a.append(b);
}

void concat(std::string* a, std::string* b) {
    a->append(*b);
}


int main() {
    std::string a = "foo", b = "bar";

    print(a, b);      // prints foobar
    a == "foo";       // true

    concat(a, b);
    a == "foobar";    // true

    concat(&a, &b);
    a == "foobarbar"; // true
}

Class definition

A class can contain data members (attributes), member functions (methods) and other types
class MobileRobot {
public:
    /***    Nested types    ***/
    enum class SizeUnit {
        Meters,
        Centimeters,
        Inches
    };

    /***    Constructors & Destructor    ***/
    MobileRobot();
    MobileRobot(double size, SizeUnit unit = SizeUnit::Meters);
    ~MobileRobot();

    /***    Member functions     ***/
    void move(double distance, double direction);
    double getDistance() const;
    double getDirection() const;

private:
    /***    Data members      ***/
    double size_;
    std::array<int, 2> wheel_encoders_;
    std::array<double, 2> motor_voltages_;
};

Class usage

class MobileRobot {
    //...
};

int main() {
    MobileRobot rob(1.5, MobileRobot::SizeUnit::Centimeters);
    MobileRobot rob2(1.5); // unit is 'MobileRobot::SizeUnit::Meters'

    rob.move(2.5, 0.3);
    double dist = rob.getDistance();
    double dir = rob.getDirection();

    rob.size_ = 3. // ILLEGAL, 'size_' is private within MobileRobot
}
    

Data members

Non-static data members are bound to an object
Static data members are bound to the class itself
class MobileRobot {
public:
    MobileRobot() :
        distance_target(1.),
        direction_target(1.57)
    {
        ++swarm_size;
    }
    ~MobileRobot() {
        --swarm_size;
    }

    static size_t swarm_size;
    double distance_target;
    double direction_target;
};
size_t MobileRobot::swarm_size = 0;
int main() {
    using namespace std;

    MobileRobot rob1;
    cout << MobileRobot::swarm_size << endl; // 1

    MobileRobot rob2;
    cout << MobileRobot::swarm_size << endl; // 2

    rob1.distance_target = 3.5;
    cout << rob1.distance_target << endl;    // 3.5
    cout << rob2.distance_target << endl;    // 1.
};

Members functions

Non-static member functions are bound to an object
Static member functions are bound to the class itself
class MobileRobot {
public:
    void moveTo(double x, double y);
    void moveTo(Point point);

    // 'const' at the end means that no
    // data members will be modified
    double getDistance() const;
    double getDirection() const;

    static const size_t wheelCount();

private:
    double encodersToDistance() const;
};
const size_t wheelCount() {return 2;}
int main() {
    using namespace std;

    MobileRobot rob;
    rob.moveTo(2., 3.);
    rob.moveTo(Point(-3., 4.));

    cout << rob.getDistance() << endl;       // 5
    cout << rob.getDirection() << endl;      // ~0.93
    cout << rob.wheelCount() << endl;        // 2

    cout << rob.encodersToDistance() << endl
};

Class template

A class template defines a family of classes
template<typename ValueT>
struct Vector2D {
    ValueT x;
    ValueT y;

    Vector2D(ValueT x, ValueT y) :
        x(x),
        y(y)
    {
    }

    ValueT length() const {
        return std::sqrt(x*x + y*y);
    }

    void add(const Vector2D<ValueT>& other) {
        x += other.x;
        y += other.y;
    }
};

int main() {
    using namespace std;

    Vector2D<int> v1(3, 4);
    cout << v1.length() << endl; // 5

    Vector2D<int> v2(-3, -4);
    v1.add(v2);
    cout << v1.length() << endl; // 0

    Vector2D<double> v3(3.14, 1.6);
    cout << v3.length() << endl; // ~3.52

    v3.add(v1); // incompatible types

};

What's a scope?

A scope limits the validity of some name to a portion of code
int main() {
    int answer = 42;     // scope for the first 'answer' begins
    answer = 12;         // modifies the first 'answer'
    {                    // an opening brace creates a new scope
        int answer = -1; // create a second 'answer' that hides the first one
        answer = 512;    // the second 'answer' gets modified
    }                    // a closing brace closes the current scope
    answer == 12;        // true
}

Some commom scopes

int a; // 'a' is a global variable (to be avoided)

void my_function(int a) {
    int b; // 'a' & 'b' are local to the function.
           // This 'a' is unrelated to the global 'a'
}

struct Foo {
    int bar; // 'bar' is only available via a 'Foo' object.
}

void setFooBar(Foo& foo) {
    foo.bar = 42;
}

namespace ns {
    enum AudienceIsLost {
        Yes,
        No
    };
}
ns::AudienceIsLost are_you_lost; // use :: to access 'ns' scope

Pick and stick to a convention

Unless you are asked to use a given convention, just pick one
But, please, stick to it!

Some conventions:

My convention

Here is an overview of "my coding style"

// Use only the minimum set of includes
#include <cstddef> // forget C versions, e.g stddef.h
#include <OtherClass>
#include <string>
#include <thread>
#include <chrono>
#include <vector>
#include <algorithm>

// try to keep namespaces to a single word
namespace stuff {

// camel case with leading lower case character for functions (member/non-member)
void fooBar();

// camel case with leading upper case character for class names
// always put the access specifier (public/protected/private) for base class(es)
class KeepItShort : virtual public Parent {
public: // public comes first

    // New types first (if any)
    enum class Colors {
        Red,
        Green,
        Blue
    };

    // Then constructors (if any)
    KeepItShort();
    KeepItShort(size_t max_length);

    // Then the destructor (if any)
    ~KeepItShort();

    // Then member function
    size_t getMaxLength() const;         // use const whenever possible
    void use(const OtherClass& other);   // pass non-fundamental types by reference
                                         // and use const reference is possible

    virtual void print() const override; //  use 'override' when overriding a member function

    [[deprecated("That's old stuff")]] dontUseThis(); // use attributes like 'deprecated'

    size_t length; // don't be affraid of public data members
                   // a.k.a don't put getters/setters if they are useless

protected: // then protected
    virtual void stopHere() final; // use final when adequate

private: // then private
    size_t max_length_; // add a trailing underscore for private data members
};

} // namespace stuff

// Don't put argc/argv if you don't use them
int main() {

    // don't put a return statement if you don't need it
}

void stuff::fooBar() {
    size_t max_length = 42; // lower case for variables
    auto keep_it_short = stuff::KeepItShort(max_length); // I like this syntax
    stuff::KeepItShort other(max_length);                // but this one is also ok

    // lower case for lambda functions
    auto say_hello = [](const std::string& name) {
        using namespace std::std;               // local 'using namespace' is fine
        using namespace std::chrono_literals;
        cout << "Hello " << flush;
        for(auto c: name) {                     // range-based for loops are great
            this_thread::sleep_for(500ms); // forget about C time functions
            cout << c << flush;
        }
    };
    auto th = std::thread(say_hello, "John"); // use standard stuff (e.g not POSIX/CWinThread)

    // Always use braces, even for single line instructions
    if(keep_it_short.length < 12) {
        std::cout << "That's short" << std::endl;
    }
    else {
        std::cout << "Thats's long" << std::endl;
    }

    auto values = std::vector<double>{1., 2., 3., 4., 5.};
    // C++17 style to keep 'value' inside the 'if' scope
    if(auto value = std::find(values.begin(), values.end(), 4.); value != values.end()) {
        std::cout << "Found " << *value << std::endl;
    }
}

Thank you!

Any question?

www.000webhost.com