C++: Difference between revisions
No edit summary |
|||
(190 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
__FORCETOC__ | __FORCETOC__ | ||
C++ is a very popular and powerful language which includes all the low-level features of [[C_(programming_language) | C]] (e.g. pointers, operator overloading) along many high-level features (RAII, STD algorithms, STL containers) thanks to the C++ standard library. | |||
==Usage== | |||
How to do things using the [https://en.wikipedia.org/wiki/C%2B%2B_Standard_Library C++ standard library (stdlib)]. | |||
===Compilation=== | |||
{{See also|CMake|Makefile}} | |||
====cmake==== | |||
====g++==== | |||
<syntaxhighlight lang="bash"> | |||
g++ my_driver.c [-Iincludefolder] -o my_program.out | |||
</syntaxhighlight> | |||
Standard optimizations | |||
* <code>-std=c++17</code> for C++17 support | |||
* <code>-O3</code> for level 3 optimizations | |||
* <code>-g</code> to include debugging info | |||
* <code>-march=native</code> - use all instructions available on the current CPU | |||
* <code>-mtune=native</code> - optimize for the current CPU | |||
== | ===Syntax=== | ||
=== Sleep === | ====Main==== | ||
< | All C++ programs launch in a <code>main</code> function. | ||
Similar to [[C (programming language) | C]], the arguments are <code>int argc</code> and <code>char *argv[]</code>.<br> | |||
These can be easily converted to a <code>std::vector<std::string></code> for convenience. | |||
<syntaxhighlight lang="cpp"> | |||
#include <string> | |||
#include <vector> | |||
int main(int argc, char *argv[]) { | |||
std::vector<std::string> args(argv, argv + argc); | |||
// Your code here | |||
return EXIT_SUCCESS; | |||
} | |||
</syntaxhighlight> | |||
====Headers==== | |||
[https://stackoverflow.com/questions/10694255/cmath-vs-math-h-and-similar-c-prefixed-vs-h-extension-headers Reference] | |||
C++ includes C-headers such as <code>math.h</code> and <code>cmath</code>.<br> | |||
The C-style header will place everything in the global namespace while the C++ header will place everything in <code>std</code>.<br> | |||
You should use <code>cmath</code>. | |||
====Lambda Expressions==== | |||
[https://en.cppreference.com/w/cpp/language/lambda Reference] | |||
====Casting==== | |||
[https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used Types of casts] | |||
C++ has several types of casts including: | |||
* [https://en.cppreference.com/w/cpp/language/static_cast <code>static_cast</code>] - your standard cast with conversion. Does not perform any checks. | |||
* [https://en.cppreference.com/w/cpp/language/dynamic_cast <code>dynamic_cast</code>] - for casting objects with checking, requires a polymorphic base class (with a virtual function). Will return nullptr. | |||
* [https://en.cppreference.com/w/cpp/language/reinterpret_cast <code>reinterpret_cast</code>] - cast without any conversion, for directly dealing with binary data, equivalent to <code>*(T*)</code> in C. | |||
====References==== | |||
References are accepted or store using <code>&</code>.<br> | |||
For example: | |||
<syntaxhighlight lang="c++"> | |||
void healPerson(Person &person) { | |||
person.health = 100; | |||
} | |||
</syntaxhighlight> | |||
References are like pointers since they do not copy the object except they cannot be null and they cannot be reassigned.<br> | |||
Note that primitives can also be used with references, in which case changes will propagate to the underlying value.<br> | |||
You can also use them as class attributes, initializing them in the constructor's initializer list.<br> | |||
To store references in a vector, you can use <code>std::reference_wrapper</code> and include the <code>functional</code> header. | |||
====Types==== | |||
For simple programs, you can use the standard types: | |||
* <code>int</code>, <code>uint</code>, <code>long</code>, <code>size_t</code> | |||
* <code>float</code>, <code>double</code> | |||
See [https://stackoverflow.com/questions/6462439/whats-the-difference-between-long-long-and-long SO] for the standard and guaranteed precision of these built-in types. | |||
C++ also has fixed-width types in <code>#include <cstdint</code> (since C++11).<br> | |||
[https://en.cppreference.com/w/cpp/header/cstdint cppreference cstdint]<br> | |||
I recommend using these for anything with specific or high precision requirements.<br> | |||
Typically, I use: | |||
* <code>uint8_t</code> instead of <code>char</code> or <code>std::byte</code>.<br> | |||
* <code>int64_t</code> instead of <code>long long</code> | |||
===String=== | |||
<code>#include <string></code><br> | |||
If you don't need to own the string, prefer to use <code>string_view</code>. | |||
<syntaxhighlight lang="cpp"> | |||
// c-str to string | |||
char *old_string = "my c-style string"; | |||
string cpp_string(old_string); | |||
// string to c-str | |||
cpp_string.c_str(); | |||
// char to string | |||
char my_char = 'a'; | |||
string my_str(1, my_char); | |||
</syntaxhighlight> | |||
====String Interpolation==== | |||
[https://stackoverflow.com/questions/10410023/string-format-alternative-in-c Reference] | |||
<syntaxhighlight lang="C++"> | |||
#include <iostream> | |||
#include <sstream> | |||
#include <string> | |||
int main() { | |||
std::string a = "a", b = "b", c = "c"; | |||
// apply formatting | |||
std::stringstream s; | |||
s << a << " " << b << " > " << c; | |||
// assign to std::string | |||
std::string str = s.str(); | |||
std::cout << str << "\n"; | |||
} | |||
</syntaxhighlight> | |||
====Buildings Strings==== | |||
[https://www.fluentcpp.com/2017/12/19/build-strings-from-plain-string-up-to-boost-karma/ The Complete Guide to Building Strings In C++]<br> | |||
There are multiple ways of buildings strings in C++.<br> | |||
Strings are mutable in C++.<br> | |||
I typically use <code>+</code> or <code>ostringstream</code> to build strings. | |||
====std::basic_string_view==== | |||
[https://en.cppreference.com/w/cpp/string/basic_string_view std::basic_string_view] | |||
This is useful for writing functions which accept anything that looks like a string such as substrings, since typically <code>std::string::substr</code> performs a copy. | |||
Note that <code>std::string_view</code> is <code>std::basic_string_view<char></code>. | |||
===Filesystem=== | |||
<code>#include <filesystem></code><br> | |||
Convenient functions for filesystem. Added since C++17. | |||
====Path==== | |||
[https://en.cppreference.com/w/cpp/filesystem/path cppreference] | |||
Note if you use g++ <= version 9, you will need to add the flag <code>-lstdc++fs</code>. | |||
<syntaxhighlight lang="cpp"> | |||
using std::filesystem::path; | |||
// Initialization | |||
path my_path = "my_dir/my_file"; | |||
// or my_path = path("my_dir") / "my_file"; | |||
// Append to path | |||
path("foo") / "bar"; // path("foo/bar") | |||
path("foo") / "/bar"; // path("/bar") | |||
// Print | |||
std::cout << my_path << std::endl; // prints "my_dir/my_file" with quotes | |||
std::cout << my_path.string() << std::endl; // prints my_dir/my_file without quotes | |||
</syntaxhighlight> | |||
;Notes | |||
* <code>path</code> supports implicit conversion to <code>string</code> | |||
====Directories==== | |||
* [https://en.cppreference.com/w/cpp/filesystem/create_directory <code>create_directory(path)</code> and <code>create_directories(path)</code>] | |||
* [https://en.cppreference.com/w/cpp/filesystem/directory_iterator <code>directory_iterator(path)</code>] | |||
;Notes | |||
* <code>create_directory</code> requires that the parent directory already exists | |||
** If not, use <code>create_directories</code> instead | |||
===Fstream=== | |||
<code>#include <fstream></code><br> | |||
Used for input/output of files | |||
====Reading and Writing==== | |||
Reading and writing is done using <code>fstream</code>.<br> | |||
If you don't need r/w, use <code>istream</code> for reading or <code>ostream</code> for writing.<br> | |||
<syntaxhighlight lang="C++"> | |||
#include <iostream> | |||
#include <fstream> | |||
int main() { | |||
std::ifstream my_file("my_file.txt"); | |||
std::string line; | |||
// Read line by line | |||
// You can also read using << | |||
while (getline(my_file, line)) { | |||
std::cout << line << std::endl; | |||
} | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
====Reading a whole file==== | |||
[https://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html Reference and comparison of different methods] | |||
<syntaxhighlight lang="C++"> | |||
#include <cerrno> | |||
#include <fstream> | |||
#include <string> | |||
#include <string_view> | |||
std::string get_file_contents(std::string_view filename) { | |||
std::ifstream in(filename, std::ios::in | std::ios::binary); | |||
if (in.good()) { | |||
std::string contents; | |||
in.seekg(0, std::ios::end); | |||
contents.resize(static_cast<unsigned int>(in.tellg())); | |||
in.seekg(0, std::ios::beg); | |||
in.read(&contents[0], contents.size()); | |||
return contents; | |||
} | |||
std::cerr << "Failed to open file: " << filename << std::endl; | |||
throw(errno); | |||
} | |||
</syntaxhighlight> | |||
===Regular Expressions=== | |||
<code>#include <regex></code> | |||
[https://en.cppreference.com/w/cpp/regex Reference] | |||
<!-- | |||
<syntaxhighlight lang="C++> | |||
#include <iostream> | |||
#include <regex> | |||
int main() { | |||
std::regex myRegex("(\\d+)"); | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
--> | |||
===Thread=== | |||
<code>#include <thread></code><br> | |||
[https://en.cppreference.com/w/cpp/thread/thread std::thread reference]<br> | |||
Basic Usage: | |||
<syntaxhighlight lang="C++"> | |||
std::thread my_thread(thread_function); | |||
// Calling methods | |||
// You can also pass in parameters as usual | |||
std::thread my_thread(&Class::method, this)); | |||
// Lambda functions | |||
std::thread my_thread([&]() { | |||
// do something | |||
}); | |||
// Wait for thread to finish | |||
my_thread.join(); | |||
// get id of thread | |||
std::thread::id my_id = my_thread.get_id(); | |||
// get id of this thread | |||
std::thread::id my_id = std::this_thread::get_id(); | |||
</syntaxhighlight> | |||
==== Sleep ==== | |||
<syntaxhighlight lang="C++"> | |||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
</ | </syntaxhighlight > | ||
====Parallel For==== | |||
[https://www.alecjacobson.com/weblog/?p=4544 Reference] | |||
===Memory=== | |||
<code>#include <memory></code><br> | |||
====Smart Pointers==== | |||
[https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/ Smart Pointers]<br> | |||
Smart pointers were added in C++11.<br> | |||
There are 3 types of smart pointers: | |||
* [https://en.cppreference.com/w/cpp/memory/unique_ptr <code>std::unique_ptr</code>] - one piece of code ''owns'' the memory at any given time.<br> | |||
* <code>std::shared_ptr</code> - the memory has multiple owners. | |||
* <code>std::weak_ptr</code> - a non-owning reference to a shared_ptr. | |||
In general, there should be one object owning an object using a <code>unique_ptr</code>. Whenever you pass the value around, other functions should receive the object as a reference making it clear that they do not have ownership of the object. Smart pointers are nullable and assignable similar to regular pointers. | |||
Prefer to use <code>make_unique</code> or <code>make_shared</code> which will only make one memory allocation for both the object and the pointer rather than two memory allocations.<br> | |||
You can call <code>my_ptr.reset(new Car())</code> to change the pointer or <code>my_ptr.reset()</code> to deallocate the object referenced by the pointer. | |||
Example: | |||
<syntaxhighlight lang="cpp"> | |||
// Block-scope car | |||
Car my_car; | |||
// Old C++ | |||
// Must call delete my_car; to avoid memory leaks. | |||
Car *my_car = new Car(); | |||
// Using unique ptr | |||
std::unique_ptr<Car> my_car(new Car()); | |||
// Or starting from C++14 | |||
auto my_car = std::make_unique<Car>(); | |||
</syntaxhighlight> | |||
;Notes | |||
* If the object you need is not very large, you can consider just including it as part of your class (or leaving it on the stack) rather than use pointers. | |||
* If you want to get a copy of the smart pointer to the current object, the object must '''publically''' inherit <code>std::enable_shared_from_this<T></code> | |||
** Then you can call <code>shared_from_this()</code> from within any method (not the constructor). | |||
** May throw <code>bad_weak_ptr</code> if you call <code>shared_from_this()</code> without <code>make_shared</code> or if you do not publically inherit <code>std::enable_shared_from_this<T></code> | |||
* When writing functions when do not operate on pointers and do not claim ownership of objects, you should just take a reference to the object as the argument. | |||
* <code>std::auto_ptr</code> was a predecessor to <code>std::unique_ptr</code> which allowed copies. It shouldn't be used anymore. | |||
====Garbage Collection==== | |||
Starting from C++11, you should use smart pointers such as [https://en.cppreference.com/w/cpp/memory/shared_ptr <code>shared_ptr</code>] which have automatic garbage collection.<br> | |||
<br> | |||
Traditional C++ does not have garbage collection.<br> | |||
After using <code>new</code> to allocate an object, use <code>delete</code> to deallocate it. <br> | |||
You can also use C allocation with <code>malloc</code>, <code>calloc</code>, <code>alloca</code>, and <code>free</code>, though it is [https://stackoverflow.com/questions/184537/in-what-cases-do-i-use-malloc-and-or-new not recommended] since these are not type-safe.<br> | |||
====Custom Deleter==== | |||
[https://www.bfilipek.com/2016/04/custom-deleters-for-c-smart-pointers.html Custom Deleters]<br> | |||
When using smart pointers, the default deleter is the <code>delete</code> function but you can also specify your own deleter. | |||
<syntaxhighlight lang="cpp"> | |||
# Using a functor | |||
struct AVFrameDeleter { | |||
void operator()(AVFrame *p) { av_frame_free(&p); } | |||
}; | |||
std::unique_ptr<AVFrame, AVFrameDeleter> rgb_frame(av_frame_alloc()); | |||
# Using free | |||
std::unique_ptr<void *, decltype(std::free) *> my_buffer(std::malloc(10), std::free); | |||
</syntaxhighlight> | |||
====Deallocate==== | |||
Normally, containers such as <code>std::vector</code> will automatically deallocate memory from the heap when the destructor is called. However, occationally you may want to coerse this deallocation yourself.<br> | |||
There are a few ways to do this: | |||
* Use smart pointers | |||
* Swap | |||
* Call a clear/shrink/deallocate function | |||
Example [https://stackoverflow.com/questions/3054567/right-way-to-deallocate-an-stdvector-object Reference]: | |||
<syntaxhighlight lang="cpp"> | |||
// Using smart pointers | |||
std::unique_ptr<std::vector<float>> my_vector = make_unique<std::vector<float>>(99); | |||
my_vector.reset(); | |||
// Swap | |||
std::vector<float> my_vector(99); | |||
my_vector = std::vector<float>; | |||
// Or alternatively | |||
// std::vector<float>().swap(my_vector); | |||
// std::swap(my_vector, std::vector<float>); | |||
// Swap for cl::Buffer | |||
cl::Buffer my_buf(context, CL_MEM_READ_WRITE, size); | |||
my_buf = cl::Buffer(); | |||
// Clear and shrink | |||
// Specific to std::vector | |||
std::vector<float> my_vector(99); | |||
my_vector.clear(); | |||
my_vector.shrink_to_fit(); | |||
</syntaxhighlight> | |||
===Limits=== | |||
<code>#include <limits></code><br> | |||
[https://en.cppreference.com/w/cpp/types/numeric_limits Reference]<br> | |||
C++ has standard macros such as <code>INT_MAX</code>.<br> | |||
The limits header adds these limits for every type.<br> | |||
<syntaxhighlight lang="cpp"> | |||
// Equivalent to FLT_MAX | |||
std::numeric_limits<float>::max(); | |||
</syntaxhighlight> | |||
===Utility=== | |||
<code>#include <utility></code><br> | |||
====std::move==== | |||
[https://en.cppreference.com/w/cpp/utility/move Ref]<br> | |||
Use <code>std::move</code> to move containers. | |||
===Algorithm=== | |||
====std::find==== | |||
[https://en.cppreference.com/w/cpp/algorithm/find Reference]<br> | |||
====std::generate==== | |||
[https://en.cppreference.com/w/cpp/algorithm/generate cppreference]<br> | |||
Allows you to fill a container using a function call<br> | |||
<syntaxhighlight lang="cpp"> | |||
#include <random> | |||
#include <iostream> | |||
#include <algorithm> | |||
int main() | |||
{ | |||
std::random_device rd; | |||
std::mt19937 gen(rd()); | |||
# Fill with integers in [0, 10] | |||
std::uniform_int_distribution<> dis(0, 10); | |||
std::vector<int> my_vec(10, 0); | |||
std::generate(my_vec.begin(), my_vec.end(), [&](){return dis(gen);}); | |||
for (int v : my_vec) { | |||
std::cout << v << " "; | |||
} | |||
std::cout << std::endl; | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
===Numeric=== | |||
====std::iota==== | |||
[https://en.cppreference.com/w/cpp/algorithm/iota Reference]<br> | |||
Fills an array or vector with increasing values. Can pass in a starting number. | |||
<syntaxhighlight lang="cpp"> | |||
std::vector<int> v(60); | |||
std::iota(v.begin(), v.end(), 0); | |||
</syntaxhighlight> | |||
====std::accumulate==== | |||
Adds up numbers. Can pass in a starting number. | |||
<syntaxhighlight lang="cpp"> | |||
std::vector<int> v(60); | |||
std::iota(v.begin(), v.end(), 0); | |||
std::accumulate(v.begin(), v.end(), 0); | |||
</syntaxhighlight> | |||
===Chrono=== | |||
<code>#include <chrono></code><br> | |||
I now prefer using <code>absl::Time</code> and <code>absl::Duration</code> over Chrono because they abstract away the underlying type. | |||
<syntaxhighlight lang="cpp"> | |||
auto start = std::chrono::high_resolution_clock::now(); | |||
// do something | |||
auto end = std::chrono::high_resolution_clock::now(); | |||
std::cout << "Time elapsed: " | |||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() | |||
<< " ms" << std::endl; | |||
</syntaxhighlight> | |||
===Execution=== | |||
<code>#include <execution></code><br> | |||
The execution header gives you tools for parallel execution (since C++17).<br> | |||
See [https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t execution_policy_tag].<br> | |||
[https://devblogs.microsoft.com/cppblog/using-c17-parallel-algorithms-for-better-performance/ C++17 Parallel Algorithms blog].<br> | |||
[https://developer.nvidia.com/blog/accelerating-standard-c-with-gpus-using-stdpar/ Nvidia Accelerating Standard C++ with GPUs Using stdpar]<br> | |||
;Parallel Sorting Example | |||
<syntaxhighlight lang="cpp"> | |||
std::sort(std::execution::par_unseq, sorted.begin(), sorted.end()); | |||
</syntaxhighlight> | |||
* <code>std::execution::seq</code> sequential | |||
* <code>std::execution::unseq</code> vectorized only (C++20) | |||
* <code>std::execution::par</code> parallel | |||
* <code>std::execution::par_unseq</code> parallel and vectorized | |||
===Random=== | |||
<code>#include <random></code><br> | |||
[https://en.cppreference.com/w/cpp/header/random cppreference.com]<br> | |||
<syntaxhighlight lang="cpp"> | |||
std::random_device rd; //Will be used to obtain a seed for the random number engine | |||
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() | |||
std::uniform_int_distribution<> dis(1, 6); | |||
for (int n=0; n<10; ++n) | |||
//Use dis to transform the random unsigned int generated by gen into an int in [1, 6] | |||
std::cout << dis(gen) << ' '; | |||
std::cout << '\n'; | |||
</syntaxhighlight> | |||
===const=== | |||
For variables: | |||
# Use <code>constexpr</code> for values initialized at compile time and won't change at runtime. Most of the time, this is what you want for hardcoded compile time parameters. | |||
# Use <code>const</code> for values initialized at runtime and won't change. | |||
# Use <code>constinit</code> for values initialized at compile time and may change at runtime. I haven't found a use case for this yet. | |||
For functions: | |||
# Add <code>const</code> to the end of a method declaration if it won't change the object. | |||
# Add <code>constexpr</code> if the function can be evaluated at compile time, i.e. can accepts and output <code>constexpr</code> variables. | |||
# Add <code>consteval</code> if you want to force the function to only be evaluated at compile time. | |||
==STL== | |||
STL is the Standard Template Library originally implemented in 1994 by Stepanov and Lee from HP.<br> | |||
STL consists of a general set of algorithms, containers, functions, and iterators.<br> | |||
Today, STL refers to those containers and algorithms which are now built into the standard library (std) of C++. | |||
===Simple Containers=== | |||
====std::pair==== | |||
[https://en.cppreference.com/w/cpp/utility/pair std::pair] | |||
===Sequences=== | |||
====std::array==== | |||
<code>#include <array></code><br> | |||
This wrapper around C-style arrays gives us size information and allows the array to be passed around by reference while keeping the array on the stack or in a struct. | |||
Unless you need stack allocation or allocation into a struct, you are should probably use a vector. | |||
====std::vector==== | |||
<code>#include <vector></code> | |||
https://en.cppreference.com/w/cpp/container/vector<br> | |||
This is a dynamically-allocated resizable array, known as an ArrayList in Java.<br> | |||
<syntaxhighlight lang="c++"> | |||
// Basics | |||
vector my_vec; | |||
// Vector with size 5 | |||
vector my_vec(5); | |||
// Vector with size 5 initialized to 1 | |||
vector my_vec(5, 1); | |||
// Length of vector | |||
my_vec.size(); | |||
// Equivalent to size()==0 | |||
my_vec.empty(); | |||
// Equivalent to my_vec[0]; | |||
// Undefined on empty vectors | |||
my_vec.front(); | |||
// Equivalent to my_vec[my_vec.size()-1]; | |||
// Undefined on empty vectors | |||
my_vec.back(); | |||
</syntaxhighlight> | |||
Note that [https://en.cppreference.com/w/cpp/container/vector_bool <code>vector<bool></code>] is a special case of bit-packed booleans instead of an array of bools. You should use <code>vector<char></code> instead if your code relies on it being continguous.<br> | |||
====std::span==== | |||
<code>#include <span></code><br> | |||
https://en.cppreference.com/w/cpp/container/span<br> | |||
This is view of some contiguous amount of memory. If the size is static, this is equivalent to a single pointer, otherwise is it equivalent to two pointers (i.e. begin and end). | |||
If you use this as the parameter to your function, it will accept both arrays and vectors. | |||
Additionaly, there is a [https://en.cppreference.com/w/cpp/container/span/subspan subspan] function so you don't need to pass around indices or pointers to get subvectors. | |||
====std::deque==== | |||
Double-ended queue | |||
====std::list==== | |||
This is a doubly linked list. You can delete elements from the middle of the list if you know have an iterator. | |||
<syntaxhighlight lang="cpp"> | |||
list<int> m_list; | |||
list<int>::iterator m_it = m_list.insert(5); | |||
// Remove the element | |||
m_list.erase(m_it); | |||
</syntaxhighlight> | |||
===Container adaptors=== | |||
====std::queue==== | |||
[https://en.cppreference.com/w/cpp/container/queue Reference]<br> | |||
<syntaxhighlight lang="c++"> | |||
std::queue<int> my_queue; | |||
my_queue.push(a); | |||
auto val = my_queue.front(); | |||
my_queue.pop(); // returns void | |||
</syntaxhighlight> | |||
====std::stack==== | |||
[https://en.cppreference.com/w/cpp/container/stack cppreference] | |||
<syntaxhighlight lang="cpp"> | |||
std::stack<char> my_stack; | |||
// Push to stack | |||
// You can also use emplace | |||
// Returns void | |||
my_stack.push('a'); | |||
// Peek | |||
// Always make sure stack is not empty | |||
char top = my_stack.top('a'); | |||
// Pop | |||
// Note: returns void | |||
// Always make sure stack is not empty | |||
my_stack.pop(); | |||
</syntaxhighlight> | |||
====std::priority_queue==== | |||
This is a min/max heap. | |||
===Associative Containers=== | |||
Also known as maps or associative arrays. | |||
====std::set==== | |||
[https://en.cppreference.com/w/cpp/container/set reference]<br> | |||
<code>#include<set></code><br> | |||
This is a binary tree (likely red-black tree). You can assume <math>O(\log n)</math> operations. | |||
====std::map==== | |||
[https://en.cppreference.com/w/cpp/container/map reference]<br> | |||
<code>#include<map></code><br> | |||
This is a binary tree (likely red-black tree). You can assume <math>O(\log n)</math> operations. | |||
====std::unordered_set==== | |||
[https://en.cppreference.com/w/cpp/container/unordered_set reference]<br> | |||
<code>#include <unordered_set></code><br> | |||
This is a hashset. You can assume operations are <math>O(1)</math> on average and <math>O(N)</math> worst case.<br> | |||
<syntaxhighlight lang="cpp> | |||
std::unordered_set<int> my_set; | |||
// Add | |||
my_set.insert(5); | |||
// Check contains | |||
my_set.find(5) != my_set.end(); // Before C++20 | |||
my_set.contains(5); // C++20 | |||
// Remove | |||
my_set.erase(5); | |||
</syntaxhighlight> | |||
====std::unordered_map==== | |||
[https://en.cppreference.com/w/cpp/container/unordered_map reference]<br> | |||
<code>#include<unordered_map></code><br> | |||
This is a hashmap. You can assume operations are <math>O(1)</math> on average and <math>O(N)</math> worst case.<br> | |||
<syntaxhighlight lang="C++"> | |||
std::unordered_map<int, std::string> my_map; | |||
my_map[5] = "hey"; // Fine as long as value type is not a reference. | |||
my_map.insert({5, "hey"}); // Necessary if value type is a reference. | |||
my_map.find(5) != my_map.end(); | |||
my_map.contains(5); // C++20 | |||
</syntaxhighlight> | |||
;Custom Keys | |||
How to use a rational number as a key in C++ | |||
<syntaxhighlight lang="C++"> | |||
struct Fraction | |||
{ | |||
int num; | |||
int den; | |||
bool operator==(const Fraction &other) const { | |||
return num*other.den == den * other.num; | |||
} | |||
Fraction(int a, int b) : num(a), den(b) {} | |||
}; | |||
</syntaxhighlight> | |||
==Functional Programming== | |||
https://medium.com/swlh/doing-it-the-functional-way-in-c-5c392bbdd46a | |||
Many of these can be parallelized with [https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t execution policies] such as <code>std::execution::par</code> and <code>std::execution::par_unseq</code>. Paired with [https://adaptivecpp.github.io/AdaptiveCpp/stdpar/ AdaptiveCPP], some operations can be automatically GPU accelerated as well. | |||
Most of these require C++20. | |||
===Map=== | |||
* <code>std::for_each</code> | |||
* <code>std::transform</code> | |||
* [https://en.cppreference.com/w/cpp/algorithm/copy <code>std::copy</code>, <code>std::copy_if</code>] | |||
* [https://en.cppreference.com/w/cpp/algorithm/fill <code>std::fill</code>] | |||
===Reduce/Fold=== | |||
* <code>std::reduce</code> | |||
* <code>std::accumulate</code> | |||
* [https://en.cppreference.com/w/cpp/algorithm/ranges/fold_left <code>std::ranges::fold_left</code>] (C++23) | |||
===Filter=== | |||
* <code>std::copy_if</code> | |||
* <code>std::remove_if</code> | |||
* <code>std::find_if</code> | |||
==Programming Styles== | |||
===Modern C++=== | |||
[https://github.com/rigtorp/awesome-modern-cpp List of resources]<br> | |||
* Use RAII principles. | |||
** I.e. each object should manage it's own memory rather than the caller having to manage it. | |||
** You should never use `malloc` and `free` unless interfacing with C libraries. | |||
* Avoid the use of new and delete, instead using vector or smart pointers. | |||
* Use clang-format. | |||
;Resources | |||
* [https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md CppCoreGuidelines] | |||
* [https://google.github.io/styleguide/cppguide.html Google C++ Style Guide] - note that some people dislike this since it is focused on interoperability and suggests avoiding exceptions. | |||
==RAII== | |||
[https://en.cppreference.com/w/cpp/language/raii cppreference raii]<br> | |||
[https://en.cppreference.com/w/cpp/language/rule_of_three cppreference rule_of_three]<br> | |||
Resource Acquisition Is Initialization - binds the life cycle of a resource to the lifetime of an object.<br> | |||
For instance, the resource for a vector is an allocated amount of memory. Once the vector is destroyed and the destructor called, the resource is released.<br> | |||
If you need any from one of the rules, you need to implement the remainder | |||
;Rule of zero<br> | |||
Do not use a custom deconstructor, copy constructor, or copy assignment. Push all of these operations into the classes of member variables such as <code>std::vector</code> and <code>unique_ptr</code>. This is the best and simplest case. | |||
;[[Wikipedia: Rule of three (C++ programming) | Rule of three]] | |||
* Destructor | |||
* Copy constructor | |||
* Copy assignment operator | |||
;[[Wikipedia: Rule of three (C++ programming)#Rule of five | Rule of five]] | |||
* All from rule of three plus: | |||
* Move constructor | |||
* Move operator | |||
;Rule of four and a half: | |||
* Destructor | |||
* Copy constructor | |||
* Copy-and-swap assignment operator | |||
* Swap function | |||
{{hidden | Example Rule of Four RAII Class | | |||
Copied from [https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom stack overflow] | |||
<syntaxhighlight lang="cpp"> | |||
#include <algorithm> // std::copy | |||
#include <cstddef> // std::size_t | |||
class dumb_array | |||
{ | |||
public: | |||
// (default) constructor | |||
dumb_array(std::size_t size = 0) | |||
: mSize(size), | |||
mArray(mSize ? new int[mSize]() : nullptr) | |||
{ | |||
} | |||
// copy-constructor | |||
dumb_array(const dumb_array& other) | |||
: mSize(other.mSize), | |||
mArray(mSize ? new int[mSize] : nullptr), | |||
{ | |||
// note that this is non-throwing, because of the data | |||
// types being used; more attention to detail with regards | |||
// to exceptions must be given in a more general case, however | |||
std::copy(other.mArray, other.mArray + mSize, mArray); | |||
} | |||
// destructor | |||
~dumb_array() | |||
{ | |||
delete [] mArray; | |||
} | |||
// assignment operator | |||
dumb_array& operator=(dumb_array other) // (1) | |||
{ | |||
swap(*this, other); // (2) | |||
return *this; | |||
} | |||
// swap | |||
friend void swap(dumb_array& first, dumb_array& second) // nothrow | |||
{ | |||
// enable ADL (not necessary in our case, but good practice) | |||
using std::swap; | |||
// by swapping the members of two objects, | |||
// the two objects are effectively swapped | |||
swap(first.mSize, second.mSize); | |||
swap(first.mArray, second.mArray); | |||
} | |||
private: | |||
std::size_t mSize; | |||
int* mArray; | |||
}; | |||
</syntaxhighlight> | |||
}} | |||
==Useful Libraries== | |||
A list of useful libraries | |||
===Boost=== | |||
{{main | Boost (C++ libraries)}} | |||
A set of popular C++ libraries. Most are header-only. | |||
===cxxopts=== | |||
[https://github.com/jarro2783/cxxopts Link]<br> | |||
A header-only C++ argument parser.<br> | |||
Note that if you already use Boost, you can use <code>Boost::Program_options</code> instead. | |||
===Eigen=== | |||
{{main | Eigen (C++ library)}} | |||
A header-only C++ linear algebra library. | |||
===absl=== | |||
https://github.com/abseil/abseil-cpp is a library used by Google which supplements the standard library. | |||
Useful things: | |||
# <code>absl::Time</code> and <code>absl::Duration</code>. | |||
# [https://abseil.io/docs/cpp/guides/strings#abslstrcat absl strings] | |||
# [https://abseil.io/docs/cpp/guides/logging absl logging] | |||
Many parts of absl now have <code>std::</code> equivalents such as <code>std::unique_ptr</code>, <code>std::string_view</code>, <code>std::span</code>. Unless contributing to Google codebases, you should probably prefer those. | |||
At Google, they prefer absl hash containers over unordered_set and unordered_map: | |||
# <code>absl::flat_hash_map</code> | |||
==References== | |||
[[Category:Programming languages]] |