const int n; // compilation error since n must be initialised
weird example
#include <iostream>using namespace std;int main() { const int a = 5; // const variable int* b; // normal pointer b = (int*) &a; // this is allowed *b = 10; // this is allowed apparently count << a << " " << b << " " << &a << " " << *b << endl;}
5 0xdeadbeef 0xdeadbeef 10
b actually points to a. But when accessed through a the compiler substitutes the constant expression. Technically the behavior is undefined
with pointers:
int* a normal. can take address/value of anything, including constant variables
const int* a. Value of a is constant. can’t change by dereferencing it, can change address though
*a = 2 ERROR
a = (int*) &NEW_VARIABLE WORKS
int* const a does the opposite of above. can change the value at a, but not a itself
int const* a is the same as const int* a
const int* const a makes everything constant
this directly applies to strings as well, where you may change the string but not individual characters and vice versa
char *str = strdup("IIT Kharagpur");str[0] = 'N';str = strdup("MeowLand");const char *str; // can change str but not str[i]char* const str; // can change str[i] but not strconst char* const str; // can not change anything
with classes
class Entity {private: int m_X, m_Y,public: // adding const after parameters says the method can't do anything to the class. // It is essentially read only int GetX() const { return m_X; }}
Note
int* x, y; // only x is a pointerint *x, *y; // both are pointers
volatile
allows other operations to change your variable. shared memory
volatile int* pi1 = 0; // value at p is volatileint* volatile pi2 = 0; // address of p is volatilevolatile int* volatile pi3 = 0; // both are volatile
volatile removes a lot of compiler optimisations
volatile x = 1;// ...x++;
how this would work is a three step process usually
mv <reg>, x
add <reg>, 0x01
mv x, <reg>
however what if there is an interrupt during this? so you want to protect your x. you’ll be doing something like disabling interrupt or something. C doesn’t know anything about multithreading so no inbuilt support.
inline functions
functions that are expanded right below where they are called. This creates a speed vs memory tradeoff if the inline function is called multiple times
func1 <---- jump here when called
// have to pass parameters to this location // creates overhead
inline func2
...
main
call func1
call func2
func2 <--- func2 is expanded here, so less speed overhead
#include <iostream>using namespace std;inline int SQUARE(int x) { return x*x; }int main() { int a = 3, b; b = SQUARE(a); cout << b << endl; return 0;}
you can see that SQUARE() (0x1192) was expanded right after main() (0x1149) which is considerably closer than it would be normally with _start() and all cluttering up the stack
Warn
inlineing is a directive, the compiler is suggested to inline, not forced to. It may not inline functions with a large body.
inline functions may NOT be recursive
the function body is needed for inlineing at the time of function call, which means that you can not put them in libraries etc where they are treated like extern. Which is why implement inline functions in header files as the CPP expands header files in the main file.
inline functions can not be overloaded. They must not have two different definitions (unless they are the exact same definition)
Pointers
#include <iostream>using namespace std;int main() { unsigned char* buffer = new char[8]; memset(buffer, 0, 8); // i liked this, the rest of the example is useless delete[] buffer;}
References
pointers in disguise
references have to reference an already existing variable
they don’t occupy memory
#include <iostream>using namespace std;inline void increment(int value) { value++; }inline void ptrIncrement(int* value) { (*value)++; }inline void refIncrement(int& value) { value++; }int main() { int a = 5; // variable int* b = &a; // pointer int& c = a; // reference, must be initiliased // c is now basically an alias for a c = 2; // this works increment(a); // passed by value => a NOT incremented cout << a << endl; // still 2 ptrIncrement(b); // passed by reference => a is incremented cout << a << endl; // 3 refIncrement(c); // also passed by reference, but simpler // can also pass a directly cout << a << endl; // 4 int d = 0; c = d; // a = 0 // can NOT change what c references cout << a << endl; // 0}
using const int& stops a reference parameters from being changed in the function. But this is essentially the same as pass by value, except it saves memory
however it is slower because pointer traversal has speed overhead
Return by Reference:
TBA
• Call
◦ Pass parameters of built-in types by value
▷ Recall: Array parameters are passed by reference in C and C++
◦ Pass parameters of user-defined types by reference
▷ Make a reference parameter const if it is not used for output
• Return
◦ Return built-in types by value
◦ Return user-defined types by reference
▷ Return value is not copied back
▷ May be faster than returning a value
▷ Beware: Calling function can change returned object
▷ Never return a local variables by reference
Default Parameters
#include <iostream>using namespace std;inline IdentifyFunc(int a = 10) { return (a); } // func with default parametersint main() { int x = 5; y; y = IdentityFunc(x); // y = 5 y = IdentityFunc(); // y takes default = 10}
this allows some parameters to be optional
all parameters to the right of a default parameter must have default values
void f(int, double = 0.0, char*) // ERROR
default parameters can not be redefined
void g(int = 0);void g(int = 69); // ERROR
- Default parameters should be supplied in the header files, and not in the function definitions
```cpp
------- myFunc.h
void g(int, double, char = 'a')
void g(int i, double f = 0.0, char ch); // new overload
void g(int i = 0; double f; char ch); // new overload
------- myFunc.c
import "myFunc.h"
using namespace std;
// do not add defaults here
void g(int i, double d, char c) {
// ...
}
-------
Function Overloading
Functions having the same name, similar functionality, but different algorithms, are identified by different interfaces (parameters).
You can call the same function with different parameters to do different things
typedef struct { int data[10][10]; } Mat; // 2D Matrixtypedef struct { int data[1][10]; } VecRow; // Row Vectortypedef struct { int data[10][1]; } VecCol; // Column Vectorvoid Multiply(const Mat& a, const Mat& b, Mat& c); // c = a * bvoid Multiply(const Mat& a, const VecCol& b, VecCol& c); // c = a * bvoid Multiply(const VecRow& a, const Mat& b, VecRow& c); // c = a * bvoid Multiply(const VecCol& a, const VecRow& b, Mat& c); // c = a * bvoid Multiply(const VecRow& a, const VecCol& b, int& c); // c = a * bint main() { Mat m1, m2, rm; VecRow rv, rrv; VecCol cv, rcv; int r; Multiply(m1, m2, rm); // rm <-- m1 * m2 Multiply(m1, cv, rcv); // rcv <-- m1 * cv Multiply(rv, m2, rrv); // rrv <-- rv * m2 Multiply(cv, rv, rm); // rm <-- cv * rv Multiply(rv, cv, r); // r <-- rv * cv return 0;}
This is also called Static Polymorphism
another example
#include <iostream>using namespace std;int Area(int a, int b) { return a*b; }int Area(int c) {return c*c; }double Area(double a, double b) { return a*b; }
two functions having the same signature but different return types can not be overloaded (obv)
int Area(int a, int b);double Area(int a, int b);
**Overload Resolution**:
This is theoretical and conceptual: Must study
- https://www.youtube.com/watch?v=zVJfwYtjIoY
- https://learn.microsoft.com/en-us/cpp/cpp/function-overloading?view=msvc-170
# Operator Overloading
```cpp
#include <iostream>
#include <string>
struct Vector2 {
float x, y;
Vector2(float x, float y)
: x(x), y(y) {}
Vector2 Add (const Vector2& other) const {
return Vector2(x + other.x, y + other.y);
}
Vector2 Multiply (const Vector2& other) const {
return Vector2(x* other.x, y * other.y);
}
Vector2 operator+(const Vector2& other) const {
return Add(other);
}
Vector2 operator*(const Vector2& other) const {
return Multiply(other);
}
};
int main() {
Vector2 position(4.0f, 4.0f);
Vetor2 speed(0.5f, 1.5f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result = position.Add(speed.Multiply(powerup));
// getting convoluted now, define operators instead
result = position + (speed * powerup); // do this instead
}