infuerno.github.io

Pluralsight - C++ Fundamentals

Context

Modern C++

The standard library contains:

Managing memory yourself is old-school.

C++/CLI

C++/CLI is a variant of C++ which makes managed code. Visual Studio can compile C++ code either into native code or managed code. Managed code can be easily called from other .NET modules. Note, choose CLR project to compile to managed code, choose Win32 projects to compile to native code.

Versions of C++

C++ doesn’t belong to anybody. A standards committee decides on updates. First version was C++98 with a small update C++03. Next version was C++11 with a small update C++14.

Tools

Visual Studio

Running a console application without debugging automatically give you a ‘Press any key to continue…’ at the end so you can see all the output before closing the command window.

When creating a new project, click “include pre-compiled header” to include the “stdafx.h” header file to make the application compile faster (not run faster).

Console Application Structure

# preprocessor directives The preprocessor goes through the code before the compiler and tweaks it a little bit. One thing it does is combines multiple files into one. Put just the definitions of functionality into a header file and then include that header file into each file that needs it. Includes from the Standard Library do not have the .h on the end.

:: scope resolution operator as in std::cout

Language Basics

C++ is strongly typed - once a variable is declared as an int, it is always an int.

Fundamental types are built into the language and include int, bool, char. User defined types include e.g. std::string. They can do everything that fundamental types can do i.e. full participants in the language.

You can see there are red wigglies here. They say: Error: a value of type “const char *” cannot be used to initialize an entity of type “char”. The important part of that is probably the word ‘Error’.

Fundamental Types on MSDN: http://msdn.microsoft.com/en-us/library/cc953fe1.aspx Data Type Ranges on MSDN: http://msdn.microsoft.com/en-us/library/s3f49ktz.aspx

Different types hold different kinds of data with different lengths and different maxiumum values. When assigning doubles to ints, ints to bools, ints to chars etc, there are no runtime errors. However, the compiler knows the different lengths and will help you and issue warnings.

Casting

Compiler will convert types when they’re compatible, with a warning if data might be lost. Error only if they are not compatible. By casting, the compiler warnings disappear, though this still may lead to trouble. Safer casts available with templates. Suffixes can be used to show the type of a literal and are a form of casting.

Overflow

Overflow can happen silently. E.g. char x = 300 will just assign the value 44 to x without error.

User defined types

Classes and objects

Enums

Enums in c++ are not prefixed by the name of the enumeration, so have to be unique across all enumerations defined. Generally just have a header file with the enum defined within.

Preprocessor

The preprocessor is most often used to include all files required into one file. This is achieved with preprocessor directives such as #include "Person.h". If files are included multiple times, the compiler errors. Rather than keep track of the whole tree of inclusions, just use the #ifndef and #endif to defined a constant in your header file, which has the effect of thereafter only including that file if the constant hasn’t been defined.

#pragma once does the same thing for Visual Studio.

Flow of control

Now a momentary word about bracing style. People have arguments about brace positioning that far exceed the arguments they can have about politics or religion or any other major touch points.

Switch

expressions and values must be integral type or enum

Functions

Free functions

Member functions

Inline functions

Error messages

Errors from calling functions tend to come from one of two places:

  1. the compiler - have you declared the function (usually in the .h)
  2. the linker - have you implemented the function (usually in the .cpp)

For visual studio, the .cpp files in particular MUST be part of the project, not just exist in the folder.

Immediate if

Much cleaner to use immediate if if applicable: result = something? 7: 302;

Operators

0 matches false, everything else matches true. Yoda syntax: if (3==i) (this is popular because if typo occurs and only one equals sign is entered, in this case there would be a compiler error).

Bitwise operators

These do the operations bit by bit, & and ^. Commonly used to save space and time by packing individual bit values into a single number. E.g. if you have an operation which can take 7 different flags, one way is to take 7 different parameters, all alternatively, take a integer, check the lowest 7 bits of that integer to see if the options are on or off. This is implemented using constants (so you don’t have to work out which int corresponds to a certain set of options being on). flag1 = 1, flag2 = 2, flag3 = 4, flag4 = 8 etc. Bitwise or them to get your set of options, bitwise and them to check if the option is set or not.

