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, regex, STL containers) thanks to the C++ standard library.
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 <cerrno>
#include <string_view>


std::string get_file_contents(const std::string &filename)
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:
* <code>unique_ptr</code>
* [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>shared_ptr</code>
* <code>std::shared_ptr</code> - the memory has multiple owners.
* <code>weak_ptr</code>
* <code>std::weak_ptr</code> - a non-owning reference to a shared_ptr.
Use <code>unique_ptr</code> when one piece of code ''owns'' the memory at any given time.<br>
 
Use <code>shared_ptr</code> when multiple objects need to reference the same thing.<br>
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.
Use <code>weak_ptr</code> to avoid cyclic dependencies which cause issues with reference counting.<br>
 
If you are using C++14 or newer, you should 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>
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>
Alternatively if you already have a smart pointer, 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.
 
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>
Lots of useful time stuff. Good for timing your code.
 
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>
In C++, you can use <code>std::vector</code> which gives you a resizable array.
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.
This will allocate an array in the heap.<br>
Unless you need stack allocation or allocation into a struct, you are should probably use a vector.
 
[https://shendrick.net/Coding%20Tips/2015/03/15/cpparrayvsvector.html array vs vector]<br>
If you need a statically allocated array, you can use <code>std::array</code> in the <code>array</code> header.<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 unlike <code>std::vector</code>.<br>
If you want to manually allocate an array on the heap, you can do so as follows:
<syntaxhighlight lang="C++">
auto my_arr = std::make_shared<std::array<char,64>>();
</syntaxhighlight>


====std::vector====
====std::vector====
[https://en.cppreference.com/w/cpp/container/vector Reference]<br>
<code>#include <vector></code>
Use vector for almost everything...<br>
https://en.cppreference.com/w/cpp/container/vector<br>
It is an ArrayList.<br>
This is a dynamically-allocated resizable array, known as an ArrayList in Java.<br>
Note that <code>vector<bool></code> is not an array of bools.<br>
This has several nuances so you should use <code>vector<char></code> instead.<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 &lt;span&gt;</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); # C++20
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.
* Use smart pointers instead of new and delete.
* Avoid the use of new and delete, instead using vector or smart pointers.
** Namely, <code>std::unique_ptr</code>, <code>std::shared_ptr</code>, and <code>std::vector</code>.
* 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 (destructor called), the resource is released.<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>
In general, each RAII object should have all of the following:
 
* Constructor acquiring resources
If you need any from one of the rules, you need to implement the remainder
* Copy Constructor
 
* Assignment operator
;Rule of zero<br>
* Destructor releasing resources
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.
* Swap function (for <code>std::swap</code>)
 
* Move constructor (since C++11, for <code>std::move</code>)
;[[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);
    }
    dumb_array& operator=(dumb_array other) // (1)
    {
        swap(*this, other); // (2)
        return *this;
    }
    dumb_array(dumb_array&& other) noexcept ††
        : dumb_array() // initialize via default constructor, C++11 only
    {
        swap(*this, other);
     }
     }


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]]