C++

From miki
Revision as of 13:54, 13 September 2021 by Mip (talk | contribs) (→‎Data structures)
Jump to navigation Jump to search

References

See also C references and C / C++.

Links

C++ language
Including C Language Library
and ... IOStream Library
Create a local copy with:
# Make a local copy of www.cplusplus.com - use option -P http://proxy:port if needed
sudo mkdir httrack
sudo chown www:www-data httrack/
cd httrack/
sudo su www -c "httrack http://www.cplusplus.com/ -W -O /var/www/httrack -I0 -n -d -%v \
    '-www.cplusplus.com/forum/*' '-www.cplusplus.com/src/*' '-www.cplusplus.com/member/*' \
    '-www.cplusplus.com/user/*' '-www.cplusplus.com/contact*' -P 127.0.0.1:8118"
STL
sudo apt install libstd++-6-doc
man std::vector
Miscellaneous
manpage stl-manual (from SGI)
sudo apt-get install stl-manual
ln -s /usr/share/doc/stl-manual/html /var/www/sgi        # Now the manual is available at http://localhost/sgi

Books

C++ Standards

C++11

References
Range-based for loop

C++11 introduces range-based for loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
const auto& cv = v;
int a[] = {0, 1, 2, 3, 4, 5};

for (const int& i : v)            // access by const reference
for (auto i : v)                  // access by value, the type of i is int
for (auto&& i : v)                // access by forwarding reference, the type of i is int&
for (auto&& i : cv)               // access by f-d reference, the type of i is const int&
for (int n : {0, 1, 2, 3, 4, 5})  // the initializer may be a braced-init-list
for (int n : a)                   // the initializer may be an array

Use std::as_const(x) to prevent copy-on-write:

struct cow_string { /* ... */ }; // a copy-on-write string
cow_string str = /* ... */;

// for(auto x : str) { /* ... */ } // may cause deep copy
for(auto x : std::as_const(str)) { /* ... */ }

This is not to be confused with the for_each algorithm in STL.

Lambda expressions

A lambda expression has the form: [capture](parameters)->return-type {body}.

int main()
{
  char s[]="Hello World!";
  int Uppercase = 0; //modified by the lambda
  for_each(s, s+sizeof(s), [&Uppercase] (char c) {
    if (isupper(c))
      Uppercase++;
    });
  cout<< Uppercase<<" uppercase letters in: "<< s<<endl;
}
Automatic Type Deduction auto and decltype

Use keyword auto to infer the type of an object automatically:

auto x=0; //x has type int because 0 is int
auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long

The new operator decltype takes an expression and returns its type:

const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;

Note that the reference type of auto&& depends on the the reference given (as per reference collapsing rule [1]) [2]:

A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&

For short, auto&& is an universal reference (like T&& in templates) that just binds to anything [3].

Uniform Initialization Syntax

C++ has at least four different initialization notations, some of which overlap.

Parenthesized initialization looks like this:

std::string s("hello");
int m=int(); //default initialization

You can also use the = notation for the same purpose in certain cases:

std::string s="hello";
int x=5;

For POD aggregates, you use braces:

int arr[4]={0,1,2,3};
struct tm today={0};

Finally, constructors use member initializers:

struct S {
 int x;
 S(): x(0) {} };

C++11 cleans up this mess with a uniform brace notation:

class C
{
int a;
int b;
public:
  C(int i, int j);
};

C c {0,0}; //C++11 only. Equivalent to: C c(0,0);

int* a = new int[3] { 1, 2, 0 }; //C++11 only

class X {
  int a[4];
public:
  X() : a{1,2,3,4} {} //C++11, member array initializer
};

In C++11 you can initialize containers intuitively:

// C++11 container initializer
vector<string> vs={ "first", "second", "third"};
map singers =
  { {"Lady Gaga", "+1 (212) 555-7890"},
    {"Beyonce Knowles", "+1 (212) 555-0987"}};

Similarly, C++11 supports in-class initialization of data members:

class C
{
  int a=7; //C++11 only
public:
  C();
};
Deleted and Defaulted Functions

Defaulted functions instruct the compiler to provide the default implementation for these functions:

struct A
{
 A()=default; //C++11
 virtual ~A()=default; //C++11
};

The opposite of a defaulted function is a deleted function. To disable copying, declare these two special member functions =delete:

struct NoCopy
{
 NoCopy & operator =( const NoCopy & ) = delete;
 NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //compilation error, copy ctor is deleted
nullptr

nullptr is strongly-typed:

void f(int); //#1
void f(char *);//#2
//C++03
f(0); //which f is called?
//C++11
f(nullptr) //unambiguous, calls #2
Delegating Constructors

In C++11 a constructor may call another constructor of the same class:

class M //C++11 delegating constructors
{
  int x, y;
  char *p;
public:
  M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
  M(): M(0) {cout<<"delegating ctor"<<endl;} //#2 delegating
};
Rvalue References

Rvalue references can bind to rvalues, e.g. temporary objects and literals [4].

The primary reason for adding rvalue references is move semantics.

you can declare a move constructor and a move assignment operator like this:

class Movable
{
  Movable (Movable&&); //move constructor
  Movable&& operator=(Movable&&); //move assignment operator
};
std::move

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.

// Simple move constructor
A(A&& arg) : member(std::move(arg.member)) // the expression "arg.member" is lvalue
{} 
// Simple move assignment operator
A& operator=(A&& other) {
     member = std::move(other.member);
     return *this;
}

Reference collapsing rule [5]:

typedef int&  lref;
typedef int&& rref;
int n;
lref&  r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref&  r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&
More stuff
  • C++11 Standard Library
  • Threading Library
  • New Smart Pointer Classes
  • New C++ Algorithms

C++14

References
Return type deduction

auto can be used as function return type. Most useful when combined with templates, or to avoid cascade of API changes if changing some object internal type.

auto getvalue()
{
    return 1.4;
}
Generic lambdas

These are lambdas using auto as parameter type:

// Here we make in fact a named-lambda
auto adder  = [](auto op1, auto op2){ return op1 + op2; };

This is the loose equivalent of creating a template function.

Initialized Lambda Captures

Now lambda parameter can be initialized:

auto y = [&r = x, x = x+1]()->int {...}

This allows for the move semantic in lambdas:

#include <memory>
#include <iostream>
 
int main()
{
  std::unique_ptr<int> p(new int);
  int x = 5;
  *p = 11;
  auto y = [p=std::move(p)]() { std::cout << "inside: " << *p << "\n";};
  y();
  std::cout << "outside: " << *p << "\n";
  return 0;
}
The deprecated attributes
[[deprecated("Consider using something other than cranky")]]
int cranky()
{
   return 0;
}

Handling is compiler specific.

Binary Literals and Digit Separators

Now we can use 0b to construct binary literals (like 0x for hex).

int val = 0b11110000;

Use ' to separate digit group (will not change the value):

int   mask   = 0b1000'0001'1000'0000;
float salary = 300'000.00;
Miscellaneous
auto up = make_unique<LongTypeName>(args)           # Better than calling new without disclaimer

C++17

Description of C++17 features, in 100 source code examples.

C++0x (old stuff)

auto_ptr deprecated
unique_ptr and shared_ptr
  • Using unique_ptr, Part I
    Basically, unique_ptr permits move from rvalues with copy syntax using a move constructor (which binds to rvalues), while blocking the copy from lvalues by making the copy constructor (which binds to lvalues) private:
template <class T>
class unique_ptr
{
public:
 unique_ptr(unique_ptr&& u);   // rvalues bind here
private:
 unique_ptr(const unique_ptr&); // lvalues bind here
};
rvalue references (and move semantics)

Benchmark

Between compilers

