-
C++ is a statically typed language. That is, the type of every entity (e.g., object, value, name, and expression) must be known to the compiler at its point of use. The type of an object determines the set of operations applicable to it.
-
The operator << ("put to") writes its second argument onto its first.
-
A function can be a member of a class. For such a member function, the name of its class is also part of the function type. For example:
char& String::operator[](int index); // type: char& String::(int)
-
Defining multiple functions with the same name is known as function overloading and is one of the essential parts of generic programming.
-
The size of a type is implementation defined (i.e., it can vary among different machines) and can be obtained by the
sizeof
operator -
A
0b
prefix indicates a binary (base 2) integer literal (e.g., 0b10101010). A0x
prefix indicates a hexadecimal (base 16) integer literal (e.g., 0xBAD1234). A0
prefix indicates an octal (base 8) integer literal (e.g., 0334). -
To make long literals more readable for humans, we can use a single quote (') as a digit separator. For example, π is about 3.14159'26535'89793'23846'26433'83279'50288 or 0x3.243F'6A88'85A3'08D3
-
When defining a variable, you don’t need to state its type explicitly when it can be deduced from the initializer:
auto b = true; // a bool auto ch = 'x'; // a char auto i = 123; // an int auto d = 1.2; // a double auto z = sqrt(y); // z has the type of whatever sqr t(y) retur ns auto bb {true}; // bb is a bool
-
Scope of variables
- local scope
- class scope
- namespace scope
-
const
: The value of a const can be calculated at run time.constexpr
: to be evaluated at compile time, allows placement of data in read-only memory. The value ofconstexpr
must be calculated by the compilerconstexpr int dmv = 17; int var {17}; const double sqv = sqrt(var);
-
constant expressions are required by language rules e.g., array bounds, case labels, template value arguments, and constants declared using constexpr.
-
compile-time evaluation is important for performance.
-
There is no "null reference." A reference must refer to a valid object (and implementations assume that it does).
-
the >> operator ("get from") is used for input. The type of the right-hand operand of >> determines what input is accepted, and its right-hand operand is the target of the input operation.
-
Like a for-statement, an if-statement can introduce a variable and test it. A name declared in a condition is in scope on both branches of the if-statement.
void do_something(vector<int>& v) { if (auto n = v.siz e(); n!=0) { // ... we get here if n!=0 ... } // ... }
-
The new operator allocates memory from an area called the free store (also known as dynamic memory and heap).
-
Objects allocated on the free store are independent of the scope from which they are created and "live" until they are destroyed using the delete operator.
-
Unlike an ordinary function, a constructor is guaranteed to be used to initialize objects of its class. Thus, defining a constructor eliminates the problem of uninitialized variables for a class.
-
A
union
is astruct
in which all members are allocated at the same address so that the union occupies only as much space as its largest member.enum Type { ptr, num }; union Value { Node *p; int i; }; struct Entry { string name; Type t; Value v; // use v.p if t == ptr; use v.i if t == num };
-
The standard library type,
variant
, can be used to eliminate most direct uses of unions. Avariant
stores a value of one of a set of alternative typesstruct Entry { string name; variant<Node *, int> v; }; void f(Entry *pe) { if(holds_alternative<int>(pe->v)) { cout << get<int>(pe->v); } }
-
The problem with unions is that they're very simple and crude. You don't have a way to know what's the currently used type and what’s more they won’t call destructors of the underlying types.
-
The enumerators from a "plain" enum are entered into the same scope as the name of their enum and implicitly converts to their integer value.
using std::swap; // using declaration using namespace std; // using directive
-
A function that should never throw an exception can be declared
noexcept
.void user(int sz) noexcept { Vector v(sz); iota(&v[0], &v[sz], 1); // fill v with 1,2,3,4... // ... }
-
In well-designed code try-blocks are rare. Avoid overuse by systematically using the RAII technique.
-
Compilers are optimized to make returning a value much cheaper than throwing the same value as an exception.
-
terminating the program (by invoking a function like
terminate()
,exit()
, orabort()
). -
do not believe the myth that exception handling is slow; it is often faster than correct handling of complex or rare error conditions, and of repeated tests of error codes.
-
The standard library offers the debug macro,
assert()
, to assert that a condition must hold at run time. -
The default behavior for both argument passing and value return is "copy", but some copies can implicitly be optimized to moves.
-
if the class representation changes in any significant way, a user must recompile. This is the price to pay for having concrete types behave exactly like built-in types.
-
Functions defined in a class are inlined by default. It is possible to explicitly request inlining by preceding a function declaration with the keyword inline.
-
A container is an object holding a collection of elements.
-
A
static_cast
does not check the value it is converting; the programmer is trusted to use it correctly. -
Other casts are
reinterpret_cast
for treating an object as simply a sequence of bytes andconst_cast
for "casting away const." -
The word
virtual
means "may be redefined later in a class derived from this one." -
A class that provides the interface to a variety of other classes is often called a polymorphic type.
-
We can ask "is this Shape a kind of Smiley?"" using the
dynamic_cast
operator:Shape∗ ps {read_shape(cin)}; if (Smiley∗ p = dynamic_cast<Smiley∗>(ps)) { // ... does ps point to a Smiley? ... // ... a Smiley; use it } else { // ... not a Smiley, try something else ... }
-
If at run time the object pointed to by the argument of
dynamic_cast
is not of the expected type (here, Smiley) or a class derived from the expected type,dynamic_cast
returnsnullptr
. -
Assigning the result of new to a ‘‘naked pointer’’ is asking for trouble.
-
If a class X has a destructor that performs a nontrivial task, such as free-store deallocation or lock release, the class is likely to need the full complement of functions:
class X { public: X(Sometype); // "ordinary constructor": create an object X(); // default constructor X(const X&); // copy constructor X(X&&); // move constructor X& operator=(const X&); // copy assignment: clean up target and copy X& operator=(X&&); // move assignment: clean up target and move ˜X(); // destructor: clean up // ... };
-
When a class has a pointer member, it is usually a good idea to be explicit about copy and move operations.
-
=delete
can be used to suppress any function, not just essential member functions -
A constructor taking a single argument defines a conversion from its argument type.
complex z1 = 3.14; // z1 becomes {3.14,0.0} complex z2 = z1∗2; // z2 becomes z1*{2.0,0} == {6.28,0.0}
-
Copying of an object of a class is defined by two members: a copy constructor and a copy assignment.
Vector(const Vector& a); // copy constr uctor Vector& operator=(const Vector& a); // copy assignment
-
The name
this
is predefined in a member function and points to the object for which the member function is called.class Vector { // ... Vector(const Vector& a); // copy constructor Vector& operator=(const Vector& a); // copy assignment Vector(Vector&& a); // move constructor, no const argument Vector& operator=(Vector&& a); // move assignment, no const argument };
-
The && means "rvalue reference" and is a reference to which we can bind an rvalue.
-
The standard-library function
move()
doesn't actually move anything. Instead, it returns a reference to its argument from which we may move – an rvalue reference; it is a kind of cast. -
To give identical treatment to both operands of a binary operator, such as ==, it is best defined as a free-standing function in the namespace of its class.
-
The standard-library
unordered_map<K,V>
is a hash table withK
as the key type andV
as the value type. To use a typeX
as a key, we must definehash<X>
. The standard library does that for us for common types, such asstd::string
. -
A template is a class or a function that we parameterize with a set of types or values.
-
Templates are a compile-time mechanism, so their use incurs no run-time overhead compared to hand-crafted code.
-
A template argument for which a concept is specified is called a constrained argument and a template for which an argument is constrained is called a constrained template.
-
In addition to type arguments, a template can take value arguments. A template value argument must be a constant expression.
template <typename T, int N> struct Buffer { using value_type = T; constexpr int size() { return N; } T[N]; };
-
A function template can be a member function, but not a vir tual member.
-
The function called
operator()
implements the "function call," "call," or "application" operator()
. -
Function objects used to specify the meaning of key operations of a general algorithm are often referred to as policy objects.
-
a lambda with an
auto
parameter makes the lambda a template, or a generic lambda. -
every standard-library container provides
value_type
as the name of its value type -
There is no separate compilation of templates: #include template definitions in every translation unit that uses them.
-
Most template arguments must meet specific requirements for the template to compile properly and for the generated code to work properly. That is, most templates must be constrained templates.
-
The type-name introducer
typename
is the least constraining, requiring only that the argument be a type.
# | header filename | example |
---|---|---|
1 | <algorithm> |
copy(), find(), sort() |
2 | <array> |
array |
3 | <chrono> |
duration, time_point |
4 | <cmath> |
sqrt(), pow() |
5 | <complex> |
complex, sqrt(), pow() |
6 | <filesystem> |
path |
7 | <forward_list> |
forward_list |
8 | <fstream> |
fstream, ifstream, ofstream |
9 | <future> |
future, promise |
10 | <ios> |
hex, dec, scientific, fixed, defaultfloat |
11 | <iostream> |
istream, ostream, cin, cout |
12 | <map> |
map, multimap |
13 | <memory> |
unique_ptr, shared_ptr, allocator |
14 | <random> |
default_random_engine, normal_distribution |
15 | <regex> |
regex, smatch |
16 | <string> |
string, basic_string |
17 | <set> |
set, multiset |
18 | <sstream> |
istringstream, ostringstream |
19 | <stdexcept> |
length_error, out_of_range, runtime_error |
20 | <thread> |
thread |
21 | <unordered_map> |
unordered_map, unordered_multimap |
22 | <utility> |
move(), swap(), pair |
23 | <variant> |
variant |
24 | <vector> |
vector |
-
The standard string has a move constructor, so returning even long strings by value is efficient.
-
These days,
string
is usually implemented using the short-string optimization. That is, short string values are kept in the string object itself and only longer strings are placed on free store. -
string_view
is basically a (pointer,length) pair denoting a sequence of characters. -
In
<reg ex>
, the standard library provides support for regular expressions:regex_match()
: Match a regular expression against a string (of known size)regex_search()
: Search for a string that matches a regular expression in an (arbitrarily long) stream of dataregex_replace()
: Search for strings that match a regular expression in an (arbitrarily long) stream of data and replace themregex_iterator
: Iterate over matches and submatchesregex_token_iterator
: Iterate over non-matches
-
The regular expression syntax and semantics are designed so that regular expressions can be compiled into state machines for efficient execution. The regex type performs this compilation at run time.
-
A suffix
?
after any of the repetition notations(?, ∗, +, and { })
makes the pattern matcher "lazy" or "non-greedy." That is, when looking for a pattern, it will look for the shortest match rather than the longest. By default, the pattern matcher always looks for the longest match; this is known as the Max Munch rule. -
The I/O stream classes all have destructors that free all resources owned (such as buffers and file handles).
-
You can read a whole line using the
getline()
function:std::getline(std::cin, line);
-
For a
list
,insert(p, elem)
inserts an element with a copy of the valueelem
before the element pointed to byp
. Here,p
may be an iterator pointing one-beyond-the-end of the list. -
The standard library offers a balanced binary search tree (usually, a red-black tree) called map.
-
The cost of a
map
lookup isO(log(n))
wheren
is the number of elements in themap
. However, in many cases, we can do better by using a hashed lookup rather than a comparison using an ordering function, such as <.struct Record { string name; int product_code; // ... }; struct Rhash { // a hash function for Record size_t operator()(const Record& r) const { return hash<string>()(r.name) ˆ hash<int>()(r.product_code); } }; unordered_set<Record,Rhash> my_set; // set of Records using Rhash for lookup
-
Designing good hash functions is an art and sometimes requires knowledge of the data to which it will be applied. Creating a new hash function by combining existing hash functions using exclusive-or (ˆ) is simple and often very effective.
-
An empty
forward_list
occupies just one word, whereas an emptyvector
occupy three. -
Use
reserve()
to avoid invalidating pointers and iterators to elements. -
An iterator is an object of some type.
-
A predicate should not modify the elements to which it is applied.
-
std::move()
doesn't move anything. Instead, it casts its argument to an rvalue reference, thereby saying that its argument will not be used again and therefore may be moved.template <typename T> void swap(T& a, T& b) { T tmp {move(a)}; // the T constructor sees an rvalue and moves a = move(b); // the T assignment sees an rvalue and moves b = move(tmp); // the T assignment sees an rvalue and moves }
-
an
array
can be allocated with its elements on the stack, in an object, or in static storage. -
If we try to access an
optional
that does not hold a value, the result is undefined; an exception is not thrown. Thus,optional
is not guaranteed type safe. -
Use
std::mem_fn()
to make a function object from a member function. -
The standard-library
function
is a type that can hold any object you can invoke using the call operator()
. That is, an object of typefunction
is a function object. -
A random number generator consists of two parts:
- An engine that produces a sequence of random or pseudo-random values
- A distribution that maps those values into a mathematical distribution in a range
-
The standard library directly supports concurrent execution of multiple threads in a single address space.
-
We call a computation that can potentially be executed concurrently with other computations a task. A thread is the system-level representation of a task in a program.
-
A task is a function or a function object.
-
To "join" a thread means to "wait for the thread to terminate."
-
This
scoped_lock
will proceed only after acquiring all its mutexes arguments and will never block ("go to sleep") while holding a mutex. -
The basic
mutex
allows one thread at a time to access data. One of the most common ways of sharing data is among many readers and a single writer. This "reader-writer lock" idiom is supported beshared_mutex
. A reader will acquire the mutex "shared" so that other readers can still gain access, whereas a writer will demand exclusive access.shared_mutex mx; // a mutex that can be shared void reader() { shared_lock lck {mx}; // willing to share access with other readers // ... read ... } void writer() { unique_lock lck {mx}; // needs exclusive (unique) access // ... write ... }
-
The basic support for communicating using external events is provided by
condition_variables
. Acondition_variable
is a mechanism allowing one thread to wait for another. In particular, it allows a thread to wait for some condition (often called an event) to occur as the result of work done by other threads.
-
object-oriented techniques using classes and virtual functions (run time polymorphism), generic programming techniques using templates (compile time polymorphism).
-
run-time dispatch or dynamic dispatch.
-
C++ can also be used as a traditional, imperative programming language.
-
Generic programming is a way of developing software that maximizes code reuse in a way that does not sacrifice performance.
-
Generic programming is programming based on parameterization.
-
C++ without virtual is not OO. Programming with classes but without dynamic binding is called "object based," but not "object oriented."
-
With OO and generic programming, reuse can also be accomplished by having old code call new code.
-
In C++
f()
declares a function that takes no parameters (in C, a function declared usingf()
can be passed an arbitrary number of parameters of arbitrary types). -
There are some very subtle differences as well, like
sizeof('x')
is equal tosizeof(char)
in C++ but is equal tosizeof(int)
in C. Also, C++ puts structure "tags" in the same namespace as other names, whereas C requires an explicitstruct
. -
"undefined", "unspecified", "implementation defined", and "well-formed"; see the ISO C++ standard.
-
Software Development Is Decision Making.
-
C++ guaranteed minimum sizes
-
C++ guarantees
- a char is exactly one byte which is at least 8 bits,
- short is at least 16 bits,
- int is at least 16 bits, and
- long is at least 32 bits.
-
It also guarantees the unsigned version of each of these is the same size as the original
#include <stdint.h> /* not part of the C++ standard */
class Fred { static const int max_ = 107; }; // add the following line in exactly one .cpp file int Fred::max_;
-
Every
#define
macro effectively creates a new keyword in every source file and every scope until that symbol is#undefd
. The preprocessor lets you create a#define
symbol that is always replaced independent of the {...} scope where that symbol appears. -
the goal of all programming rules is to reduce time, cost and risk. If a rule actually makes things worse, it is a bad rule, period.
-
Mixing signed and unsigned values in a single arithmetic expression is often confusing for programmers — the compiler doesn’t always do what you expect it should do.
-
programmers should choose between !A && !B and !(A || B) based on which one is more obvious to whoever will be maintaining the code.
inline bool my_isnan(double x) { return x != x; }
-
The headers in ISO Standard C++ don't have a
.h
suffix. -
The C++ standard library is guaranteed to have 18 standard headers from the C language.
-
<cxxx>
versions provide their declarations in thestd
namespace only, and the<xxx.h>
versions make them available both instd
namespace and in the global namespace. -
The C++ standard library is also guaranteed to have 32 additional standard headers that have no direct counterparts in C.
-
using std::cout;
// a using-declaration that lets you use cout without qualification -
Code must be written to be read, not by the compiler, but by another human being.
-
When initializing an object's member objects in the constructor, always use initialization lists rather than assignment. The performance difference for user-defined classes can be substantial (3x!)
-
Instead of global variables, using static data member or use unnamed namespace.
class Foo { static int xyz; }; namespace { int xyz; }
-
there are three major considerations: visibility, lifetime, and thread safety.
-
templates go beyond that, and support generic programming, template metaprogramming, etc. through a combination of features such as integer template arguments, specialization, and uniform treatment of built-in and user-defined types.
-
a less desirable result of C++'s template flexibility is late detection of errors and horrendously bad error messages.
-
C++11 adopted all of C99's preprocessor extensions and library extensions, but not C99's new language features, so language features like the
restrict
keyword that were added in C99 are generally not part of ISO C++. -
Calling an undeclared function is poor style in C and illegal in C++.
-
In C, you can implicitly convert a
void*
to aT*
. -
a C compiler won't understand the
extern "C"
construct, you must wrap theextern "C" {
and}
lines in an#ifdef
so they won't be seen by normal C compilers.#ifdef __cplusplus extern "C" { #endif .... #ifdef __cplusplus } #endif
-
the symbol
__cplusplus
is#defined
if and only-if the compiler is a C++ compiler. -
The
extern "C"
line tells the compiler that the external information sent to the linker should use C calling conventions and name mangling -
C++ allows you to model the problem domain itself, which allows you to program in the language of the problem domain rather than in the language of the solution domain.
-
The C++ language gives you a way to find out how many bits are in a byte in your particular implementation: include the header
<climits>
, then the actual number of bits per byte will be given by theCHAR_BIT
macro. -
C++ language requires that your operator overloads take at least one operand of a "class type" or enumeration type.
-
the stream
std::cin
goes into a "failed state," and all subsequent input attempts return immediately without doing anything. -
In text mode, end-of-line sequences and possibly other things are translated; in binary mode, they are not.
-
you can never reseat a reference to make it refer to a different object.
-
const overloading helps you achieve const correctness.
-
const overloading is when you have an inspector method and a mutator method with the same name and the same number of and types of parameters. The two distinct methods differ only in that the inspector is const and the mutator is non-const.
-
subscript operators often come in pairs.
const Fred& operator[] (unsigned index) const; Fred& operator[] (unsigned index);
-
delete p
delete the pointed-to-data*p
-
it is not safe to delete the same pointer twice
Foo *p = new Foo; delete p; delete p; // DISASTER!!
-
The C++ language guarantees that
delete p
will do nothing if p isnull
. -
with the Named Constructor Idiom, the constructors are all private or protected, and there are one or more
public static create()
methods (the so-called "named constructors"), -
In C, encapsulation was accomplished by making things static in a compilation unit or module
-
encapsulation is for code, not people.
-
The fact that C++ allows a class's methods and friends to access the non-public parts of all its objects, not just the this object.
-
Construct On First Use idiom
Encapsulate what varies Favor Composition over inheritance Program to Interfaces, not implementations Strive for loosely coupled designs between objects that interact
Design Principle: Identify the aspects of your application that vary and separate them from what stays the same.
All patterns provide a way to let some part of a system vary independently of all other parts.