Bitshift operators

Shifts all the bits along e.g. 4 >> 1 is 2, 4 << 1 is 8

Operator overloading

int i = j + 3;
Order newOrder = oldOrder + newItem;

Examples

Writing an overload

There are two ways of writing an overload:

  1. As a member function of whatever type is before the operator which takes one parameter, the type after the operator. Often, especially with comparison operators, the two types will be the same.

    // myObject < something bool MyClass::operator<(OtherType something)

  2. As a free function which takes two parameters. Often if this is a complement to a set of comparison operators on a class which defines comparison to e.g. int, these free function definitions will be included in the class .cpp file.

    // myObject < something bool operator<(MyClass myObject, OtherType something)

Since this isn’t a member of MyClass, it only has access to public member functions (unless you use friend). You can use friend by specifying that the free function is a friend inside the class definition. Thereafter the free function can access private member variables. This is one of the few situations where using friend is acceptable.

Templates

Function templates

The example below works for all types which have defined the less than operator. Note, for those that haven’t, the error messages are vague since they will often refer to the implentation and that isn’t obvious for the user of a function.

template <class T>
T max(T& t1, T& t2)
{
    return t1 < t2? t2: t1;
}

Class templates

An example of a template class which could take e.g. int or float

template <class T>
class Accum
{
private:
    T total;
public:
    Accum(T start): total(start) {};
    T operator+=(const  T& T) {return total = total + t;};
    T GetTotal() {return total;}
};

Template specialization

Compilers will generate appropriate code from the template for any type you pass it, but you can also specify the code you want if necessary. You may need this if:

Example of trying to accumulate Person (which doesn’t define operator+)

template <>
class Accum<Person>
    {
private:
    int total;
public:
    Accum(Person start): total(start) {};
    int operator+=(Person& t) {return total = total + t.GetNumber();};
    int GetTotal() {return total;}
};
  1. Take a copy of the template code
  2. Remove the class T from the template declaration on the first line
  3. Add the type fo the class declaration on the second line
  4. Replace type T with appropriate types (often the class type, but not necessarily)

Pointers and references

Pointers

References

C++ got pointers from C, but added references. References are much simpler than pointers, and are like aliases. Unlike pointers, they can’t be changed to point to different places, the target is set on declaration and that’s that. Also don’t have to use different punction (->) to talk to the target, just use . as normal.

int& rA = a; // set reference equal to a (hence alias analogy)
rA = b; // now aliasing b 
rA = 5; // change value to 5

Examples of bad programming

int *badPointer; // declare a pointer, but not pointing to any memory
*badPointer = 3; // try to dereference the pointer, program blows up

int *badPointer = nullptr;
if (badPointer) // this will now resolve to true or false (would still error if try to deref)
{
    *badPointer = 3;
}

int &badReference; // won't compile, has to point to something

Const

Const and pointers

The Free Store

Manual memory management

GREAT DEMO OF HOW TO IMPLEMENT WITH MANUAL MEMORY MANAGEMENT<<<

Smart pointers

Smart pointers in the Standard Library

Pointers and inheritance

Slicing

Slicing occurs when you pass objects by value and you pass e.g. a SavingsAccount when a function or object is expecting a BankAccount. The memory allocated is only enough for the BankAccount so anything extra in SavingsAccount is lost OR the copy will fail.

To avoid slicing always use references or pointers.

Tweeter localT("Local", "Tweeter", 123, "@local")
Person localP = localT; // slicing occurs, twitter handle gets lost
cout << localP.GetName() << endl; // calls the Person version of the GetName() function

// fix with pointers (make Person and pointer, to the address of localT and dereference to call GetName())
Tweeter localT("Local", "Tweeter", 123, "@local")
Person *localP = &localT; // slicing occurs, twitter handle gets lost
cout << localP->GetName() << endl; // calls the Person version of the GetName() function

// fix with references (just declare localP as Person&)
Tweeter localT("Local", "Tweeter", 123, "@local")
Person& localP = localT; // slicing occurs, twitter handle gets lost
cout << localP.GetName() << endl; // calls the Person version of the GetName() function

Cast operators