Print service provided by iDogiCat: http://www.idogicat.com/
home logo





Home > IT > Programming > C++ Programming > Professional C++ > Professional C++: Basics

Professional C++: Basics

Here we won't cover all aspects of C++, but only cover some important points and/or new features of C++.

Namespace

// in header file
namespace mycode {
    void myfunc();
}

// in .cpp file
#include 
#include "my_namespace.h"

void mycode::myfunc()
{
    std::cout << "hello" << std::endl;
}

'using' should not be used too much, otherwise effect of namespace is eliminated. Also, don't use 'using' in header files, since this applies it to all files including this header file.

Nested namespaces: in C++17 there is a simplified way to write it:

namespace mylib {
    namespace network {
        namespace ftp {
            // ...
        }
    }
}

// the above code can be simplified as:
namespace mylib::network::ftp {
    // ...
}

Namespace alias

namespace myftp = mylib::network::ftp;

myftp::ftputil::send(...);

Enum & Strongly Typed Enum

'enum' type is not type safe, it's represented as int:

enum Pet {dog, cat};
enum Color {red, blue, yellow};

if (dog == red)
    cout << "dog == red !!!" << endl;

if (dog == 0)
    cout << "dog == 0" << endl;

Enums can be made type safe by using 'enum class':

enum class Pet {dog, cat};  // dog & cat cannot be used directly, they have to be prefixed with 'Pet::'
enum class Color : unsigned long {red, blue, yellow};   // you can even change the default 'int' to 'unsigned long'

if (Pet::dog == Color::red)    // compiler error here
    cout << "dog == red !!!" << endl;

if (Pet::dog == 0)             // compiler error here
    cout << "dog == 0" << endl;

if (Pet::dog == Pet::cat)      // OK, returns false
    cout << "dog == cat" << endl;

Initializer for if statement

C++17: if can contain an initializer, and the variable defined here can only be used in the condition and the following if body:

if (Staff tom = getStaff("Tom"); tom.getAge() > 30) {
    cout << "Tom is older than 30. He's " << tom.getAge() << " years old." << endl;
}

iDog: is it only me? I really feel that this looks ugly.

Switch

Types can be used in switch:

  • int, or type that can be converted to int
  • enum, or strongly typed enum

'case' must be followed by a constant.

'switch' can also contain an initializer like 'if'.

Fallthrough can generate a warning unless the case has an empty body. [[fallthorugh]] attribute can be specified to explicitly specify that it's an intentional fallthrough.

switch(color) {
case Color::gray:
    // do something about gray
    [[fallthrough]];
case Color::black:
    // do something about gray and black
    break;
case Color::red:        // here '[[fallthrough]]' is not needed
case Color::blue:
    // do something
    break;
// ...
}

iDog: Personally I feel attributes weird... But unfortunately it has been introduced to many languages (C++, C#, and annotation in Java).

Function Return Type Deduction

You can ask the compiler to figure out the return type of a function automatically:

auto add(int n1, int n2)
{
    return n1 + n2;
}

This can also be used on recursive calls, but in this case, the first return statement must be a non-recursive call.

iDog: why not 'var' as that in Java & C#?

Current Function’s Name

Every function has a local predefined variable __func__ containing the name of it:

void myfunc()
{
    std::cout << "Entering function " << __func__ << std::endl;
    // ...
}

C-style array

int array1[10];            // num must be const
int array2[10] = {};       // initialize all elements with default value (here it's 0)
int array3[10] = {1, 2};   // first 2 elements initialized as 1 & 2, all other ones 0
int array4[] = {1, 2, 3, 4, 5};  // array with 5 elements

size_t size = std::size(array4);      // need to include <array>, available in C++17
size = sizeof(array4) / sizeof(array4[0]);  // old way

C++ style arrays

std::array: size is fixed.

#include <array>

std::array myArray = {1, 2, 3, 4, 5};

for (size_t i = 0; i < myArray.size(); ++i)
    cout << "value #" << i << " is " << myArray[i] << endl;

Advantages:

  • it won't be automatically cast to a pointer, to avoid many bugs
  • it has iterators
  • it knows its size

std::vector: size is not fixed.

#include <vector>

std::vector<int> myVector = {1, 2, 3};
myVector.push_back(4);
myVector.push_back(5);

for (size_t i = 0; i < myVector.size(); ++i)
    cout << "value #" << i << " is " << myVector[i] << endl;

Structured Binding

This allows you to declare multiple variables and initialize them with elements from an array/struct/pair/tuple.

std::array myArray = {1, 2, 3};
auto [a, b, c] = myArray;   // must use 'auto' (cannot use 'int' here); number should be the same

struct Point {
    int x; int y; int z;
    Point (int x, int y, int z) {
        this->x = x;
        this->y = y;
        this->z = z;
    }
};

Point pt(1, 2, 3);
auto [x, y, z] = pt;    // use all the non-static public members

Range-based for loop

This kind of for loop works for C-style arrays, initializer lists, and containers that has begin() and end() returning iterators.

std::array myArray = {1, 2, 3};

for(int i : myArray)
    std::cout << i << std::endl;

int sum(int (&list)[3]) {      // here the size of the array should be retained. so 'int list[3]' won't work.
   int s = 0;

   for (auto i : list)
      s += i;

   return s;
}

int a[] = {1, 2, 3};
auto s = sum(a);  // error if size of a is not 3

Initializer List

Initializer list makes it easy to write functions that accept variable number of arguments of the same type.

#include <initializer_list>

using namespace std;

int sum(initializer_list<int> list) {
    int s = 0;

    for(auto i : list)
        s += i;

    return s;
}

int a = sum({1, 2, 3});