Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Const Reference:
- Const& Example a;
- Const* Example a;
- 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)
- 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.
- A const reference is useful for:
- -Inmutability of the underlining object/variable
- -Avoiding call-by-values where the value is copied during function calls (slow, costs memory). Instead call-by-ref is more efficient
- 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!
- Intialization list required for:
- -Const variables members
- -Reference variables
- -Other class objects without a default constructor
- -Base classes without a default constructor
- Intialization list order:
- -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
- Default constructor:
- A default constructor is only created automatically if no custom constructor is provided. Even a copy constructor already counts!
- Copy constructor:
- 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)
- Copy:
- Example(const Example& m); //Copy constructor
- Same issue can occur with the assignment operator since this would also copy/assign contents over:
- Example& operator= (const Example& m); //Assignment operator
- 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!
- Virtual functions:
- -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
- -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
- -Derived class functions can invoke their base version by using the explicit Base::Function() type call
- -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
- Derived class constructors will first call their base class constructors BEFORE executing
- Derived class destructors will automatically invoke their base class destructors AFTER executing
- 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)
- Note: Virtual functions incur a performance and memory penality, so don't use them blindly!
- Overloaded functions:
- -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
- -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
- Class inheritance:
- Derived class is defined as class Derived : public BaseClass
- Can derive from multiple base classes with commas
- Access specifier before the base class:
- -Public means everything gets inherited as is. Derived class only has access to public and protected members and functions
- -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
- -Private downgrades public and protected down to private in the derived class
- All members and functions are otherwise inherited by the derived class as is and can be used from within (that are non private!)
- Cast types:
- 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.
- 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.
- 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.
- 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.
- Note: A class that declares or inherits a virtual function is called a polymorphic class
- Ptr vs. Reference:
- A pointer in C++ is a variable that holds the memory address of another variable:
- -It is not necessary to initialize it with a value during declaration
- -It can be assigned a NULL value
- -It must be dereferenced with a * to access the variable’s value
- -Arithmetic operations can be performed on it
- -After declaration, it can be re-assigned to any other variable of the same type
- 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:
- -It is necessary to initialize it with a value during declaration
- -It CANNOT be NULL
- -It does not need to be dereferenced and can be used simply by name
- -Arithmetic operations can NOT be performed on it
- -It CANNOT be re-assigned to any other variable after its initialization
- What is a forward declaration?
- -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
- -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
- -Used to break cyclic references where two definitions in 2 different header both use each other (header #include loop cycle)
- -e.g. class A; is a simple forward declaration of A without giving the definition
- How forward-declarations can significantly reduce build/compile times?
- 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
- Another compilation speedup:
- 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
- How can you use many more forward declarations than usual?
- 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
- POD (Plain Old Data) structures:
- -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
- -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
- -No user defined constructor/destructor of any kind (including copy constructor or assign operator)
- -No virtual functions, no base classes
- -Every member must be public except static members and functions which can be private/protected
- -Can be safely copied with memcpy
- -Non virtual, non static member functions are allowed (that are public)
- Stack vs. Heap
- -Stack is used for static memory allocation and Heap for dynamic memory allocation, both stored in the computer's RAM
- -Using "new" will allocate memory on the heap (same as alloc/malloc)
- -In-line variables that are automatically GCed when they go out of scope are allocated on the stack
- -Static variables are stored in a special data region of the executable that is neither the stack or heap (only one copy per program)
- std::map vs. std::unordered_map
- std::map:
- -Stores key, value pairs, identical with std::unordered_map
- -Allows iteration in a sorted manner (in the order elements were inserted), std::unordered_map does not
- -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
- -Uses a tree, std::unordered_map uses a hash table
- -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
- -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
- -std::vector is more performant than a std::map for most tasks involving sorted access
- std::vector vs. std::list vs. std::deque
- -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
- -vector and std::list and std::deque are ordered
- -vector can use iterators for ordered forward traversal
- -vector can perform constant time O (1) (O = Order) direct random lookups, std::list does not have index based lookup
- -vector can be pre-allocated to a default size, std::list can not
- -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
- -vector is cheaper RAM wise since std::list requires more storage for the links
- -Use a map if you need backwards searches from random positions or random insertions/deletions. Otherwise vector is a better choice
- -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)
- Namespace:
- -A namespace is a declarative region that provides a scope to the identifiers (the names of types, functions, variables, etc) inside it
- -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
- -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;)
- Templates:
- -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
- -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
- -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
- -We also have the possibility to write class templates, so that a class can have members and methods that use template parameters as types
- -Can't split declaration and definition/implementation of a template
- Definition:
- template <class or typename T>
- T GetMax (T a, T b) {
- T result;
- result = (a > b) ? a : b;
- return (result);
- }
- Call:
- int x, y;
- GetMax<int>(x, y); (implicit without specifying int works as well if both x and y are known to be ints)
- Stream insertion/extraction:
- << insertion operator
- >> extraction operator
- Avoiding including the same header file twice:
- -Avoids declaring the same types twice, which would error during compile
- -Classic: include guards. #ifndef name #define name. Name usually the unique header file name
- -Modern: #pragma once
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement