C++: Difference between revisions
(40 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, | 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== | ==Usage== | ||
Line 81: | Line 81: | ||
===String=== | ===String=== | ||
<code>#include <string></code><br> | <code>#include <string></code><br> | ||
If you don't need to own the string, prefer to use <code>string_view</code>. | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
Line 119: | Line 121: | ||
Strings are mutable in C++.<br> | Strings are mutable in C++.<br> | ||
I typically use <code>+</code> or <code>ostringstream</code> to build strings. | 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=== | ===Filesystem=== | ||
Line 182: | Line 191: | ||
[https://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html Reference and comparison of different methods] | [https://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html Reference and comparison of different methods] | ||
<syntaxhighlight lang="C++"> | <syntaxhighlight lang="C++"> | ||
#include <cerrno> | |||
#include <fstream> | #include <fstream> | ||
#include <string> | #include <string> | ||
#include < | #include <string_view> | ||
std::string get_file_contents( | std::string get_file_contents(std::string_view filename) { | ||
{ | |||
std::ifstream in(filename, std::ios::in | std::ios::binary); | std::ifstream in(filename, std::ios::in | std::ios::binary); | ||
if (in.good()) | if (in.good()) { | ||
std::string contents; | std::string contents; | ||
in.seekg(0, std::ios::end); | in.seekg(0, std::ios::end); | ||
Line 257: | Line 265: | ||
Smart pointers were added in C++11.<br> | Smart pointers were added in C++11.<br> | ||
There are 3 types of smart pointers: | 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: | Example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
Line 287: | Line 296: | ||
** 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> | ** 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. | * 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==== | ====Garbage Collection==== | ||
Line 410: | Line 420: | ||
===Chrono=== | ===Chrono=== | ||
<code>#include <chrono></code><br> | <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"> | <syntaxhighlight lang="cpp"> | ||
auto start = std::chrono::high_resolution_clock::now(); | auto start = std::chrono::high_resolution_clock::now(); | ||
Line 451: | Line 463: | ||
std::cout << '\n'; | std::cout << '\n'; | ||
</syntaxhighlight> | </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== | ||
Line 464: | Line 487: | ||
====std::array==== | ====std::array==== | ||
<code>#include <array></code><br> | <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. | |||
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 | |||
====std::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++"> | <syntaxhighlight lang="c++"> | ||
// Basics | // Basics | ||
Line 503: | Line 516: | ||
my_vec.back(); | my_vec.back(); | ||
</syntaxhighlight> | </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==== | ====std::deque==== | ||
Line 548: | Line 570: | ||
my_stack.pop(); | my_stack.pop(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
====std::priority_queue==== | |||
This is a min/max heap. | |||
===Associative Containers=== | ===Associative Containers=== | ||
Line 571: | Line 596: | ||
my_set.insert(5); | my_set.insert(5); | ||
// Check contains | // Check contains | ||
my_set.find(5) != my_set.end(); | my_set.find(5) != my_set.end(); // Before C++20 | ||
my_set.contains(5); | my_set.contains(5); // C++20 | ||
// Remove | // Remove | ||
my_set.erase(5); | my_set.erase(5); | ||
Line 583: | Line 608: | ||
<syntaxhighlight lang="C++"> | <syntaxhighlight lang="C++"> | ||
std::unordered_map<int, std::string> my_map; | std::unordered_map<int, std::string> my_map; | ||
my_map.insert(5, "hey"); | 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.find(5) != my_map.end(); | ||
my_map.contains(5); // C++20 | my_map.contains(5); // C++20 | ||
Line 602: | Line 628: | ||
}; | }; | ||
</syntaxhighlight> | </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== | ==Programming Styles== | ||
Line 609: | Line 658: | ||
** I.e. each object should manage it's own memory rather than the caller having to manage it. | ** 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. | ** 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. | * Use clang-format. | ||
Line 619: | Line 667: | ||
==RAII== | ==RAII== | ||
[https://en.cppreference.com/w/cpp/language/raii cppreference raii]<br> | [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> | 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 | 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]] | ||
{{hidden | Example RAII Class | | * 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] | Copied from [https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom stack overflow] | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
Line 661: | Line 725: | ||
} | } | ||
// 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 | friend void swap(dumb_array& first, dumb_array& second) // nothrow | ||
{ | { | ||
Line 670: | Line 743: | ||
swap(first.mSize, second.mSize); | swap(first.mSize, second.mSize); | ||
swap(first.mArray, second.mArray); | swap(first.mArray, second.mArray); | ||
} | } | ||
Line 706: | Line 766: | ||
{{main | Eigen (C++ library)}} | {{main | Eigen (C++ library)}} | ||
A header-only C++ linear algebra 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== | ==References== | ||
[[Category:Programming languages]] | [[Category:Programming languages]] |