  • g++
  • EKOPath4, a recently open-sourced compiler with much better performance than gcc/g++.
  • Intel compiler
  • pathCC, PathScale compiler

Between languages

Nested Classes

See Nested classes on ibm.com.

Virtual destructors

Rule of thumb: make your destructor virtual if your class has any virtual functions.

Virtual destructors are needed when a derived class is explicitly deleted via delete, but using a reference/pointer to a base class.

Patterns

Resource Allocation Is Initialization (RAII)

Reference:

From Kevin on stackoverflow.com: The idea is that an object's destructor is responsible for freeing resources. When the object has automatic storage duration, the object's destructor will be called when the block in which it was created exits — even when that block is exited in the presence of an exception.

Tips

Scoped enums

Use scoped enums to specify the underlying (storage) type for enums (available since c++11, ie. gcc/clang option -std=c++11):

typedef enum : unsigned char { zero, one, two } enum8_t;
typedef enum : unsigned short { zero16, one16, two16 } enum16_t;

assert (sizeof(enum8_t) == 1);                 
assert (sizeof(enum16_t) == 2);

Finally blocks

From stackoverflow.com:

int * array = new int[10000000];
try {
    // Some code that can throw exceptions
    // ...
    throw std::exception();
    // ...
} catch (...) {
    // The finally-block (if an exception is thrown)
    delete[] array;
    // re-throw the exception.
    throw; 
}
// The finally-block (if no exception was thrown)
delete[] array;

Note that the finally block may throw an exception, hence discarding the original exception.

To avoid the double delete:

std::exception_ptr e;

try { 
    /*try block*/ 
}
catch (...) {
    /*finally block*/
    e = std::current_exception();
}
if (e) {                            // e evaluates to 'false' if no exception!
    std::rethrow_exception(e);
}

Miscellaneous

Problem Solution
Overloading operators i++ or ++i
See [6] for more details.
class Number {
public:
    // prefix ++ - Must return (*this)
    Number& operator++ ();    
    // postfix ++ - Must never return (*this) by reference
    Number  operator++ (int);  // ... OR ....
    void  operator++ (int);   
};
Reset ostringstream
ostringstream oss;
oss << "Hello," << 123 << endl;
string s = oss.str();

oss.str("");                  // oss empty now - we can reuse it
oss << "World!" << 456 << endl;

Pits

Persistent iomanip flags

iostream is by far the sh*tiest IO library as easily demonstrated by the amount of code needed to simply print hexadecimal numbers, and still even get it right [7]

# Print hexadecimal number
cout << "0x" << setfill('0') << setw(2) << hex << 10 << endl;               // WRONG!!!
cout << "0x" << setfill('0') << setw(2) << right << hex << 10 << endl;      // GOOD
cout << "0x" << setfill('0') << setw(2) << internal << hex << 10 << endl;   // BETTER

When using setw, we must use either left, right or internal to make sure we don't inherit one of the previous setting.

Binary I/O

See [8] for reference:

  • Open a file. Binary file must be opened with mode ios::binary.
  • // Open a file for input
    ifstream myFile ("data.bin", ios::in | ios::binary);
    
    // Open a file for output
    ofstream myFile;
    ...
    myFile.open ("data2.bin", ios::out | ios::binary);
    
    // Open a file for input and output
    fstream myFile;
    myFile.open ("data3.bin", ios::in | ios::out | ios::binary);
    
  • Read from a file. Complex data must be casted to (char*).
  • #include <fstream.h>
    ...
    char buffer[100];
    ifstream myFile ("data.bin", ios::in | ios::binary);
    myFile.read (buffer, 100);
    if (!myFile) {
        // An error occurred!
        // myFile.gcount() returns the number of bytes read.
        // calling myFile.clear() will reset the stream state
        // so it is usable again.
    }
    ...
    if (!myFile.read (buffer, 100)) {
        // Same effect as above
    }
    
  • Write to a file. Complex data must be casted to (char*).
  • #include <fstream.h>
    ...
    char buffer[100];
    ofstream myFile ("data.bin", ios::out | ios::binary);
    myFile.write (buffer, 100);
    

Security Tips

Problem Solution
Use mlock() to prevent a section of memory from swapping to disk
(source: "Building Secure Software," John Viega & Gary McGraw)

Tools

GNU cflow

GNU cflow analyzes a collection of C source files and prints a graph, charting control flow within the program.

GNU cflow is able to produce both direct and inverted flowgraphs for C sources. Optionally a cross-reference listing can be generated. Two output formats are implemented: POSIX and GNU (extended).

Miscellaneous

<iostream.h> or <iostream>

  • <iostream> is the standard compliant library. <iostream.h> is deprecated since many many years.
  • <iostream> contains a set of templatized I/O classes which support both narrow and wide characters (by contrast, <iostream.h> classes are confined to char exclusively).
  • Third, the C++ standard specification of iostream's interface was changed in many subtle aspects. Consequently, the interfaces and implementation of <iostream> differ from <iostream.h>.
  • Finally, <iostream> components are declared in namespace std whereas <iostream.h> components are declared in the global scope.

Note that both libraries cannot be mixed in one program.

Data structures

Tree Iterator

  • Using parent pointer [9]
  • Using threaded tree. Threaded trees replaces the null right pointer with a pointer to next node. [10]
This requires a boolean flag to tell whether the right pointer is a child or thread.

Libraries

Math

Eigen is a C++ template library for linear algebra: matrices, vectors, numerical solvers, and related algorithms.
Blaze is an open-source, high-performance C++ math library for dense and sparse arithmetic.