This section contains notes about some of the most useful things from the std
namespace. The C++ standard library also contains the entire C standard library, each header being available through <c*>
, e.g. <cstdlib>
, which in C is equivalent to <stdlib.h>
, and <cmath>
, which in C is equivalent to <math.h>
.
See all C++ Standard Library headers.
<string>
[TODO]
There is no built-in string type in C++, but the standard library provides std::string
in <string>
. Note that string literals on the other hand are part of the language itself, their data type is const char*
.
The set of methods available to the std::string
class is similar to the methods available to std::vector
, plus a few more special string manipulation methods and operator support like +
, <<
, >>
.
Raw Strings:
There are raw string literals just like in Python where everything inside the string is treated as raw characters, not special characters. This means you wonβt have to escape any special characters with backslash and theyβll all lose their meaning. This is especially useful when defining strings containing regex patterns which contain a bunch of backslashes.
The format for defining a raw string literal is: .
- String formatting with
std::stringstream
from<sstream>
str.find_first_of
std::string::npos
std::string_view vs std::string
-
A
string_view
is nothing but a pointer to a string and a length. It serves as basically a read-only substring of an underlying string. -
string_view
can offer better performance thanstring
. -
Why not just use
const string&
? Because that has to refer to astd::string
exactly, not a char array,const char*
,vector<char>
etc., which means that a newstd::string
instance must be created from these other βsequence of charβ formats, which is potentially expensive. -
You can do
constexpr std::string_view s = "β¦"
, but notconstexpr std::string s = "β¦"
-
You can get string literals of type
std::string
by suffixing a regular string literal withs
, e.g."Hello"s
. -
You can get
string_view
literals by suffixing string literals withsv
, e.g."Hello"sv
.
STL Containers
The STL (standard template library) contains highly efficient generic data structures and algorithms. The STL encompasses many headers like: <array>
, <stack>
, <vector>
, etc.
<vector>
βVectorβ isnβt the best name. It should be called βArrayListβ or βDynamicArrayβ. It is implemented with an array under the hood.
- To resize the underlying array, a larger memory block is allocated and all items in the original array are copied over to the new larger one. This is an operation.
- Vectors consume more memory than arrays, but offers methods for runtime resizing.
Slicing and splicing:
<set>
Stores elements in sorted order without duplicates. For most use cases, you probably want to use unordered_set
instead, which has more favourable time complexities.
- The underlying implementation uses a balanced tree.
<unordered_set>
Same as std::set
from <set>
, just unordered.
- Uses a hash table as the underlying data structure.
<map>
A data structure mapping a set of keys to values. This one maintains order of keys. If this is irrelevant (which it usually is), use unordered_map
instead.
- Implemented with a self-balancing binary search tree.
- There are multiple ways to insert key-value pairs into a map, eg.
insert()
,[ ]
operator,emplace()
, etc.
Usage example:
<unordered_map>
The interface is very similar to std::map
from <map>
, however it offers a few more lower-level methods like bucket_count()
, load_factor()
, etc.
<array>
- To get the size of an array, youβd need to do . It is almost always recommended to use
std::vector
over regular arrays.
<stack>
<queue>
<tuple>
<algorithm>
[TODO]
I/O [TODO]
In computer science, a stream is an abstraction that represents a sequence of data that arrives over time (much like a conveyor belt delivering items). In C++, this stream abstraction is how we work with reading/writing characters coming from an input stream (eg. user input on the terminal or a file in read mode) or being written to an output stream (eg. the terminal or a file in write mode).
ostream
An ostream
serialises typed values as bytes and dumps them somewhere.
- The βput toβ operator
<<
is used on objects of typeostream
. std::cout
andstd::err
are both objects of typeostream
.
-
You can chain the put-to operator
<<
because the result of an expression likecout << βHelloβ
is itself anostream
. -
You can overload the << operator for your own classes. Example
iomanip
[TODO]
istream
An istream
takes in bytes and converts it to typed values.
- The βget fromβ operator
>>
is used as an input operator std::cin
is the standard input stream
-
Formatted extraction: The type of the RHS of the βget fromβ operator determines what input is accepted
-
You can chain the get-from operator
>>
just like for put-to<<
. -
The user input can be space-separated, new-line-separated or tab-separated integers. There can be any number of β β, β\nβ, β\tβ characters between the integers
-
If what the user types in cannot be casted to the expected type, nothing happens. The program continues execution and the variable ends up being uninitialised
-
-
Unformatted line extraction with
std::getline
When you want to read an entire line up to and not including the newline character, you should usegetline
rather than directly read fromcin
(which always considers space characters β β, β\nβ, β\tβ to be terminating) -
Common pitfall: when you do formatted execution followed by unformatted extraction, youβll skip over the unformatted extraction. This is fixed with
std::cin.ignore()
Suppose you have:
You are actually typing β10\nβ for the first input prompt. The β\nβ unfortunately remains in the buffer when we get to the next
getline
call, which terminates immediately upon seeing the newline, thereby skipping input extract.To solve this, you need to call
std::cin.ignore()
to skip over the newline.std::cin >> age; std::cin.ignore(); std::getline(std::cin, name);
File Manipulation (fstream
)
The fstream.h
header defines ifstream
, which you use to open a file in read mode, ofstream
, which you use to open a file in write mode, and fstream
which you can use to create, read and write to files.
String Streams (sstream
)
String streams let you treat instances of std::string
as stream objects, letting you work with them in the same way that youβd work with cin
, cout
of file streams.
Filesystem
C++17 gives us the std::filesystem
API which finally lets us basically do ls
on directories and traverse the filesystem, create symbolic links, get file stats, etc.
Memory
<memory>
<memory>
provides two smart pointers: unique_ptr
and shared_ptr
, for managing objects allocated on the heap.
Use smart pointers whenever you need pointer semantics. The main time you do is when you want to make use of a polymorphic object. You do not need pointer semantics when returning things from a function because that will be handled by copy and move (furthermore, copy elision ensures no unnecessary copies).
unique_ptr
By giving a pointer to unique_ptr
, we can have confidence that when that unique_ptr
goes out of scope, the object it tracks gets deallocated in the destructor of unique_ptr
.
- Itβs recommended to use
make_unique<Foo>(...).
instead ofunique_ptr<T>(new Foo(...))
, mainly so you can completely eliminate the usage of nakednew
anddelete
s. - Since
unique_ptr
represents sole ownership, its copy constructor and assignment operation are disabled. You can use move semantics to transfer theunique_ptr
from one variable to another. - You can pass and return
unique_ptr
s in functions (by value).- Wait, why is this allowed when the copy operation is disabled? Basically, it is guaranteed for either copy elision to happen or for
std::move
to be implicitly called on the return value. Either way, the caller ofmake_foo
is guaranteed sole ownership.βThe part of the Standard blessing the RVO goes on to say that if the conditions for the RVO are met, but compilers choose not to perform copy elision, the object being returned must be treated as an rvalue. In effect, the Standard requires that when the RVO is permitted, either copy elision takes place orΒ
std::move
Β is implicitly applied to local objects being returned.β β Scott Meyers. - Always return smart pointers by value.
- Wait, why is this allowed when the copy operation is disabled? Basically, it is guaranteed for either copy elision to happen or for
βThe code using
unique_ptr
will be exactly as efficient as code using the raw pointers correctly.β β Bjarne Stroustrup, A Tour of C++.
shared_ptr
Similar to unique_ptr
, but shared_ptr
s get copied instead of moved. The object held by shared_ptr
is deleted only when no other shared_ptr
points at it.
- Prefer
make_shared
over directly constructingshared_ptr
and usingnew
.
weak_ptr
[TODO]
When you assign std::shared_ptr
to a variable of type std::weak_ptr
, it wonβt increment the underlying references count managed by the shared_ptr
.
Concurrency
<thread>
-
Simple full example
<mutex>
std::mutex
is a very simple lockable object used to synchronise access to a resource shared by parallel threads.
-
Race condition example and how to solve it with mutexes
The following is the output of running the program. You can see the lines being printed are also jumbled because
cout
is also a βresourceβ being accessed by both threads. We need to lock access tocount
andcout
.Solution:
<future>
[TODO]
async
[TODO]
Utilities
<regex>
Note: using raw string literals, , makes writing regex patterns easier because you wonβt be confused about backslashes escaping things that you didnβt mean to escape.