-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathNotes.txt
More file actions
217 lines (157 loc) · 10.2 KB
/
Notes.txt
File metadata and controls
217 lines (157 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
* Better to write the namespace with :: as we may not use single namespace throughout a program.
* Overflow:
char x = 127;
std::cout << (int)x << std::endl; // prints 127
x++;
std::cout << (int)x << std::endl; // prints -128
* Dynamic decleration of variables allows you to use variables wherever they needed.
This improves efficiency as the declared variable will be removed after its scope ends.
You can define a variable inside an if statement, just like you do in for loops.
* In switch case statement, a fall-thru means executing next case in the absence of a break.
int and char are the allowed data types in cases.
In else if ladder, multiple conditions are checked.
in switch case it will directly jump to the case statement. So switch is faster than else if ladder
* For loop is used when the number of iterations are known in advance.
* The declared variables are allocated in stack. When you declare a pointer and allocate space using new, it is allocated from heap.
You need to delete the allocated memory from heap yourself.
Otherwise it will be kept until as long as program is running.
Stack varia are deleted automotically.
* Some problems when using pointers:
- Uninitilized pointer
- Memory leak
- Dangling pointer
Java and .NET deals with such issues. CPP doesn't. This is more powerfull. But you need to be careful.
* Reference:
It is like creating an alias. The same memory location is used.
x = 10;
int &y = x; // y can be used instead of x afterwards
// if you print &x and &y, they are the same
// once the reference is defined, it cannot be reinitialized for another variable:
// for example, &y = z; is not possible as we first assigned x to y.
// int &y; is also invalid, as decleration of a reference is not possible without initialization
* size of a pointer is independent of its data type. int *p1; or float *p2; or char *p3; all takes 8 bytes in latest compilers.
(assuming that pointer takes 2 bytes to make explanation simple)
* int x=10; int *y=&x; int * &z=y; // x is a variable. y is a pointer variable, pointing to x. z is a reference to a pointer variable.
// int *&z=y; means z is another name of y. now y and z are 2 names of same pointer.
* int x = 9; // Global variable
int main() {
int x = 10; // Local variable inside main()
cout << x + ::x; // Using `::x` to refer to the global variable
}
* Thinking in modular programming vs. OOP: modular you think as the functions. In objects, objects have functions and data related to those functions.
* Principles of OOP:
- Abstraction
- Encapsulation
.Data Hiding
- Inheritance
- Polymorphism
* Abstraction: About a class, I only know the functions of it.
I only know the name of it, no idea how it works (until I need to go into the implementation details.)
We use printf, but we don't know the implementation of it
(of course it can be written by every programmer but you usually don't need it)
* Encapsulation: Data is encapsulated, functions are visible.
It is for avoiding mishandling. Unintentional changes. It has nothing to do with security.
Data is private, functions are public.
* Inheritance: a class inherites features from the parent and can have more...
* Polymorphism: allows a single interface to represent multiple forms of behaviors.
Function overloading and method overriding are allowed by polymorphism.
* Scope resolution operator can be used, for example, to define a function body outside the class.
Let's say the function is declared inside a class. It is possible to use scope resolution to
define the function body outside.
class Rectangle{
int perimeter();
}
int Rectangle::perimeter(){
return 2*(width + height)
}
* It is a good practice in C++ to declare a function inside a class and define it outside using
the scope resolution operator (::). This approach enhances code clarity, organization, and
maintainability.
✅ 1. Improves Readability and Maintainability
Declaring function prototypes inside the class provides a clear interface.
Keeping definitions outside the class keeps the class definition clean and readable.
✅ 2. Reduces Compilation Dependencies
If the function definition is in a separate .cpp file, it reduces code recompilation when changes are made.
✅ 3. Useful for Large Functions
If a function is long or complex, defining it outside prevents cluttering the class definition.
✅ 4. Enables Separate Compilation
The class declaration (.h file) can be included in multiple files without duplicating large function bodies.
* An inline function suggests that the compiler replaces the function call with its actual code, eliminating the overhead of function calls.
If a function is defined inside the class, it becomes inline.
If declared inside and defined outside, becomes outline.
If declared inside with the keyword "inline", becomes inline.
Inline works like it becomes the part of the main function. Outline, the compiler calss the function inside the main.
* In C struct you can have data, but not functions. In CPP, both are allowed.
* Struct vs. Class: In struct everything, data and functions, is public by default. In Class, it is by default private.
* Each bject will consume memory for data members.
Functions, however, will be common for all the objects in memory.
* Operator overloading: e.g., + operator can be overloaded for adding: two matrices A + B, two complex numbers ...
* There are a rectangle class, a cuboid class from the rectangle, and a table class has a rectangle object inside.
- cuboid isA rectangle
- table hasA rectangle
* Access specifiers: public, private, protected
- Upon object, you can only access public members.
- A derived class cannot access private members of the base class, can access to protected and public.
Table:
Inside class: private ✅ protected ✅ public ✅
Inside derived class: private ❌ protected ✅ public ✅
On object: private ❌ protected ❌ public ✅
- Things are actually accessible but not directly modifiable.
e.g. d.length = 10 is not allowed, but getLength() can be used ...
* There are also multilevel and multiple inheritance (this one is not provided in java)
- multilevel: A <- B <- C
- multiple: A <-C, B <- C
* Generalization: the term "shape" is a generalization of e.g. circle, rectangle, triangle...
We cannot define a shape without the derived classes: shape itself does not exist.
So there is a bottom up relation: we find a general term to define circle, rectangle ...
Specialization: there exists a rectangle class, and we derived a cuboid class, which is a special
version of it. So, there is a top down approach.
- Purpose of generalization is polymorphism, purpose of specialization is share the features to child classes
In generalization, parent class doesn't have much to be inherited. Mostly conceptual.
* We declare a base class pointer and make it point to a child class object.
* Overriding: same functionality, but the function is defined in its own way.
* There are 3 types of base classes in C++ based on having the functions as concrete or virtual:
- All concrete -> reusability
- Some concrete, some pure virtual -> reusability, polymorphism (abstract)
- All pure virtual -> polymorphism (abstract)
* Runtime Polymorphism is achieved using base class pointer to derived class object and override method is called
* A friend function/class can help to access private and protected members of a class upon object.
This is useful in operator overloading.
* Static members can be used as:
- counters
- shared memory (so one object can write and others read)
- information about the class (same information for all objects)
* Static member functions can be called using class name and also using object
* Inner classes: useful for dividing the outer class into smaller pieces.
e.g., LinkedList class can have a Head class inside.
Be careful about its visibility, if it is defined as private, then not accessible from outside of the outer class.
* Errors:
Syntax -> Compiler (Programmer)
Logical -> Debugger (Programmer)
Runtime -> Exception handling (User: e.g., driver doesn't put enough fuel into the car, car need to warn...)
* One benefit of using exceptions: they allow communication between functions,
e.g. a function cannot finish its execution successfully because of a problem,
so it throws an exception instead of returning a certain value.
The caller function is notified this way that something went wrong.
* A lambda expression in C++ is an anonymous function (a function without a name) that can be used inline. It allows you to define small functions quickly without needing to declare them separately.
[ capture ] ( parameters ) -> return_type { function_body };
// -------------
auto greet = []() {
std::cout << "Hello, Lambda!" << std::endl;
};
greet(); // Calls the lambda function
// -------------
auto add = [](int a, int b) {
return a + b;
};
std::cout << add(3, 5); // Output: 8
* Smart pointers in C++ are wrapper classes around raw pointers that automate memory management by
ensuring that dynamically allocated memory is properly deallocated when it's no longer needed.
They help prevent memory leaks and dangling pointers.
Smart Pointer | Purpose | Usage
-----------------------------------------------------------------------------------------------------------------------------
std::unique_ptr<T> | Exclusive ownership (only one pointer can own the object at a time) | Fast and safe
std::shared_ptr<T> | Shared ownership (multiple pointers can share ownership) | Uses reference counting
std::weak_ptr<T> | Non-owning reference to a shared_ptr | Prevents circular references
* Ellipses: to be able to variable number of arguments
int sum(int n, ...)