Advertisement
dragonbane

C++

Dec 7th, 2022 (edited)
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.22 KB | None | 0 0
  1. Const Reference:
  2. Const& Example a;
  3. Const* Example a;
  4.  
  5. Const in both cases mean the Example class is not mutable, aka members of it can not be changed. It also means member functions can only be called if they are also declared const (there is an assurance these functions also won't manipulate members)
  6.  
  7. A const ptr can be re-assigned, a reference can NEVER be re-assigned after initialisation, const or otherwise. This also means a reference member variable has to be intialized using the intialization list.
  8.  
  9. A const reference is useful for:
  10. -Inmutability of the underlining object/variable
  11. -Avoiding call-by-values where the value is copied during function calls (slow, costs memory). Instead call-by-ref is more efficient
  12.  
  13. Const after the ptr type would simply mean the ptr is const and can not be re-assigned, but the object itself can be mutated!
  14.  
  15. Intialization list required for:
  16. -Const variables members
  17. -Reference variables
  18. -Other class objects without a default constructor
  19. -Base classes without a default constructor
  20.  
  21. Intialization list order:
  22. -Should match member declaration order in the class header (bases classes first), otherwise you risk issues as regardless of the init list order, members always get intialized in the order they were declared. If you intialize a member variable as depending on another member's value (also init in the list), you crash if the order doesn't match
  23.  
  24. Default constructor:
  25. A default constructor is only created automatically if no custom constructor is provided. Even a copy constructor already counts!
  26.  
  27. Copy constructor:
  28. Automatically created if not provided. Intializes a new instance of this class with another object of the same type as the only input parameter. Copies all members over. Dangerous if this includes a ptr if you have a destructor deleting it, which would be called twice on the same pointer if both variables go out of scope. Crashes the 2nd time/undefined. Classes with a ptr member that gets deleted in the destructor should always provide their own copy constructor to be safe! In the copy constructor, allocate a new instance of that object/create a new buffer before copying the contents (with memcpy or using ptr deref assignment: *newNember = *value)
  29.  
  30. Copy:
  31. Example(const Example& m); //Copy constructor
  32.  
  33. Same issue can occur with the assignment operator since this would also copy/assign contents over:
  34. Example& operator= (const Example& m); //Assignment operator
  35.  
  36. Another note: Be mindful when exceptions can potentially happen that you want to catch. If a constructor allocates memory and throws an exception during it, the destructor will not be invoked and memory could get leaked. A smart pointer or proper checks and frees would avoid it! Throwing inside a constructor is fine generally, but never throw inside a destructor!
  37.  
  38. Virtual functions:
  39. -Functions that can be replaced in a derived class while ensuring that calling the function on a ptr of the base class will in fact call the function of the actual derived class if it is what's actually stored under the base class ptr
  40. -Derived classes can also replace non virtual functions, but then calling it on the base class ptr will in fact call it on the base class
  41. -Derived class functions can invoke their base version by using the explicit Base::Function() type call
  42. -Virtual destructors should ALWAYS be used in base classes if the class is planned to get inherited by derived classes at any point. Automatically can be assumed if your class has even one virtual method. This ensures that deleting a derived class through a Base class ptr will call the proper derived class destructor which avoids leaking memory and is also undefined behavior
  43.  
  44. Derived class constructors will first call their base class constructors BEFORE executing
  45. Derived class destructors will automatically invoke their base class destructors AFTER executing
  46.  
  47. This ensures constructors construct top-to-bottom (member allocation before constructor func) while destructors destroy in the reverse order bottom-to-top (destructor func before members are destroyed in reverse order)
  48.  
  49. Note: Virtual functions incur a performance and memory penality, so don't use them blindly!
  50.  
  51.  
  52. Overloaded functions:
  53. -Functions with the same name, but providing a different definition, different return param (optional) + different input params (must!). Decided at compile type which function version fits the calling arguments
  54. -An overloaded function CAN have a different return type, but MUST have different input parameters. An identical definition that just differs in its return type is invalid as the compiler treats both functions the same
  55.  
  56.  
  57. Class inheritance:
  58. Derived class is defined as class Derived : public BaseClass
  59. Can derive from multiple base classes with commas
  60.  
  61. Access specifier before the base class:
  62. -Public means everything gets inherited as is. Derived class only has access to public and protected members and functions
  63. -Protected means public base members and functions are downgraded to protected in the derived class. That means another derived class of this class can use them, but outside code working on instances of this class can not access them
  64. -Private downgrades public and protected down to private in the derived class
  65.  
  66. All members and functions are otherwise inherited by the derived class as is and can be used from within (that are non private!)
  67.  
  68.  
  69.  
  70. Cast types:
  71. const_cast<type> (expr) − The const_cast operator is used to explicitly override const and/or volatile in a cast. The target type must be the same as the source type except for the alteration of its const or volatile attributes. This type of casting manipulates the const attribute of the passed object, either to be set or removed.
  72.  
  73. dynamic_cast<type> (expr) − The dynamic_cast performs a runtime cast that also verifies the validity of the cast. If the cast cannot be made, the cast fails and the expression evaluates to null. A dynamic_cast performs casts only on polymorphic types (aka base/derived classes with at least one virtual function) and can cast a BaseA* pointer into a DerivedB* pointer only if the object being pointed to actually is a DerivedB object. Requires that at least one of the involved types has a virtual method.
  74.  
  75. reinterpret_cast<type> (expr) − The reinterpret_cast operator changes a pointer to any other type of pointer. It also allows casting from pointer to an integer type (address value of the ptr) and vice versa.
  76.  
  77. static_cast<type> (expr) − The static_cast operator performs a cast without checking the validty of the cast at runtime outside of what the compiler can check for during compile time. For example, it can be used to cast a base class pointer into a derived class pointer.
  78.  
  79. Note: A class that declares or inherits a virtual function is called a polymorphic class
  80.  
  81.  
  82.  
  83.  
  84. Ptr vs. Reference:
  85.  
  86. A pointer in C++ is a variable that holds the memory address of another variable:
  87. -It is not necessary to initialize it with a value during declaration
  88. -It can be assigned a NULL value
  89. -It must be dereferenced with a * to access the variable’s value
  90. -Arithmetic operations can be performed on it
  91. -After declaration, it can be re-assigned to any other variable of the same type
  92.  
  93. A reference is an alias for an already existing variable. Once a reference is initialized to a variable, it cannot be changed to refer to another variable:
  94. -It is necessary to initialize it with a value during declaration
  95. -It CANNOT be NULL
  96. -It does not need to be dereferenced and can be used simply by name
  97. -Arithmetic operations can NOT be performed on it
  98. -It CANNOT be re-assigned to any other variable after its initialization
  99.  
  100.  
  101. What is a forward declaration?
  102. -Forward Declaration refers to the beforehand declaration of the signature of a variable, function, class prior to its usage (done later in the program). Typical at the top of a file
  103. -In C++ it is mainly used for classes and structs so they can be used before the compiler sees the actual definition/implementation. This can also be the case in the same file where you want to supply the definition of a struct A below another struct B that depends on A, so forward declaring A solves it
  104. -Used to break cyclic references where two definitions in 2 different header both use each other (header #include loop cycle)
  105. -e.g. class A; is a simple forward declaration of A without giving the definition
  106.  
  107. How forward-declarations can significantly reduce build/compile times?
  108. Forward declarations cut down compile times by avoiding needless header includes. Which have to perform file IO. And if that header again includes other headers, the compiler spends a long time just traversing that chain, even when you only might need a single class. It also blows up the object file initially since header includes are just pasted in-line
  109.  
  110. Another compilation speedup:
  111. A single change in a header requires all files that include it to be at least incrementally rebuild, which can be avoided with a forward declaration, which also cuts down on build times
  112.  
  113. How can you use many more forward declarations than usual?
  114. When you include forward declarations to a struct/class, the compiler will still error if another struct/class depends on this struct/class as member, because it doesnt know the size/layout of the struct/class yet. So if you instead strictly use ptrs to struct/class types, it will compile, since all ptrs are the same size (4 bytes on 32 bit, 8 bytes on 64 bit). That allows using many more forward declarations than usual
  115.  
  116.  
  117. POD (Plain Old Data) structures:
  118. -They are trivial and standard layout C++ classes or structs (structs and classes are the same in C++ with the only difference being classes are by default private and structs public) that are compatible with classic C structs
  119. -Is an aggregate class/struct that itself only has POD members (only exception are static members which can be anything). Allowed POD types are all primitive types, ptrs, enums and POD objects (structs/classes). std::string for example is not a POD class
  120. -No user defined constructor/destructor of any kind (including copy constructor or assign operator)
  121. -No virtual functions, no base classes
  122. -Every member must be public except static members and functions which can be private/protected
  123. -Can be safely copied with memcpy
  124. -Non virtual, non static member functions are allowed (that are public)
  125.  
  126.  
  127. Stack vs. Heap
  128. -Stack is used for static memory allocation and Heap for dynamic memory allocation, both stored in the computer's RAM
  129. -Using "new" will allocate memory on the heap (same as alloc/malloc)
  130. -In-line variables that are automatically GCed when they go out of scope are allocated on the stack
  131. -Static variables are stored in a special data region of the executable that is neither the stack or heap (only one copy per program)
  132.  
  133.  
  134. std::map vs. std::unordered_map
  135.  
  136. std::map:
  137. -Stores key, value pairs, identical with std::unordered_map
  138. -Allows iteration in a sorted manner (in the order elements were inserted), std::unordered_map does not
  139. -Access and search complexity is log (N), std::unordered_map is constant time O (1) (O = Order). Thus an std::unordered_map access can be performed in a single lookup. logarithmic N (N = map size) scale means the speed gets less impacted the more elements the map has, speed doesn't decrease linearly. The reason for that is the tree splitting in half during lookups
  140. -Uses a tree, std::unordered_map uses a hash table
  141. -Tree is split in half repeatedly during lookups explaining the log scale. std::unordered_map hash table is accessed using integers created from the strings after hashing them
  142. -Use std::map for ordered data, std::unordered_map for anything else that doesn't require ordering and especially when requiring fast single element access
  143. -std::vector is more performant than a std::map for most tasks involving sorted access
  144.  
  145.  
  146. std::vector vs. std::list vs. std::deque
  147.  
  148. -vector is a dynamic array, elements are placed in continous storage. std::list uses a double linked list and can thus perform forward and backward traversal (since each list entry links to the next and prior entry), no continous storge for a std::list
  149. -vector and std::list and std::deque are ordered
  150. -vector can use iterators for ordered forward traversal
  151. -vector can perform constant time O (1) (O = Order) direct random lookups, std::list does not have index based lookup
  152. -vector can be pre-allocated to a default size, std::list can not
  153. -vector insertions/deletions at the end requires constant time, insertions/deletes elsewhere are costly. std::map random position insertions/deletions are cheaper since only 2 links need to be updated
  154. -vector is cheaper RAM wise since std::list requires more storage for the links
  155. -Use a map if you need backwards searches from random positions or random insertions/deletions. Otherwise vector is a better choice
  156. -deque sits in the middle and is a good choice if you mostly want to use a vector, but also want to insert/remove elements fast at the start (so deque is good for QUEUE like systems as the name implies, consumer pops from the start, providers push to the end, resulting in a first-in-first-out "FIFO" queue)
  157.  
  158. Namespace:
  159. -A namespace is a declarative region that provides a scope to the identifiers (the names of types, functions, variables, etc) inside it
  160. -Namespaces are used to organize code into logical groups and to prevent name collisions that can occur especially when your code base includes multiple libraries
  161. -Identifiers outside the namespace can access the members by using the fully qualified name for each identifier, for example std::string, or a using Directive for all the identifiers in the namespace (using namespace std;)
  162.  
  163. Templates:
  164. -Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type
  165. -Achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function. These function templates can use these parameters as if they were any other regular type
  166. -When the compiler encounters this call to a template function, it uses the template to automatically generate a function replacing each appearance of myType by the type passed as the actual template parameter and then calls it. This process is automatically performed by the compiler and is invisible to the programmer
  167. -We also have the possibility to write class templates, so that a class can have members and methods that use template parameters as types
  168. -Can't split declaration and definition/implementation of a template
  169.  
  170. Definition:
  171. template <class or typename T>
  172. T GetMax (T a, T b) {
  173. T result;
  174. result = (a > b) ? a : b;
  175. return (result);
  176. }
  177.  
  178. Call:
  179. int x, y;
  180. GetMax<int>(x, y); (implicit without specifying int works as well if both x and y are known to be ints)
  181.  
  182.  
  183.  
  184. Stream insertion/extraction:
  185. << insertion operator
  186. >> extraction operator
  187.  
  188.  
  189. Avoiding including the same header file twice:
  190. -Avoids declaring the same types twice, which would error during compile
  191. -Classic: include guards. #ifndef name #define name. Name usually the unique header file name
  192. -Modern: #pragma once
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement