mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-12 17:51:53 +08:00
Compare commits
1 Commits
94d4e06415
...
chore/sync
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42eda07c6a |
@@ -2,42 +2,42 @@
|
||||
|
||||
An access violation is a specific type of error that occurs when a program attempts to access an illegal memory location. In C++, access violations are most commonly caused by:
|
||||
|
||||
- **Dereferencing a null or invalid pointer.**
|
||||
- **Accessing an array out of bounds.**
|
||||
- **Reading or writing to memory freed by the user or the operating system.**
|
||||
* **Dereferencing a null or invalid pointer.**
|
||||
* **Accessing an array out of bounds.**
|
||||
* **Reading or writing to memory freed by the user or the operating system.**
|
||||
|
||||
It is crucial to identify access violations because they can lead to unpredictable behavior, application crashes, or corruption of data.
|
||||
|
||||
Some examples of access violations are:
|
||||
|
||||
## Dereferencing null or invalid pointer
|
||||
Dereferencing null or invalid pointer
|
||||
-------------------------------------
|
||||
|
||||
```cpp
|
||||
int *p = nullptr;
|
||||
int x = *p; // Access violation: trying to access null pointer's content
|
||||
```
|
||||
int *p = nullptr;
|
||||
int x = *p; // Access violation: trying to access null pointer's content
|
||||
|
||||
|
||||
## Accessing an array out of bounds
|
||||
Accessing an array out of bounds
|
||||
--------------------------------
|
||||
|
||||
```cpp
|
||||
int arr[5] = {1, 2, 3, 4, 5};
|
||||
int y = arr[5]; // Access violation: index out of bounds (valid indices are 0-4)
|
||||
```
|
||||
int arr[5] = {1, 2, 3, 4, 5};
|
||||
int y = arr[5]; // Access violation: index out of bounds (valid indices are 0-4)
|
||||
|
||||
|
||||
## Reading or writing to freed memory
|
||||
Reading or writing to freed memory
|
||||
----------------------------------
|
||||
|
||||
```cpp
|
||||
int* p2 = new int[10];
|
||||
delete[] p2;
|
||||
p2[3] = 42; // Access violation: writing to memory that has been freed
|
||||
```
|
||||
int* p2 = new int[10];
|
||||
delete[] p2;
|
||||
p2[3] = 42; // Access violation: writing to memory that has been freed
|
||||
|
||||
|
||||
### Debugging Access Violations
|
||||
|
||||
Tools like _debuggers_, _static analyzers_, and _profilers_ can help identify access violations in your code. For example:
|
||||
|
||||
* **Microsoft Visual Studio**: Use the built-in debugger to identify the line of code responsible for the access violation error.
|
||||
|
||||
* **Valgrind**: A popular Linux tool that detects memory leaks and access violations in your C++ programs.
|
||||
|
||||
* **AddressSanitizer**: A runtime memory error detector for C++ that can detect out-of-bounds accesses, memory leaks, and use-after-free errors.
|
||||
* **Microsoft Visual Studio**: Use the built-in debugger to identify the line of code responsible for the access violation error.
|
||||
|
||||
* **Valgrind**: A popular Linux tool that detects memory leaks and access violations in your C++ programs.
|
||||
|
||||
* **AddressSanitizer**: A runtime memory error detector for C++ that can detect out-of-bounds accesses, memory leaks, and use-after-free errors.
|
||||
@@ -2,88 +2,90 @@
|
||||
|
||||
The Standard Template Library (STL) in C++ provides a collection of generic algorithms that are designed to work with various container classes. These algorithms are implemented as functions and can be applied to different data structures, such as arrays, vectors, lists, and others. The primary header file for algorithms is `<algorithm>`.
|
||||
|
||||
## Key Concepts
|
||||
Key Concepts
|
||||
------------
|
||||
|
||||
## Sorting
|
||||
Sorting
|
||||
-------
|
||||
|
||||
Sorting refers to arranging a sequence of elements in a specific order. The STL provides several sorting algorithms, such as `std::sort`, `std::stable_sort`, and `std::partial_sort`.
|
||||
|
||||
### std::sort
|
||||
|
||||
`std::sort` is used to sort a range of elements [first, last) in non-descending order (by default). You can also use custom comparison functions or lambda expressions to change the sorting order.
|
||||
`std::sort` is used to sort a range of elements \[first, last) in non-descending order (by default). You can also use custom comparison functions or lambda expressions to change the sorting order.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {10, 9, 8, 7, 6, 5};
|
||||
std::sort(nums.begin(), nums.end());
|
||||
|
||||
for (int num : nums) {
|
||||
std::cout << num << ' ';
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {10, 9, 8, 7, 6, 5};
|
||||
std::sort(nums.begin(), nums.end());
|
||||
|
||||
for (int num : nums) {
|
||||
std::cout << num << ' ';
|
||||
}
|
||||
// Output: 5 6 7 8 9 10
|
||||
}
|
||||
// Output: 5 6 7 8 9 10
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Searching
|
||||
Searching
|
||||
---------
|
||||
|
||||
Searching refers to finding if a particular element is present within a given range of elements. STL provides various searching algorithms, such as `std::find`, `std::binary_search`, and `std::find_if`.
|
||||
|
||||
### std::find
|
||||
|
||||
`std::find` is used to find the iterator of the first occurrence of a given value within the range [first, last).
|
||||
`std::find` is used to find the iterator of the first occurrence of a given value within the range \[first, last).
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {5, 6, 7, 8, 9, 10};
|
||||
auto it = std::find(nums.begin(), nums.end(), 9);
|
||||
|
||||
if (it != nums.end()) {
|
||||
std::cout << "Found 9 at position: " << (it - nums.begin());
|
||||
} else {
|
||||
std::cout << "9 not found";
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {5, 6, 7, 8, 9, 10};
|
||||
auto it = std::find(nums.begin(), nums.end(), 9);
|
||||
|
||||
if (it != nums.end()) {
|
||||
std::cout << "Found 9 at position: " << (it - nums.begin());
|
||||
} else {
|
||||
std::cout << "9 not found";
|
||||
}
|
||||
// Output: Found 9 at position: 4
|
||||
}
|
||||
// Output: Found 9 at position: 4
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Modifying Sequences
|
||||
Modifying Sequences
|
||||
-------------------
|
||||
|
||||
The STL also provides algorithms for modifying sequences, such as `std::remove`, `std::replace`, and `std::unique`.
|
||||
|
||||
### std::remove
|
||||
|
||||
`std::remove` is used to remove all instances of a value from a container within the given range [first, last). Note that the function does not resize the container after removing elements.
|
||||
`std::remove` is used to remove all instances of a value from a container within the given range \[first, last). Note that the function does not resize the container after removing elements.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {5, 6, 7, 6, 8, 6, 9, 6, 10};
|
||||
nums.erase(std::remove(nums.begin(), nums.end(), 6), nums.end());
|
||||
|
||||
for (int num : nums) {
|
||||
std::cout << num << ' ';
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> nums = {5, 6, 7, 6, 8, 6, 9, 6, 10};
|
||||
nums.erase(std::remove(nums.begin(), nums.end(), 6), nums.end());
|
||||
|
||||
for (int num : nums) {
|
||||
std::cout << num << ' ';
|
||||
}
|
||||
// Output: 5 7 8 9 10
|
||||
}
|
||||
// Output: 5 7 8 9 10
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Summary
|
||||
Summary
|
||||
-------
|
||||
|
||||
STL algorithms in C++ provide a set of useful functions for key operations such as sorting, searching, and modifying sequences. The algorithms can be used with a variety of container classes, making them highly versatile and an essential part of C++ programming.
|
||||
STL algorithms in C++ provide a set of useful functions for key operations such as sorting, searching, and modifying sequences. The algorithms can be used with a variety of container classes, making them highly versatile and an essential part of C++ programming.
|
||||
@@ -4,29 +4,29 @@ Argument Dependent Lookup (ADL) or Koenig Lookup is a mechanism in C++ that allo
|
||||
|
||||
ADL allows the compiler to find functions in the same namespace as the arguments, even if the function is not declared at the point of use or within the namespace provided. This is especially useful when working with templates or generic programming.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
Consider the following example using a namespace and overloaded `operator<<()`:
|
||||
|
||||
```cpp
|
||||
namespace MyNamespace {
|
||||
class MyClass {
|
||||
public:
|
||||
int value;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
|
||||
os << "MyClass: " << obj.value;
|
||||
return os;
|
||||
namespace MyNamespace {
|
||||
class MyClass {
|
||||
public:
|
||||
int value;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
|
||||
os << "MyClass: " << obj.value;
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
MyNamespace::MyClass obj;
|
||||
obj.value = 42;
|
||||
using std::cout; // Required to use 'cout' without fully qualifying it.
|
||||
cout << obj << '\n'; // ADL is used to find the correct overloaded 'operator<<'.
|
||||
}
|
||||
```
|
||||
|
||||
int main() {
|
||||
MyNamespace::MyClass obj;
|
||||
obj.value = 42;
|
||||
using std::cout; // Required to use 'cout' without fully qualifying it.
|
||||
cout << obj << '\n'; // ADL is used to find the correct overloaded 'operator<<'.
|
||||
}
|
||||
|
||||
|
||||
In this example, when you call `cout << obj;` in `main()`, ADL is used to find the correct `operator<<()` in the `MyNamespace` namespace because the argument `obj` is of type `MyNamespace::MyClass`.
|
||||
@@ -2,65 +2,65 @@
|
||||
|
||||
Arithmetic operators are used to perform mathematical operations with basic variables such as integers and floating-point numbers. Here is a brief summary of the different arithmetic operators in C++:
|
||||
|
||||
## 1. Addition Operator (`+`)
|
||||
1\. Addition Operator (`+`)
|
||||
---------------------------
|
||||
|
||||
It adds two numbers together.
|
||||
|
||||
```cpp
|
||||
int sum = a + b;
|
||||
```
|
||||
int sum = a + b;
|
||||
|
||||
|
||||
## 2. Subtraction Operator (`-`)
|
||||
2\. Subtraction Operator (`-`)
|
||||
------------------------------
|
||||
|
||||
It subtracts one number from another.
|
||||
|
||||
```cpp
|
||||
int difference = a - b;
|
||||
```
|
||||
int difference = a - b;
|
||||
|
||||
|
||||
## 3. Multiplication Operator (`*`)
|
||||
3\. Multiplication Operator (`*`)
|
||||
---------------------------------
|
||||
|
||||
It multiplies two numbers together.
|
||||
|
||||
```cpp
|
||||
int product = a * b;
|
||||
```
|
||||
int product = a * b;
|
||||
|
||||
|
||||
## 4. Division Operator (`/`)
|
||||
4\. Division Operator (`/`)
|
||||
---------------------------
|
||||
|
||||
It divides one number by another. Note that if both operands are integers, it will perform integer division and the result will be an integer.
|
||||
|
||||
```cpp
|
||||
int quotient = a / b; // integer division
|
||||
float quotient = float(a) / float(b); // floating-point division
|
||||
```
|
||||
int quotient = a / b; // integer division
|
||||
float quotient = float(a) / float(b); // floating-point division
|
||||
|
||||
|
||||
## 5. Modulus Operator (`%`)
|
||||
5\. Modulus Operator (`%`)
|
||||
--------------------------
|
||||
|
||||
It calculates the remainder of an integer division.
|
||||
|
||||
```cpp
|
||||
int remainder = a % b;
|
||||
```
|
||||
int remainder = a % b;
|
||||
|
||||
|
||||
## 6. Increment Operator (`++`)
|
||||
6\. Increment Operator (`++`)
|
||||
-----------------------------
|
||||
|
||||
It increments the value of a variable by 1. There are two ways to use this operator: prefix (`++x`) and postfix (`x++`). Prefix increments the value before returning it, whereas postfix returns the value first and then increments it.
|
||||
|
||||
```cpp
|
||||
int x = 5;
|
||||
int y = ++x; // x = 6, y = 6
|
||||
int z = x++; // x = 7, z = 6
|
||||
```
|
||||
int x = 5;
|
||||
int y = ++x; // x = 6, y = 6
|
||||
int z = x++; // x = 7, z = 6
|
||||
|
||||
|
||||
## 7. Decrement Operator (`--`)
|
||||
7\. Decrement Operator (`--`)
|
||||
-----------------------------
|
||||
|
||||
It decrements the value of a variable by 1. It can also be used in prefix (`--x`) and postfix (`x--`) forms.
|
||||
|
||||
```cpp
|
||||
int x = 5;
|
||||
int y = --x; // x = 4, y = 4
|
||||
int z = x--; // x = 3, z = 4
|
||||
```
|
||||
int x = 5;
|
||||
int y = --x; // x = 4, y = 4
|
||||
int z = x--; // x = 3, z = 4
|
||||
|
||||
|
||||
These are the basic arithmetic operators in C++ that allow you to perform mathematical operations on your variables. Use them in combination with other control structures, such as loops and conditionals, to build more complex programs.
|
||||
@@ -8,42 +8,38 @@ The `auto` keyword is useful when you are dealing with complex types or when the
|
||||
|
||||
Here's a simple example of using `auto` for type deduction:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
// Traditional way of declaring a variable:
|
||||
int myInt = 5;
|
||||
|
||||
// Using auto for type deduction:
|
||||
auto myAutoInt = 5; // Automatically deduces the type as 'int'
|
||||
|
||||
// Example with more complex types:
|
||||
std::vector<int> myVector = {1, 2, 3, 4, 5};
|
||||
|
||||
// Without auto, iterating the vector would look like this:
|
||||
for (std::vector<int>::iterator it = myVector.begin(); it != myVector.end(); ++it) {
|
||||
std::cout << *it << '\n';
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
// Traditional way of declaring a variable:
|
||||
int myInt = 5;
|
||||
|
||||
// Using auto for type deduction:
|
||||
auto myAutoInt = 5; // Automatically deduces the type as 'int'
|
||||
|
||||
// Example with more complex types:
|
||||
std::vector<int> myVector = {1, 2, 3, 4, 5};
|
||||
|
||||
// Without auto, iterating the vector would look like this:
|
||||
for (std::vector<int>::iterator it = myVector.begin(); it != myVector.end(); ++it) {
|
||||
std::cout << *it << '\n';
|
||||
}
|
||||
|
||||
// With auto, the iterator declaration becomes simpler:
|
||||
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
|
||||
std::cout << *it << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// With auto, the iterator declaration becomes simpler:
|
||||
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
|
||||
std::cout << *it << '\n';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Keep in mind that `auto` deduces the type based on the initializer expression, so if you don't provide an initial value, you will get a compile-time error:
|
||||
|
||||
```cpp
|
||||
auto myVar; // Error: Cannot deduce the type without initializer
|
||||
```
|
||||
auto myVar; // Error: Cannot deduce the type without initializer
|
||||
|
||||
|
||||
In C++14, you can also use `auto` with function return types to let the compiler automatically deduce the return type based on the returned expression:
|
||||
|
||||
```cpp
|
||||
auto add(int x, int y) {
|
||||
return x + y; // The compiler deduces the return type as 'int'
|
||||
}
|
||||
```
|
||||
auto add(int x, int y) {
|
||||
return x + y; // The compiler deduces the return type as 'int'
|
||||
}
|
||||
@@ -4,105 +4,107 @@ Basic operations in C++ refer to the fundamental arithmetic, relational, and log
|
||||
|
||||
Here's a summary of the basic operations in C++
|
||||
|
||||
## Arithmetic Operations
|
||||
Arithmetic Operations
|
||||
---------------------
|
||||
|
||||
These operations are used for performing calculations in C++ and include the following:
|
||||
|
||||
- **Addition (+)**: Adds two numbers.
|
||||
```cpp
|
||||
int a = 5;
|
||||
int b = 6;
|
||||
int sum = a + b; // sum is 11
|
||||
```
|
||||
* **Addition (+)**: Adds two numbers.
|
||||
|
||||
- **Subtraction (-)**: Subtracts one number from the other.
|
||||
```cpp
|
||||
int a = 10;
|
||||
int b = 6;
|
||||
int diff = a - b; // diff is 4
|
||||
```
|
||||
int a = 5;
|
||||
int b = 6;
|
||||
int sum = a + b; // sum is 11
|
||||
|
||||
|
||||
- **Multiplication (*)**: Multiplies two numbers.
|
||||
```cpp
|
||||
int a = 3;
|
||||
int b = 4;
|
||||
int product = a * b; // product is 12
|
||||
```
|
||||
* **Subtraction (-)**: Subtracts one number from the other.
|
||||
|
||||
- **Division (/)**: Divides one number by another, yields quotient.
|
||||
```cpp
|
||||
int a = 12;
|
||||
int b = 4;
|
||||
int quotient = a / b; // quotient is 3
|
||||
```
|
||||
int a = 10;
|
||||
int b = 6;
|
||||
int diff = a - b; // diff is 4
|
||||
|
||||
|
||||
- **Modulus (%)**: Divides one number by another, yields remainder.
|
||||
```cpp
|
||||
int a = 15;
|
||||
int b = 4;
|
||||
int remainder = a % b; // remainder is 3
|
||||
```
|
||||
* **Multiplication (\*)**: Multiplies two numbers.
|
||||
|
||||
## Relational Operators
|
||||
int a = 3;
|
||||
int b = 4;
|
||||
int product = a * b; // product is 12
|
||||
|
||||
|
||||
* **Division (/)**: Divides one number by another, yields quotient.
|
||||
|
||||
int a = 12;
|
||||
int b = 4;
|
||||
int quotient = a / b; // quotient is 3
|
||||
|
||||
|
||||
* **Modulus (%)**: Divides one number by another, yields remainder.
|
||||
|
||||
int a = 15;
|
||||
int b = 4;
|
||||
int remainder = a % b; // remainder is 3
|
||||
|
||||
|
||||
Relational Operators
|
||||
--------------------
|
||||
|
||||
These operations compare two values and return a boolean value (true/false) depending on the comparison. The relational operations are:
|
||||
|
||||
- **Equal to (==)**: Returns true if both operands are equal.
|
||||
```cpp
|
||||
5 == 5 // true
|
||||
3 == 4 // false
|
||||
```
|
||||
* **Equal to (==)**: Returns true if both operands are equal.
|
||||
|
||||
- **Not equal to (!=)**: Returns true if operands are not equal.
|
||||
```cpp
|
||||
5 != 2 // true
|
||||
1 != 1 // false
|
||||
```
|
||||
5 == 5 // true
|
||||
3 == 4 // false
|
||||
|
||||
|
||||
- **Greater than (>)**: Returns true if the first operand is greater than the second.
|
||||
```cpp
|
||||
5 > 3 // true
|
||||
2 > 3 // false
|
||||
```
|
||||
* **Not equal to (!=)**: Returns true if operands are not equal.
|
||||
|
||||
- **Less than (<)**: Returns true if the first operand is less than the second.
|
||||
```cpp
|
||||
3 < 5 // true
|
||||
6 < 5 // false
|
||||
```
|
||||
5 != 2 // true
|
||||
1 != 1 // false
|
||||
|
||||
|
||||
- **Greater than or equal to (>=)**: Returns true if the first operand is greater than or equal to the second.
|
||||
```cpp
|
||||
5 >= 5 // true
|
||||
6 >= 2 // true
|
||||
3 >= 4 // false
|
||||
```
|
||||
* **Greater than (>)**: Returns true if the first operand is greater than the second.
|
||||
|
||||
- **Less than or equal to (<=)**: Returns true if the first operand is less than or equal to the second.
|
||||
```cpp
|
||||
4 <= 4 // true
|
||||
2 <= 3 // true
|
||||
5 <= 4 // false
|
||||
```
|
||||
5 > 3 // true
|
||||
2 > 3 // false
|
||||
|
||||
|
||||
## Logical Operators
|
||||
* **Less than (<)**: Returns true if the first operand is less than the second.
|
||||
|
||||
3 < 5 // true
|
||||
6 < 5 // false
|
||||
|
||||
|
||||
* **Greater than or equal to (>=)**: Returns true if the first operand is greater than or equal to the second.
|
||||
|
||||
5 >= 5 // true
|
||||
6 >= 2 // true
|
||||
3 >= 4 // false
|
||||
|
||||
|
||||
* **Less than or equal to (<=)**: Returns true if the first operand is less than or equal to the second.
|
||||
|
||||
4 <= 4 // true
|
||||
2 <= 3 // true
|
||||
5 <= 4 // false
|
||||
|
||||
|
||||
Logical Operators
|
||||
-----------------
|
||||
|
||||
Logical operators are used for combining multiple conditions or boolean values.
|
||||
|
||||
- **AND (&&)**: Returns true if both operands are true.
|
||||
```cpp
|
||||
true && true // true
|
||||
true && false // false
|
||||
```
|
||||
* **AND (&&)**: Returns true if both operands are true.
|
||||
|
||||
- **OR (||)**: Returns true if any one of the operands is true.
|
||||
```cpp
|
||||
true || false // true
|
||||
false || false // false
|
||||
```
|
||||
true && true // true
|
||||
true && false // false
|
||||
|
||||
|
||||
- **NOT (!)**: Returns true if the operand is false and vice versa.
|
||||
```cpp
|
||||
!true // false
|
||||
!false // true
|
||||
```
|
||||
* **OR (||)**: Returns true if any one of the operands is true.
|
||||
|
||||
true || false // true
|
||||
false || false // false
|
||||
|
||||
|
||||
* **NOT (!)**: Returns true if the operand is false and vice versa.
|
||||
|
||||
!true // false
|
||||
!false // true
|
||||
@@ -4,69 +4,69 @@ Bitwise operations are operations that directly manipulate the bits of a number.
|
||||
|
||||
Here is a quick summary of common bitwise operations in C++:
|
||||
|
||||
## Bitwise AND (`&`)
|
||||
Bitwise AND (`&`)
|
||||
-----------------
|
||||
|
||||
The bitwise AND operation (`&`) is a binary operation that takes two numbers, compares them bit by bit, and returns a new number where each bit is set (1) if the corresponding bits in both input numbers are set (1); otherwise, the bit is unset (0).
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
int result = 5 & 3; // result will be 1 (0000 0101 & 0000 0011 = 0000 0001)
|
||||
```
|
||||
int result = 5 & 3; // result will be 1 (0000 0101 & 0000 0011 = 0000 0001)
|
||||
|
||||
|
||||
## Bitwise OR (`|`)
|
||||
Bitwise OR (`|`)
|
||||
----------------
|
||||
|
||||
The bitwise OR operation (`|`) is a binary operation that takes two numbers, compares them bit by bit, and returns a new number where each bit is set (1) if at least one of the corresponding bits in either input number is set (1); otherwise, the bit is unset (0).
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
int result = 5 | 3; // result will be 7 (0000 0101 | 0000 0011 = 0000 0111)
|
||||
```
|
||||
int result = 5 | 3; // result will be 7 (0000 0101 | 0000 0011 = 0000 0111)
|
||||
|
||||
|
||||
## Bitwise XOR (`^`)
|
||||
Bitwise XOR (`^`)
|
||||
-----------------
|
||||
|
||||
The bitwise XOR (exclusive OR) operation (`^`) is a binary operation that takes two numbers, compares them bit by bit, and returns a new number where each bit is set (1) if the corresponding bits in the input numbers are different; otherwise, the bit is unset (0).
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
int result = 5 ^ 3; // result will be 6 (0000 0101 ^ 0000 0011 = 0000 0110)
|
||||
```
|
||||
int result = 5 ^ 3; // result will be 6 (0000 0101 ^ 0000 0011 = 0000 0110)
|
||||
|
||||
|
||||
## Bitwise NOT (`~`)
|
||||
Bitwise NOT (`~`)
|
||||
-----------------
|
||||
|
||||
The bitwise NOT operation (`~`) is a unary operation that takes a single number, and returns a new number where each bit is inverted (1 becomes 0, and 0 becomes 1).
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
int result = ~5; // result will be -6 (1111 1010)
|
||||
```
|
||||
int result = ~5; // result will be -6 (1111 1010)
|
||||
|
||||
|
||||
## Bitwise Left Shift (`<<`)
|
||||
Bitwise Left Shift (`<<`)
|
||||
-------------------------
|
||||
|
||||
The bitwise left shift operation (`<<`) is a binary operation that takes two numbers, a value and a shift amount, and returns a new number by shifting the bits of the value to the left by the specified shift amount. The vacated bits are filled with zeros.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
int result = 5 << 1; // result will be 10 (0000 0101 << 1 = 0000 1010)
|
||||
```
|
||||
int result = 5 << 1; // result will be 10 (0000 0101 << 1 = 0000 1010)
|
||||
|
||||
|
||||
## Bitwise Right Shift (`>>`)
|
||||
Bitwise Right Shift (`>>`)
|
||||
--------------------------
|
||||
|
||||
The bitwise right shift operation (`>>`) is a binary operation that takes two numbers, a value and a shift amount, and returns a new number by shifting the bits of the value to the right by the specified shift amount. The vacated bits are filled with zeros or sign bit depending on the input value being signed or unsigned.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
int result = 5 >> 1; // result will be 2 (0000 0101 >> 1 = 0000 0010)
|
||||
```
|
||||
int result = 5 >> 1; // result will be 2 (0000 0101 >> 1 = 0000 0010)
|
||||
|
||||
|
||||
These were the most common bitwise operations in C++. Remember to use them carefully and understand their behavior when applied to specific data types and scenarios.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@video@Intro to Binary and Bitwise Operators in C++](https://youtu.be/KXwRt7og0gI)
|
||||
- [@video@Bitwise AND (&), OR (|), XOR (^) and NOT (~) in C++](https://youtu.be/HoQhw6_1NAA)
|
||||
- [@video@Bitwise AND (&), OR (|), XOR (^) and NOT (~) in C++](https://youtu.be/HoQhw6_1NAA)
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,71 +2,69 @@
|
||||
|
||||
A build system is a collection of tools and utilities that automate the process of compiling, linking, and executing source code files in a project. The primary goal of build systems is to manage the complexity of the compilation process and produce a build (executable or binary files) in the end. In C++ (cpp), some common build systems are:
|
||||
|
||||
- **GNU Make**: It is a popular build system that uses `Makefile` to define the build process. It checks the dependencies and timestamps of source files to determine which files need to be compiled and linked.
|
||||
* **GNU Make**: It is a popular build system that uses `Makefile` to define the build process. It checks the dependencies and timestamps of source files to determine which files need to be compiled and linked.
|
||||
|
||||
Code example:
|
||||
|
||||
# Makefile
|
||||
CXX = g++
|
||||
CPPFLAGS = -Wall -std=c++11
|
||||
TARGET = HelloWorld
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): main.cpp
|
||||
$(CXX) $(CPPFLAGS)main.cpp -o $(TARGET)
|
||||
|
||||
clean:
|
||||
rm $(TARGET)
|
||||
|
||||
|
||||
* **CMake**: It is a cross-platform build system that focuses on defining project dependencies and managing build environments. CMake generates build files (like Makefiles) for different platforms and allows developers to write source code once and then compile it for different target platforms.
|
||||
|
||||
Code example:
|
||||
|
||||
# CMakeLists.txt
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(HelloWorld)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
add_executable(HelloWorld main.cpp)
|
||||
|
||||
|
||||
* **Autotools**: Also known as GNU Build System, consists of the GNU Autoconf, Automake, and Libtool tools that enable developers to create portable software across different Unix-based systems. For a C++ project, you will need to create `configure.ac`, `Makefile.am` files with specific rules, and then run the following commands in the terminal to build the project:
|
||||
|
||||
autoreconf --install
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
|
||||
* **SCons**: This build system uses Python for build scripts, making it more expressive than GNU Make. It can also build for multiple platforms and configurations simultaneously.
|
||||
|
||||
Code example:
|
||||
|
||||
# SConstruct
|
||||
env = Environment()
|
||||
env.Program(target="HelloWorld", source=["main.cpp"])
|
||||
|
||||
|
||||
* **Ninja**: A small and focused build system that takes a list of build targets specified in a human-readable text file and builds them as fast as possible.
|
||||
|
||||
Code example:
|
||||
|
||||
# build.ninja
|
||||
rule cc
|
||||
command = g++ -c $in -o $out
|
||||
|
||||
rule link
|
||||
command = g++ $in -o $out
|
||||
|
||||
build main.o: cc main.cpp
|
||||
build HelloWorld: link main.o
|
||||
default HelloWorld
|
||||
|
||||
|
||||
|
||||
Code example:
|
||||
|
||||
```
|
||||
# Makefile
|
||||
CXX = g++
|
||||
CPPFLAGS = -Wall -std=c++11
|
||||
TARGET = HelloWorld
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): main.cpp
|
||||
$(CXX) $(CPPFLAGS)main.cpp -o $(TARGET)
|
||||
|
||||
clean:
|
||||
rm $(TARGET)
|
||||
```
|
||||
|
||||
- **CMake**: It is a cross-platform build system that focuses on defining project dependencies and managing build environments. CMake generates build files (like Makefiles) for different platforms and allows developers to write source code once and then compile it for different target platforms.
|
||||
|
||||
Code example:
|
||||
|
||||
```
|
||||
# CMakeLists.txt
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(HelloWorld)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
add_executable(HelloWorld main.cpp)
|
||||
```
|
||||
- **Autotools**: Also known as GNU Build System, consists of the GNU Autoconf, Automake, and Libtool tools that enable developers to create portable software across different Unix-based systems. For a C++ project, you will need to create `configure.ac`, `Makefile.am` files with specific rules, and then run the following commands in the terminal to build the project:
|
||||
|
||||
```
|
||||
autoreconf --install
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
- **SCons**: This build system uses Python for build scripts, making it more expressive than GNU Make. It can also build for multiple platforms and configurations simultaneously.
|
||||
|
||||
Code example:
|
||||
|
||||
```
|
||||
# SConstruct
|
||||
env = Environment()
|
||||
env.Program(target="HelloWorld", source=["main.cpp"])
|
||||
```
|
||||
|
||||
- **Ninja**: A small and focused build system that takes a list of build targets specified in a human-readable text file and builds them as fast as possible.
|
||||
|
||||
Code example:
|
||||
|
||||
```
|
||||
# build.ninja
|
||||
rule cc
|
||||
command = g++ -c $in -o $out
|
||||
|
||||
rule link
|
||||
command = g++ $in -o $out
|
||||
|
||||
build main.o: cc main.cpp
|
||||
build HelloWorld: link main.o
|
||||
default HelloWorld
|
||||
```
|
||||
These are some of the popular build systems in C++, each with their own syntax and capabilities. While Make is widely used, CMake is a cross-platform build system that generates build files for other build systems like Make or Ninja. Autotools is suitable for creating portable software, SCons leverages Python for its build scripts, and Ninja focuses on fast build times.
|
||||
@@ -4,71 +4,64 @@
|
||||
|
||||
Some of the notable features in C++11 include:
|
||||
|
||||
- **Auto** keyword for automatic type inference.
|
||||
|
||||
```cpp
|
||||
auto i = 42; // i is an int
|
||||
auto s = "hello"; // s is a const char*
|
||||
```
|
||||
|
||||
- **Range-based for loop** for easier iteration over containers.
|
||||
|
||||
```cpp
|
||||
std::vector<int> vec = {1, 2, 3};
|
||||
for (int i : vec) {
|
||||
std::cout << i << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
- **Lambda functions** for creating anonymous functions.
|
||||
|
||||
```cpp
|
||||
auto add = [](int a, int b) { return a + b; };
|
||||
int result = add(3, 4); // result is 7
|
||||
```
|
||||
|
||||
- **nullptr** for representing null pointer values, instead of using `NULL`.
|
||||
|
||||
```cpp
|
||||
int* p = nullptr;
|
||||
```
|
||||
|
||||
- **Rvalue references and move semantics** to optimize the handling of temporary objects.
|
||||
|
||||
```cpp
|
||||
std::string str1 = "hello";
|
||||
std::string str2 = std::move(str1); // move the content of str1 to str2
|
||||
```
|
||||
|
||||
- **Variadic templates** for creating templates that take a variable number of arguments.
|
||||
|
||||
```cpp
|
||||
template <typename... Args>
|
||||
void printArgs(Args... args) {
|
||||
// function body
|
||||
}
|
||||
```
|
||||
|
||||
- **Static assertions** for compile-time assertions.
|
||||
|
||||
```cpp
|
||||
static_assert(sizeof(int) == 4, "This code requires int to be 4 bytes.");
|
||||
```
|
||||
|
||||
- **Thread support** for multithreading programming.
|
||||
|
||||
```cpp
|
||||
#include <thread>
|
||||
|
||||
void my_function() {
|
||||
// thread function body
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(my_function);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
* **Auto** keyword for automatic type inference.
|
||||
|
||||
auto i = 42; // i is an int
|
||||
auto s = "hello"; // s is a const char*
|
||||
|
||||
|
||||
* **Range-based for loop** for easier iteration over containers.
|
||||
|
||||
std::vector<int> vec = {1, 2, 3};
|
||||
for (int i : vec) {
|
||||
std::cout << i << '\n';
|
||||
}
|
||||
|
||||
|
||||
* **Lambda functions** for creating anonymous functions.
|
||||
|
||||
auto add = [](int a, int b) { return a + b; };
|
||||
int result = add(3, 4); // result is 7
|
||||
|
||||
|
||||
* **nullptr** for representing null pointer values, instead of using `NULL`.
|
||||
|
||||
int* p = nullptr;
|
||||
|
||||
|
||||
* **Rvalue references and move semantics** to optimize the handling of temporary objects.
|
||||
|
||||
std::string str1 = "hello";
|
||||
std::string str2 = std::move(str1); // move the content of str1 to str2
|
||||
|
||||
|
||||
* **Variadic templates** for creating templates that take a variable number of arguments.
|
||||
|
||||
template <typename... Args>
|
||||
void printArgs(Args... args) {
|
||||
// function body
|
||||
}
|
||||
|
||||
|
||||
* **Static assertions** for compile-time assertions.
|
||||
|
||||
static_assert(sizeof(int) == 4, "This code requires int to be 4 bytes.");
|
||||
|
||||
|
||||
* **Thread support** for multithreading programming.
|
||||
|
||||
#include <thread>
|
||||
|
||||
void my_function() {
|
||||
// thread function body
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(my_function);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
These are just a few examples of the many new features introduced in C++11. For a comprehensive list, you can refer to the [C++11 documentation](https://en.cppreference.com/w/cpp/11).
|
||||
@@ -1,64 +1,62 @@
|
||||
# C++11 and C++14 ###
|
||||
# C++11 and C++14
|
||||
|
||||
**C++11**
|
||||
The C++11 standard, also known as C++0x, was officially released in September 2011. It introduced several new language features and improvements, including:
|
||||
**C++11** The C++11 standard, also known as C++0x, was officially released in September 2011. It introduced several new language features and improvements, including:
|
||||
|
||||
- **Auto**: Allows compiler to infer the variable type based on its initializing expression.
|
||||
```cpp
|
||||
auto integer = 42; // integer is of int type
|
||||
auto floating = 3.14; // floating is of double type
|
||||
```
|
||||
* **Auto**: Allows compiler to infer the variable type based on its initializing expression.
|
||||
|
||||
auto integer = 42; // integer is of int type
|
||||
auto floating = 3.14; // floating is of double type
|
||||
|
||||
|
||||
* **Range-Based for Loop**: Provides foreach-like semantics for iterating through a container or array.
|
||||
|
||||
std::vector<int> numbers {1, 2, 3, 4};
|
||||
for (int number : numbers) {
|
||||
std::cout << number << '\n';
|
||||
}
|
||||
|
||||
|
||||
* **Lambda Functions**: Anonymous functions that allow the creation of function objects more easily.
|
||||
|
||||
auto add = [](int a, int b) -> int { return a + b; };
|
||||
int sum = add(42, 13); // sum is equal to 55
|
||||
|
||||
|
||||
* **nullptr**: A new keyword to represent null pointers, more type-safe than using a literal '0' or "NULL".
|
||||
|
||||
int *ptr = nullptr;
|
||||
|
||||
|
||||
* **Thread Support Library**: Provides a standard way to work with threads and synchronize data access across threads.
|
||||
|
||||
std::thread t([]() { std::cout << "Hello from another thread\n"; });
|
||||
t.join();
|
||||
|
||||
|
||||
|
||||
- **Range-Based for Loop**: Provides foreach-like semantics for iterating through a container or array.
|
||||
```cpp
|
||||
std::vector<int> numbers {1, 2, 3, 4};
|
||||
for (int number : numbers) {
|
||||
std::cout << number << '\n';
|
||||
}
|
||||
```
|
||||
**C++14** The C++14 standard was officially released in December 2014 as a small extension over C++11, focusing more on fine-tuning language features and fixing issues. Some of the new features introduced:
|
||||
|
||||
- **Lambda Functions**: Anonymous functions that allow the creation of function objects more easily.
|
||||
```cpp
|
||||
auto add = [](int a, int b) -> int { return a + b; };
|
||||
int sum = add(42, 13); // sum is equal to 55
|
||||
```
|
||||
|
||||
- **nullptr**: A new keyword to represent null pointers, more type-safe than using a literal '0' or "NULL".
|
||||
```cpp
|
||||
int *ptr = nullptr;
|
||||
```
|
||||
|
||||
- **Thread Support Library**: Provides a standard way to work with threads and synchronize data access across threads.
|
||||
```cpp
|
||||
std::thread t([]() { std::cout << "Hello from another thread\n"; });
|
||||
t.join();
|
||||
```
|
||||
|
||||
**C++14**
|
||||
The C++14 standard was officially released in December 2014 as a small extension over C++11, focusing more on fine-tuning language features and fixing issues. Some of the new features introduced:
|
||||
|
||||
- **Generic Lambdas**: Allows lambda function parameters to be declared with 'auto' type placeholders.
|
||||
```cpp
|
||||
auto add = [](auto a, auto b) { return a + b; };
|
||||
auto sum_i = add(42, 13); // Still works with integers
|
||||
auto sum_f = add(3.14, 2.72); // Now works with doubles too
|
||||
```
|
||||
|
||||
- **Binary Literals**: Allow you to input integers as binary literals for better readability.
|
||||
```cpp
|
||||
int b = 0b110101; // Decimal value is 53
|
||||
```
|
||||
|
||||
- **decltype(auto)**: Deduces the type of variable to match that of the expression it is initialized with.
|
||||
```cpp
|
||||
auto func = [](auto a, auto b) { return a * b; };
|
||||
decltype(auto) result = func(5, 3.14); // decltype(auto) deduces to "double"
|
||||
```
|
||||
|
||||
- **Variable Templates**: Allows you to define variables with template parameters.
|
||||
```cpp
|
||||
template <typename T>
|
||||
constexpr T pi = T(3.1415926535897932385);
|
||||
float r = pi<float>; // Instantiated as a float
|
||||
double d = pi<double>; // Instantiated as a double
|
||||
```
|
||||
* **Generic Lambdas**: Allows lambda function parameters to be declared with 'auto' type placeholders.
|
||||
|
||||
auto add = [](auto a, auto b) { return a + b; };
|
||||
auto sum_i = add(42, 13); // Still works with integers
|
||||
auto sum_f = add(3.14, 2.72); // Now works with doubles too
|
||||
|
||||
|
||||
* **Binary Literals**: Allow you to input integers as binary literals for better readability.
|
||||
|
||||
int b = 0b110101; // Decimal value is 53
|
||||
|
||||
|
||||
* **decltype(auto)**: Deduces the type of variable to match that of the expression it is initialized with.
|
||||
|
||||
auto func = [](auto a, auto b) { return a * b; };
|
||||
decltype(auto) result = func(5, 3.14); // decltype(auto) deduces to "double"
|
||||
|
||||
|
||||
* **Variable Templates**: Allows you to define variables with template parameters.
|
||||
|
||||
template <typename T>
|
||||
constexpr T pi = T(3.1415926535897932385);
|
||||
float r = pi<float>; // Instantiated as a float
|
||||
double d = pi<double>; // Instantiated as a double
|
||||
@@ -2,60 +2,63 @@
|
||||
|
||||
C++17, also known as C++1z, is the version of the C++ programming language published in December 2017. It builds upon the previous standard, C++14, and adds various new features and enhancements to improve the language's expressiveness, performance, and usability.
|
||||
|
||||
## Key Features:
|
||||
- If-init-statement: Introduces a new syntax for writing conditions with scope inside if and switch statements.
|
||||
```cpp
|
||||
if (auto it = map.find(key); it != map.end())
|
||||
{
|
||||
// Use it
|
||||
}
|
||||
```
|
||||
Key Features:
|
||||
-------------
|
||||
|
||||
- Structured Binding Declarations: Simplify the process of unpacking a tuple, pair, or other aggregate types.
|
||||
```cpp
|
||||
map<string, int> data;
|
||||
auto [iter, success] = data.emplace("example", 42);
|
||||
```
|
||||
* If-init-statement: Introduces a new syntax for writing conditions with scope inside if and switch statements.
|
||||
|
||||
- Inline variables: Enables `inline` keyword for variables and allows single definition of global and class static variables in header files.
|
||||
```cpp
|
||||
inline int globalVar = 0;
|
||||
```
|
||||
|
||||
- Folds expressions: Introduce fold expressions for variadic templates.
|
||||
```cpp
|
||||
template <typename... Ts>
|
||||
auto sum(Ts... ts)
|
||||
{
|
||||
return (ts + ...);
|
||||
}
|
||||
```
|
||||
|
||||
- constexpr if statement: Allows conditional compilation during compile time.
|
||||
```cpp
|
||||
template <typename T>
|
||||
auto get_value(T t)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
if (auto it = map.find(key); it != map.end())
|
||||
{
|
||||
return *t;
|
||||
// Use it
|
||||
}
|
||||
else
|
||||
|
||||
|
||||
* Structured Binding Declarations: Simplify the process of unpacking a tuple, pair, or other aggregate types.
|
||||
|
||||
map<string, int> data;
|
||||
auto [iter, success] = data.emplace("example", 42);
|
||||
|
||||
|
||||
* Inline variables: Enables `inline` keyword for variables and allows single definition of global and class static variables in header files.
|
||||
|
||||
inline int globalVar = 0;
|
||||
|
||||
|
||||
* Folds expressions: Introduce fold expressions for variadic templates.
|
||||
|
||||
template <typename... Ts>
|
||||
auto sum(Ts... ts)
|
||||
{
|
||||
return t;
|
||||
return (ts + ...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
- Improved lambda expression: Allows lambda to capture a single object without changing its type or constness.
|
||||
```cpp
|
||||
auto func = [x = std::move(obj)] { /* use x */ };
|
||||
```
|
||||
* constexpr if statement: Allows conditional compilation during compile time.
|
||||
|
||||
- Standard file system library: `std::filesystem` as a standardized way to manipulate paths, directories, and files.
|
||||
template <typename T>
|
||||
auto get_value(T t)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
{
|
||||
return *t;
|
||||
}
|
||||
else
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- New Standard Library additions: `<string_view>` (non-owning string reference), `<any>` (type-erased container), `<optional>` (optional value wrapper), `<variant>` (type-safe discriminated union / sum type), and `<memory_resource>` (library for polymorphic allocators).
|
||||
* Improved lambda expression: Allows lambda to capture a single object without changing its type or constness.
|
||||
|
||||
- Parallel Algorithms: Adds support for parallel execution of Standard Library algorithms.
|
||||
auto func = [x = std::move(obj)] { /* use x */ };
|
||||
|
||||
|
||||
This is a brief summary of the key features of C++17; it includes more features and library updates. For a complete list, you can refer to the [full list of C++17 features and changes](https://en.cppreference.com/w/cpp/17).
|
||||
* Standard file system library: `std::filesystem` as a standardized way to manipulate paths, directories, and files.
|
||||
|
||||
* New Standard Library additions: `<string_view>` (non-owning string reference), `<any>` (type-erased container), `<optional>` (optional value wrapper), `<variant>` (type-safe discriminated union / sum type), and `<memory_resource>` (library for polymorphic allocators).
|
||||
|
||||
* Parallel Algorithms: Adds support for parallel execution of Standard Library algorithms.
|
||||
|
||||
|
||||
This is a brief summary of the key features of C++17; it includes more features and library updates. For a complete list, you can refer to the [full list of C++17 features and changes](https://en.cppreference.com/w/cpp/17).
|
||||
@@ -4,81 +4,81 @@ C++20 is the latest standard of the C++ programming language, which brings signi
|
||||
|
||||
Here are some of the key features introduced in C++20:
|
||||
|
||||
## Concepts
|
||||
Concepts
|
||||
--------
|
||||
|
||||
Concepts are a way to enforce specific requirements on template parameters, allowing you to write more expressive and understandable code. They improve the error messages when using templates and ensure that the template parameters fulfill specific criteria.
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
concept Addable = requires (T a, T b) {
|
||||
{ a + b } -> std::same_as<T>;
|
||||
};
|
||||
template <typename T>
|
||||
concept Addable = requires (T a, T b) {
|
||||
{ a + b } -> std::same_as<T>;
|
||||
};
|
||||
|
||||
template <Addable T>
|
||||
T add(T a, T b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
template <Addable T>
|
||||
T add(T a, T b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
## Ranges
|
||||
Ranges
|
||||
------
|
||||
|
||||
Ranges provide a new way to work with sequences of values, enhancing the power and expressiveness of the Standard Library algorithms. The range-based algorithms make it easier and more convenient to work with sequences.
|
||||
|
||||
```cpp
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
std::vector<int> numbers = { 1, 2, 3, 4, 5 };
|
||||
|
||||
auto even_numbers = numbers | std::views::filter([](int n) { return n % 2 == 0; });
|
||||
|
||||
for (int n : even_numbers) {
|
||||
std::cout << n << ' ';
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
std::vector<int> numbers = { 1, 2, 3, 4, 5 };
|
||||
|
||||
auto even_numbers = numbers | std::views::filter([](int n) { return n % 2 == 0; });
|
||||
|
||||
for (int n : even_numbers) {
|
||||
std::cout << n << ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Coroutines
|
||||
Coroutines
|
||||
----------
|
||||
|
||||
Coroutines are a new way to write asynchronous and concurrent code with improved readability. They allow functions to be suspended and resumed, enabling you to write more efficient, non-blocking code.
|
||||
|
||||
```cpp
|
||||
#include <coroutine>
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
#include <coroutine>
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
|
||||
std::future<int> async_value(int value) {
|
||||
co_await std::chrono::seconds(1);
|
||||
co_return value * 2;
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto result = async_value(42);
|
||||
std::cout << "Result: " << result.get() << '\n';
|
||||
}
|
||||
|
||||
|
||||
std::future<int> async_value(int value) {
|
||||
co_await std::chrono::seconds(1);
|
||||
co_return value * 2;
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto result = async_value(42);
|
||||
std::cout << "Result: " << result.get() << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
## The `constexpr` and `consteval` Keywords
|
||||
The `constexpr` and `consteval` Keywords
|
||||
----------------------------------------
|
||||
|
||||
Both `constexpr` and `consteval` are related to compile-time evaluation. Functions marked with `constexpr` can be executed at compile-time or runtime, while functions marked with `consteval` can only be executed at compile-time.
|
||||
|
||||
```cpp
|
||||
constexpr int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
consteval int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr int result1 = add(3, 4); // evaluated at compile-time
|
||||
int result2 = add(5, 6); // evaluated at runtime
|
||||
constexpr int result3 = square(7); // evaluated at compile-time
|
||||
}
|
||||
```
|
||||
constexpr int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
consteval int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr int result1 = add(3, 4); // evaluated at compile-time
|
||||
int result2 = add(5, 6); // evaluated at runtime
|
||||
constexpr int result3 = square(7); // evaluated at compile-time
|
||||
}
|
||||
|
||||
|
||||
These are just some of the highlights of the C++20 standard. It also includes many other features and improvements, like structured bindings, improved lambdas, and new standard library components. Overall, C++20 makes it easier for developers to write clean, efficient, and expressive code.
|
||||
@@ -1,71 +1,80 @@
|
||||
# C vs C++
|
||||
|
||||
C and C++ are two popular programming languages with some similarities, but they also have key differences. C++ is an extension of the C programming language, with added features such as object-oriented programming, classes, and exception handling. Although both languages are used for similar tasks, they have their own syntax and semantics, which makes them distinct from each other.
|
||||
|
||||
## Syntax and Semantics
|
||||
Syntax and Semantics
|
||||
--------------------
|
||||
|
||||
### C
|
||||
- C is a procedural programming language.
|
||||
- Focuses on functions and structured programming.
|
||||
- Does not support objects or classes.
|
||||
- Memory management is manual, using functions like `malloc` and `free`.
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
* C is a procedural programming language.
|
||||
* Focuses on functions and structured programming.
|
||||
* Does not support objects or classes.
|
||||
* Memory management is manual, using functions like `malloc` and `free`.
|
||||
|
||||
void printHello() {
|
||||
printf("Hello, World!\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
printHello();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### C++
|
||||
- C++ is both procedural and object-oriented.
|
||||
- Supports both functions and classes.
|
||||
- Incorporates different programming paradigms.
|
||||
- Memory management can be manual (like C) or rely on constructors/destructors and smart pointers.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
class HelloWorld {
|
||||
public:
|
||||
#include <stdio.h>
|
||||
|
||||
void printHello() {
|
||||
std::cout << "Hello, World!\n";
|
||||
printf("Hello, World!\n");
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloWorld obj;
|
||||
obj.printHello();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Code Reusability and Modularity
|
||||
|
||||
### C
|
||||
- Code reusability is achieved through functions and modular programming.
|
||||
- High cohesion and low coupling are achieved via structured design.
|
||||
- Function libraries can be created and included through headers.
|
||||
|
||||
int main() {
|
||||
printHello();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
### C++
|
||||
- Offers better code reusability with classes, inheritance, and polymorphism.
|
||||
- Code modularity is enhanced through namespaces and well-designed object-oriented hierarchy.
|
||||
|
||||
## Error Handling
|
||||
* C++ is both procedural and object-oriented.
|
||||
* Supports both functions and classes.
|
||||
* Incorporates different programming paradigms.
|
||||
* Memory management can be manual (like C) or rely on constructors/destructors and smart pointers.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class HelloWorld {
|
||||
public:
|
||||
void printHello() {
|
||||
std::cout << "Hello, World!\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
HelloWorld obj;
|
||||
obj.printHello();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Code Reusability and Modularity
|
||||
-------------------------------
|
||||
|
||||
### C
|
||||
- Error handling in C is done primarily through return codes.
|
||||
- Lacks support for exceptions or any built-in error handling mechanism.
|
||||
|
||||
* Code reusability is achieved through functions and modular programming.
|
||||
* High cohesion and low coupling are achieved via structured design.
|
||||
* Function libraries can be created and included through headers.
|
||||
|
||||
### C++
|
||||
- Offers exception handling, which can be used to handle errors that may occur during program execution.
|
||||
- Enables catching and handling exceptions with `try`, `catch`, and `throw` keywords, providing more control over error handling.
|
||||
|
||||
## Conclusion
|
||||
* Offers better code reusability with classes, inheritance, and polymorphism.
|
||||
* Code modularity is enhanced through namespaces and well-designed object-oriented hierarchy.
|
||||
|
||||
Error Handling
|
||||
--------------
|
||||
|
||||
### C
|
||||
|
||||
* Error handling in C is done primarily through return codes.
|
||||
* Lacks support for exceptions or any built-in error handling mechanism.
|
||||
|
||||
### C++
|
||||
|
||||
* Offers exception handling, which can be used to handle errors that may occur during program execution.
|
||||
* Enables catching and handling exceptions with `try`, `catch`, and `throw` keywords, providing more control over error handling.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
Both C and C++ are powerful languages with unique features and capabilities. While C is simpler and focuses on procedural programming, C++ offers the versatility of using different programming paradigms and improved code organization. Understanding the differences between these two languages can help you decide which one is more suitable for your specific needs and programming style.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,58 +2,55 @@
|
||||
|
||||
CMake is a powerful cross-platform build system that generates build files, Makefiles, or workspaces for various platforms and compilers. Unlike the others build systems, CMake does not actually build the project, it only generates the files needed by build tools. CMake is widely used, particularly in C++ projects, for its ease of use and flexibility.
|
||||
|
||||
## CMakeLists.txt
|
||||
CMakeLists.txt
|
||||
--------------
|
||||
|
||||
CMake uses a file called `CMakeLists.txt` to define settings, source files, libraries, and other configurations. A typical `CMakeLists.txt` for a simple project would look like:
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(MyProject)
|
||||
|
||||
set(SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/src")
|
||||
set(SOURCES "${SRC_DIR}/main.cpp" "${SRC_DIR}/file1.cpp" "${SRC_DIR}/file2.cpp")
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/include")
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
||||
|
||||
|
||||
project(MyProject)
|
||||
|
||||
set(SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/src")
|
||||
set(SOURCES "${SRC_DIR}/main.cpp" "${SRC_DIR}/file1.cpp" "${SRC_DIR}/file2.cpp")
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/include")
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
||||
```
|
||||
|
||||
## Building with CMake
|
||||
Building with CMake
|
||||
-------------------
|
||||
|
||||
Here is an example of a simple build process using CMake:
|
||||
|
||||
- Create a new directory for the build.
|
||||
* Create a new directory for the build.
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
|
||||
- Generate build files using CMake.
|
||||
* Generate build files using CMake.
|
||||
|
||||
```sh
|
||||
cmake ..
|
||||
```
|
||||
cmake ..
|
||||
|
||||
|
||||
In this example, `..` indicates the parent directory where `CMakeLists.txt` is located. The build files will be generated in the `build` directory.
|
||||
|
||||
- Build the project using the generated build files.
|
||||
* Build the project using the generated build files.
|
||||
|
||||
```sh
|
||||
make
|
||||
```
|
||||
make
|
||||
|
||||
|
||||
Or, on Windows with Visual Studio, you may use:
|
||||
|
||||
```sh
|
||||
msbuild MyProject.sln
|
||||
```
|
||||
msbuild MyProject.sln
|
||||
|
||||
|
||||
CMake makes it easy to manage large projects, define custom build configurations, and work with many different compilers and operating systems. Making it a widely chosen tool for managing build systems in C++ projects.
|
||||
@@ -2,19 +2,20 @@
|
||||
|
||||
Code editors and IDEs are programs specifically designed for editing, managing and writing source code. They offer a wide range of features that make the development process easier and faster. Here's a brief introduction to some of the most popular code editors and IDEs for C++:
|
||||
|
||||
- **Visual Studio**: Visual Studio is an Integrated Development Environment (IDE) for Windows, developed by Microsoft. It includes its own integrated compiler known as Microsoft Visual C++ (MSVC).
|
||||
|
||||
- **Visual Studio Code (VSCode)**: Visual Studio Code is a popular, free, open-source, and lightweight code editor developed by Microsoft. It offers an extensive library of extensions that enhance functionality for C++ development.
|
||||
|
||||
- **Sublime Text**: Sublime Text is a cross-platform text editor that is quite popular among developers due to its speed and minimalist design. It supports C++ with the help of plugins and has a variety of themes and packages available for customization.
|
||||
|
||||
- **CLion**: CLion is an Integrated Development Environment (IDE) developed by JetBrains specifically for C and C++ developers. It provides advanced features like code completion, refactoring support, debugging, and more. It's worth noting that CLion is a commercial IDE, but there is community version available.
|
||||
* **Visual Studio**: Visual Studio is an Integrated Development Environment (IDE) for Windows, developed by Microsoft. It includes its own integrated compiler known as Microsoft Visual C++ (MSVC).
|
||||
|
||||
* **Visual Studio Code (VSCode)**: Visual Studio Code is a popular, free, open-source, and lightweight code editor developed by Microsoft. It offers an extensive library of extensions that enhance functionality for C++ development.
|
||||
|
||||
* **Sublime Text**: Sublime Text is a cross-platform text editor that is quite popular among developers due to its speed and minimalist design. It supports C++ with the help of plugins and has a variety of themes and packages available for customization.
|
||||
|
||||
* **CLion**: CLion is an Integrated Development Environment (IDE) developed by JetBrains specifically for C and C++ developers. It provides advanced features like code completion, refactoring support, debugging, and more. It's worth noting that CLion is a commercial IDE, but there is community version available.
|
||||
|
||||
|
||||
These are just a few examples, and there are many other code editors available, including Atom, Notepad++, and Geany. They all have their features and may suit different developers' needs. Finding the right code editor is often a matter of personal preference and workflow.
|
||||
|
||||
To work with C++ in your chosen code editor, you often need to install some additional tools and add-ons, such as compilers, linters, and debugger support. Make sure to follow the instructions provided in the editor's documentation to set up C++ correctly.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@Using C++ on Linux in VSCode](https://code.visualstudio.com/docs/cpp/config-linux)
|
||||
- [@feed@Explore top posts about General Programming](https://app.daily.dev/tags/general-programming?ref=roadmapsh)
|
||||
- [@feed@Explore top posts about General Programming](https://app.daily.dev/tags/general-programming?ref=roadmapsh)
|
||||
@@ -2,57 +2,57 @@
|
||||
|
||||
The process of compilation in C++ can be divided into four primary stages: Preprocessing, Compilation, Assembly, and Linking. Each stage performs a specific task, ultimately converting the source code into an executable program.
|
||||
|
||||
## Preprocessing
|
||||
Preprocessing
|
||||
-------------
|
||||
|
||||
The first stage is the preprocessing of the source code. Preprocessors modify the source code before the actual compilation process. They handle directives that start with a `#` (hash) symbol, like `#include`, `#define`, and `#if`. In this stage, included header files are expanded, macros are replaced, and conditional compilation statements are processed.
|
||||
|
||||
**Code Example:**
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#define PI 3.14
|
||||
#include <iostream>
|
||||
#define PI 3.14
|
||||
|
||||
int main() {
|
||||
std::cout << "The value of PI is: " << PI << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
std::cout << "The value of PI is: " << PI << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Compilation
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
The second stage is the actual compilation of the preprocessed source code. The compiler translates the modified source code into an intermediate representation, usually specific to the target processor architecture. This step also involves performing syntax checking, semantic analysis, and producing error messages for any issues encountered in the source code.
|
||||
|
||||
**Code Example:**
|
||||
|
||||
```cpp
|
||||
int main() {
|
||||
int a = 10;
|
||||
int b = 20;
|
||||
int sum = a + b;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
int main() {
|
||||
int a = 10;
|
||||
int b = 20;
|
||||
int sum = a + b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
## Assembly
|
||||
Assembly
|
||||
--------
|
||||
|
||||
The third stage is converting the compiler's intermediate representation into assembly language. This stage generates assembly code using mnemonics and syntax that is specific to the target processor architecture. Assemblers then convert this assembly code into object code (machine code).
|
||||
|
||||
**Code Example (x86 Assembly):**
|
||||
|
||||
```
|
||||
mov eax, 10
|
||||
mov ebx, 20
|
||||
add eax, ebx
|
||||
```
|
||||
mov eax, 10
|
||||
mov ebx, 20
|
||||
add eax, ebx
|
||||
|
||||
|
||||
## Linking
|
||||
Linking
|
||||
-------
|
||||
|
||||
The final stage is the linking of the object code with the necessary libraries and other object files. In this stage, the linker merges multiple object files and libraries, resolves external references from other modules or libraries, allocates memory addresses for functions and variables, and generates an executable file that can be run on the target platform.
|
||||
|
||||
**Code Example (linking objects and libraries):**
|
||||
|
||||
```bash
|
||||
$ g++ main.o -o main -lm
|
||||
```
|
||||
$ g++ main.o -o main -lm
|
||||
|
||||
|
||||
In summary, the compilation process in C++ involves four primary stages: preprocessing, compilation, assembly, and linking. Each stage plays a crucial role in transforming the source code into an executable program.
|
||||
In summary, the compilation process in C++ involves four primary stages: preprocessing, compilation, assembly, and linking. Each stage plays a crucial role in transforming the source code into an executable program.
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
Different C++ compilers have different features. Some of the most common features of C++ compilers are:
|
||||
|
||||
- **Optimization:** Compilers can optimize the code to improve the performance of the program. For example, they can remove redundant code, inline functions, and perform loop unrolling.
|
||||
- **Debugging:** Compilers can generate debugging information that can be used to debug the program.
|
||||
- **Warnings:** Compilers can generate warnings for suspicious code that may cause errors.
|
||||
* **Optimization:** Compilers can optimize the code to improve the performance of the program. For example, they can remove redundant code, inline functions, and perform loop unrolling.
|
||||
* **Debugging:** Compilers can generate debugging information that can be used to debug the program.
|
||||
* **Warnings:** Compilers can generate warnings for suspicious code that may cause errors.
|
||||
|
||||
Some of the most popular C++ compilers are:
|
||||
|
||||
- **GNU Compiler Collection (GCC):** GCC is a free and open-source compiler that supports many programming languages, including C++.
|
||||
- **Clang:** Clang is a C++ compiler that is part of the LLVM project. It is designed to be compatible with GCC.
|
||||
- **Microsoft Visual C++:** Microsoft Visual C++ is a C++ compiler that is part of the Microsoft Visual Studio IDE.
|
||||
- **Intel C++ Compiler:** Intel C++ Compiler is a C++ compiler that is part of the Intel Parallel Studio XE suite.
|
||||
* **GNU Compiler Collection (GCC):** GCC is a free and open-source compiler that supports many programming languages, including C++.
|
||||
* **Clang:** Clang is a C++ compiler that is part of the LLVM project. It is designed to be compatible with GCC.
|
||||
* **Microsoft Visual C++:** Microsoft Visual C++ is a C++ compiler that is part of the Microsoft Visual Studio IDE.
|
||||
* **Intel C++ Compiler:** Intel C++ Compiler is a C++ compiler that is part of the Intel Parallel Studio XE suite.
|
||||
|
||||
You should go through the documentation of your compiler to learn more about its features.
|
||||
@@ -2,39 +2,41 @@
|
||||
|
||||
A compiler is a computer program that translates source code written in one programming language into a different language, usually machine code or assembly code, that can be executed directly by a computer's processor. In the context of C++, compilers take your written C++ source code and convert it into an executable program.
|
||||
|
||||
## Popular C++ Compilers
|
||||
Popular C++ Compilers
|
||||
---------------------
|
||||
|
||||
There are several popular C++ compilers available, here's a short list of some common ones:
|
||||
|
||||
- **GNU Compiler Collection (GCC)**: Developed by the GNU Project, GCC is an open-source compiler that supports multiple programming languages, including C++.
|
||||
* **GNU Compiler Collection (GCC)**: Developed by the GNU Project, GCC is an open-source compiler that supports multiple programming languages, including C++.
|
||||
|
||||
* **Clang**: As part of the LLVM project, Clang is another open-source compiler that supports C++ and is known for its fast compilation times and extensive diagnostics.
|
||||
|
||||
* **Microsoft Visual C++ (MSVC)**: MSVC is a commercial compiler provided by Microsoft as part of Visual Studio, and it's widely used on Windows platforms.
|
||||
|
||||
* **Intel C++ Compiler (ICC)**: ICC is a commercial compiler provided by Intel and is known for its ability to optimize code for the latest Intel processors.
|
||||
|
||||
|
||||
- **Clang**: As part of the LLVM project, Clang is another open-source compiler that supports C++ and is known for its fast compilation times and extensive diagnostics.
|
||||
|
||||
- **Microsoft Visual C++ (MSVC)**: MSVC is a commercial compiler provided by Microsoft as part of Visual Studio, and it's widely used on Windows platforms.
|
||||
|
||||
- **Intel C++ Compiler (ICC)**: ICC is a commercial compiler provided by Intel and is known for its ability to optimize code for the latest Intel processors.
|
||||
|
||||
## Example of a Simple C++ Compilation
|
||||
Example of a Simple C++ Compilation
|
||||
-----------------------------------
|
||||
|
||||
Let's say you have a simple C++ program saved in a file called `hello.cpp`:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!\n";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
You can compile this program using the GCC compiler by executing the following command in a command-line/terminal:
|
||||
|
||||
```bash
|
||||
g++ hello.cpp -o hello
|
||||
```
|
||||
g++ hello.cpp -o hello
|
||||
|
||||
|
||||
This will generate an executable file called `hello` (or `hello.exe` on Windows) which you can run to see the output "Hello, World!".
|
||||
|
||||
## Note
|
||||
Note
|
||||
----
|
||||
|
||||
When learning about compilers, it's essential to know that they work closely with the linker and the standard library. The linker takes care of combining compiled object files and libraries into a single executable, while the standard library provides implementations for common functionalities used in your code.
|
||||
@@ -2,79 +2,76 @@
|
||||
|
||||
[Conan](https://conan.io/) is a popular package manager for C and C++ languages and is designed to be cross-platform, extensible, and easy to use. It allows developers to declare, manage, and fetch dependencies while automating the build process. Conan supports various build systems, such as CMake, Visual Studio, MSBuild, and more.
|
||||
|
||||
## Installation
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install Conan, you can use pip, the Python package manager:
|
||||
|
||||
```bash
|
||||
pip install conan
|
||||
```
|
||||
pip install conan
|
||||
|
||||
|
||||
## Basic Usage
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
- Create a `conanfile.txt` file in your project root directory, specifying dependencies you need for your project:
|
||||
* Create a `conanfile.txt` file in your project root directory, specifying dependencies you need for your project:
|
||||
|
||||
```ini
|
||||
[requires]
|
||||
boost/1.75.0
|
||||
[requires]
|
||||
boost/1.75.0
|
||||
|
||||
[generators]
|
||||
cmake
|
||||
|
||||
|
||||
[generators]
|
||||
cmake
|
||||
```
|
||||
* Run the `conan install` command to fetch and build required dependencies:
|
||||
|
||||
- Run the `conan install` command to fetch and build required dependencies:
|
||||
mkdir build && cd build
|
||||
conan install ..
|
||||
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
conan install ..
|
||||
```
|
||||
* Now build your project using your build system, for example CMake:
|
||||
|
||||
- Now build your project using your build system, for example CMake:
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build .
|
||||
|
||||
|
||||
```bash
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
## Creating Packages
|
||||
Creating Packages
|
||||
-----------------
|
||||
|
||||
To create a package in Conan, you need to write a `conanfile.py` file with package information and build instructions.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```python
|
||||
from conans import ConanFile, CMake
|
||||
|
||||
|
||||
class MyLibraryConan(ConanFile):
|
||||
name = "MyLibrary"
|
||||
version = "0.1"
|
||||
license = "MIT"
|
||||
url = "https://github.com/username/mylibrary"
|
||||
description = "A simple example library"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "cmake"
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure(source_folder="src")
|
||||
cmake.build()
|
||||
|
||||
def package(self):
|
||||
self.copy("*.hpp", dst="include", src="src/include")
|
||||
self.copy("*.lib", dst="lib", keep_path=False)
|
||||
self.copy("*.dll", dst="bin", keep_path=False)
|
||||
self.copy("*.so", dst="lib", keep_path=False)
|
||||
self.copy("*.a", dst="lib", keep_path=False)
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["MyLibrary"]
|
||||
```
|
||||
from conans import ConanFile, CMake
|
||||
|
||||
|
||||
class MyLibraryConan(ConanFile):
|
||||
name = "MyLibrary"
|
||||
version = "0.1"
|
||||
license = "MIT"
|
||||
url = "https://github.com/username/mylibrary"
|
||||
description = "A simple example library"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "cmake"
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure(source_folder="src")
|
||||
cmake.build()
|
||||
|
||||
def package(self):
|
||||
self.copy("*.hpp", dst="include", src="src/include")
|
||||
self.copy("*.lib", dst="lib", keep_path=False)
|
||||
self.copy("*.dll", dst="bin", keep_path=False)
|
||||
self.copy("*.so", dst="lib", keep_path=False)
|
||||
self.copy("*.a", dst="lib", keep_path=False)
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["MyLibrary"]
|
||||
|
||||
|
||||
With that setup, you can create a package by running:
|
||||
|
||||
```bash
|
||||
conan create . username/channel
|
||||
```
|
||||
conan create . username/channel
|
||||
|
||||
|
||||
This will compile the package and store it in your Conan cache. You can now use this package as a dependency in other projects.
|
||||
@@ -4,33 +4,33 @@
|
||||
|
||||
Keep in mind that using `const_cast` to modify a truly `const` variable can lead to undefined behavior, so it is best to use this feature only when absolutely necessary.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
Here's a code example showing how to use `const_cast`:
|
||||
|
||||
```cpp
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
void modifyVariable(int* ptr) {
|
||||
*ptr = 42;
|
||||
}
|
||||
|
||||
int main() {
|
||||
const int original_value = 10;
|
||||
int* non_const_value_ptr = const_cast<int*>(&original_value);
|
||||
std::cout << "Original value: " << original_value << '\n';
|
||||
|
||||
modifyVariable(non_const_value_ptr);
|
||||
std::cout << "Modified value: " << *non_const_value_ptr << ", original_value: " << original_value << '\n';
|
||||
|
||||
assert(non_const_value_ptr == &original_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
void modifyVariable(int* ptr) {
|
||||
*ptr = 42;
|
||||
}
|
||||
|
||||
int main() {
|
||||
const int original_value = 10;
|
||||
int* non_const_value_ptr = const_cast<int*>(&original_value);
|
||||
std::cout << "Original value: " << original_value << '\n';
|
||||
|
||||
modifyVariable(non_const_value_ptr);
|
||||
std::cout << "Modified value: " << *non_const_value_ptr << ", original_value: " << original_value << '\n';
|
||||
|
||||
assert(non_const_value_ptr == &original_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
In this example, we first create a `const` variable, `original_value`. Then we use `const_cast` to remove the constness of the variable and assign it to a non-const pointer, `non_const_value_ptr`. The `modifyVariable` function takes an `int*` as an argument and modifies the value pointed to by the pointer, which would not have been possible if we passed the original `const int` directly. Finally, we print the `original_value` and the `*non_const_value_ptr`, which shows that the value has been modified using `const_cast`.
|
||||
|
||||
Please note that this example comes with some risks, as it touches undefined behavior. */
|
||||
Please note that this example comes with some risks, as it touches undefined behavior. \*/
|
||||
@@ -2,96 +2,100 @@
|
||||
|
||||
C++ Containers are a part of the Standard Template Library (STL) that provide data structures to store and organize data. There are several types of containers, each with its own characteristics and use cases. Here, we discuss some of the commonly used containers:
|
||||
|
||||
## 1. Vector
|
||||
1\. Vector
|
||||
----------
|
||||
|
||||
Vectors are dynamic arrays that can resize themselves as needed. They store elements in a contiguous memory location, allowing fast random access using indices.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
std::vector<int> vec = {1, 2, 3, 4, 5};
|
||||
|
||||
vec.push_back(6); // Add an element to the end
|
||||
|
||||
std::cout << "Vector contains:";
|
||||
for (int x : vec) {
|
||||
std::cout << ' ' << x;
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
std::vector<int> vec = {1, 2, 3, 4, 5};
|
||||
|
||||
vec.push_back(6); // Add an element to the end
|
||||
|
||||
std::cout << "Vector contains:";
|
||||
for (int x : vec) {
|
||||
std::cout << ' ' << x;
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 2. List
|
||||
2\. List
|
||||
--------
|
||||
|
||||
A list is a doubly-linked list that allows elements to be inserted or removed from any position in constant time. It does not support random access. Lists are better than vectors for scenarios where you need to insert or remove elements in the middle frequently.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
int main() {
|
||||
std::list<int> lst = {1, 2, 3, 4, 5};
|
||||
|
||||
lst.push_back(6); // Add an element to the end
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
std::cout << "List contains:";
|
||||
for (int x : lst) {
|
||||
std::cout << ' ' << x;
|
||||
int main() {
|
||||
std::list<int> lst = {1, 2, 3, 4, 5};
|
||||
|
||||
lst.push_back(6); // Add an element to the end
|
||||
|
||||
std::cout << "List contains:";
|
||||
for (int x : lst) {
|
||||
std::cout << ' ' << x;
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 3. Map
|
||||
3\. Map
|
||||
-------
|
||||
|
||||
A map is an associative container that stores key-value pairs. It supports the retrieval of values based on their keys. The keys are sorted in ascending order by default.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
int main() {
|
||||
std::map<std::string, int> m;
|
||||
|
||||
m["one"] = 1;
|
||||
m["two"] = 2;
|
||||
|
||||
std::cout << "Map contains:\n";
|
||||
for (const auto &pair : m) {
|
||||
std::cout << pair.first << ": " << pair.second << '\n';
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
int main() {
|
||||
std::map<std::string, int> m;
|
||||
|
||||
m["one"] = 1;
|
||||
m["two"] = 2;
|
||||
|
||||
std::cout << "Map contains:\n";
|
||||
for (const auto &pair : m) {
|
||||
std::cout << pair.first << ": " << pair.second << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 4. Unordered_map
|
||||
4\. Unordered\_map
|
||||
------------------
|
||||
|
||||
Similar to a map, an unordered map stores key-value pairs, but it is implemented using a hash table. This means unordered_map has faster average-case performance compared to map, since it does not maintain sorted order. However, worst-case performance can be worse than map.
|
||||
Similar to a map, an unordered map stores key-value pairs, but it is implemented using a hash table. This means unordered\_map has faster average-case performance compared to map, since it does not maintain sorted order. However, worst-case performance can be worse than map.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
int main() {
|
||||
std::unordered_map<std::string, int> um;
|
||||
|
||||
um["one"] = 1;
|
||||
um["two"] = 2;
|
||||
|
||||
std::cout << "Unordered map contains:\n";
|
||||
for (const auto &pair : um) {
|
||||
std::cout << pair.first << ": " << pair.second << '\n';
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
int main() {
|
||||
std::unordered_map<std::string, int> um;
|
||||
|
||||
um["one"] = 1;
|
||||
um["two"] = 2;
|
||||
|
||||
std::cout << "Unordered map contains:\n";
|
||||
for (const auto &pair : um) {
|
||||
std::cout << pair.first << ": " << pair.second << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
These are just a few examples of C++ containers. There are other container types, such as `set`, `multiset`, `deque`, `stack`, `queue`, and `priority_queue`. Each container has its own use cases and unique characteristics. Learning about these containers and when to use them can greatly improve your efficiency and effectiveness in using C++.
|
||||
@@ -4,34 +4,34 @@ Copy-swap is a C++ idiom that leverages the copy constructor and swap function t
|
||||
|
||||
Here's a brief summary:
|
||||
|
||||
- **Copy**: Create a local copy of the right-hand side object. This step leverages the copy constructor, providing exception safety and code reuse.
|
||||
- **Swap**: Swap the contents of the left-hand side object with the temporary copy. This step typically involves swapping internal pointers or resources, without needing to copy the full contents again.
|
||||
- **Destruction**: Destroy the temporary copy. This happens upon the exit of the assignment operator.
|
||||
* **Copy**: Create a local copy of the right-hand side object. This step leverages the copy constructor, providing exception safety and code reuse.
|
||||
* **Swap**: Swap the contents of the left-hand side object with the temporary copy. This step typically involves swapping internal pointers or resources, without needing to copy the full contents again.
|
||||
* **Destruction**: Destroy the temporary copy. This happens upon the exit of the assignment operator.
|
||||
|
||||
Here's a code example for a simple `String` class:
|
||||
|
||||
```cpp
|
||||
class String {
|
||||
// ... rest of the class ...
|
||||
|
||||
String(const String& other);
|
||||
class String {
|
||||
// ... rest of the class ...
|
||||
|
||||
String(const String& other);
|
||||
|
||||
friend void swap(String& first, String& second) {
|
||||
using std::swap; // for arguments-dependent lookup (ADL)
|
||||
swap(first.size_, second.size_);
|
||||
swap(first.buffer_, second.buffer_);
|
||||
}
|
||||
|
||||
String& operator=(String other) {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
friend void swap(String& first, String& second) {
|
||||
using std::swap; // for arguments-dependent lookup (ADL)
|
||||
swap(first.size_, second.size_);
|
||||
swap(first.buffer_, second.buffer_);
|
||||
}
|
||||
|
||||
String& operator=(String other) {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Using the copy-swap idiom:
|
||||
- The right-hand side object is copied when passed by value to the assignment operator.
|
||||
- The left-hand side object's contents are swapped with the temporary copy.
|
||||
- The temporary copy is destroyed, releasing any resources that were previously held by the left-hand side object.
|
||||
|
||||
This approach simplifies the implementation and provides strong exception safety, while reusing the copy constructor and destructor code.
|
||||
* The right-hand side object is copied when passed by value to the assignment operator.
|
||||
* The left-hand side object's contents are swapped with the temporary copy.
|
||||
* The temporary copy is destroyed, releasing any resources that were previously held by the left-hand side object.
|
||||
|
||||
This approach simplifies the implementation and provides strong exception safety, while reusing the copy constructor and destructor code.
|
||||
@@ -4,40 +4,39 @@ The Copy-Write idiom, sometimes called the Copy-on-Write (CoW) or "lazy copying"
|
||||
|
||||
Let's understand this with a simple example:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
class MyString {
|
||||
public:
|
||||
MyString(const std::string &str) : data(std::make_shared<std::string>(str)) {}
|
||||
|
||||
// Use the same shared data for copying.
|
||||
MyString(const MyString &other) : data(other.data) {
|
||||
std::cout << "Copied using the Copy-Write idiom.\n";
|
||||
}
|
||||
|
||||
// Make a copy only if we want to modify the data.
|
||||
void write(const std::string &str) {
|
||||
// Check if there's more than one reference.
|
||||
if (data.use_count() > 1) {
|
||||
data = std::make_shared<std::string>(*data);
|
||||
std::cout << "Copy is actually made for writing.\n";
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
class MyString {
|
||||
public:
|
||||
MyString(const std::string &str) : data(std::make_shared<std::string>(str)) {}
|
||||
|
||||
// Use the same shared data for copying.
|
||||
MyString(const MyString &other) : data(other.data) {
|
||||
std::cout << "Copied using the Copy-Write idiom.\n";
|
||||
}
|
||||
*data = str;
|
||||
|
||||
// Make a copy only if we want to modify the data.
|
||||
void write(const std::string &str) {
|
||||
// Check if there's more than one reference.
|
||||
if (data.use_count() > 1) {
|
||||
data = std::make_shared<std::string>(*data);
|
||||
std::cout << "Copy is actually made for writing.\n";
|
||||
}
|
||||
*data = str;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::string> data;
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyString str1("Hello");
|
||||
MyString str2 = str1; // No copy operation, just shared references.
|
||||
|
||||
str1.write("Hello, World!"); // This is where the actual duplication happens.
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::string> data;
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyString str1("Hello");
|
||||
MyString str2 = str1; // No copy operation, just shared references.
|
||||
|
||||
str1.write("Hello, World!"); // This is where the actual duplication happens.
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, we have a class `MyString` that simulates the Copy-Write idiom. When a `MyString` object is created, it constructs a `shared_ptr` pointing to a string. When a `MyString` object is copied, it does not perform any actual copy operation, but simply increases the reference count of the shared object. Finally, when the `write` function is called, it checks if there's more than one reference to the data and if so, it actually creates a new copy and updates the reference. This way, unnecessary copies can be avoided until they are actually needed for modification.
|
||||
@@ -8,40 +8,39 @@ CRTP is usually employed when you want to customize certain behavior in the base
|
||||
|
||||
Here's an example demonstrating CRTP:
|
||||
|
||||
```cpp
|
||||
template <typename Derived>
|
||||
class Base {
|
||||
public:
|
||||
void interface() {
|
||||
static_cast<Derived*>(this)->implementation();
|
||||
template <typename Derived>
|
||||
class Base {
|
||||
public:
|
||||
void interface() {
|
||||
static_cast<Derived*>(this)->implementation();
|
||||
}
|
||||
|
||||
void implementation() {
|
||||
std::cout << "Default implementation in Base\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived1 : public Base<Derived1> {
|
||||
public:
|
||||
void implementation() {
|
||||
std::cout << "Custom implementation in Derived1\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived2 : public Base<Derived2> {
|
||||
// No custom implementation, so Base::implementation will be used.
|
||||
};
|
||||
|
||||
int main() {
|
||||
Derived1 d1;
|
||||
d1.interface(); // Output: "Custom implementation in Derived1"
|
||||
|
||||
Derived2 d2;
|
||||
d2.interface(); // Output: "Default implementation in Base"
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void implementation() {
|
||||
std::cout << "Default implementation in Base\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived1 : public Base<Derived1> {
|
||||
public:
|
||||
void implementation() {
|
||||
std::cout << "Custom implementation in Derived1\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived2 : public Base<Derived2> {
|
||||
// No custom implementation, so Base::implementation will be used.
|
||||
};
|
||||
|
||||
int main() {
|
||||
Derived1 d1;
|
||||
d1.interface(); // Output: "Custom implementation in Derived1"
|
||||
|
||||
Derived2 d2;
|
||||
d2.interface(); // Output: "Default implementation in Base"
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, the `Base` class is a template that takes a single type parameter. `Derived1` and `Derived2` are derived from their respective specialization of `Base`. CRTP is employed to allow custom implementations of the `implementation()` function in derived classes while providing a default behavior in the `Base` class. The `interface()` function in the `Base` class is a template for the derived class's behavior and calls the corresponding `implementation()` function based on the static type.
|
||||
|
||||
|
||||
@@ -2,135 +2,158 @@
|
||||
|
||||
In C++, data types are used to categorize different types of data that a program can process. They are essential for determining the type of value a variable can hold and how much memory space it will occupy. Some basic data types in C++ include integers, floating-point numbers, characters, and booleans.
|
||||
|
||||
## Fundamental Data Types
|
||||
Fundamental Data Types
|
||||
----------------------
|
||||
|
||||
## Integer (int)
|
||||
Integers are whole numbers that can store both positive and negative values. The size of `int` depends on the system architecture (usually 4 bytes).
|
||||
Integer (int)
|
||||
-------------
|
||||
|
||||
Integers are whole numbers that can store both positive and negative values. The size of `int` depends on the system architecture (usually 4 bytes).
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int num = 42;
|
||||
```
|
||||
|
||||
int num = 42;
|
||||
|
||||
|
||||
There are variants of `int` that can hold different ranges of numbers:
|
||||
- short (`short int`): Smaller range than `int`.
|
||||
- long (`long int`): Larger range than `int`.
|
||||
- long long (`long long int`): Even larger range than `long int`.
|
||||
|
||||
## Floating-Point (float, double)
|
||||
* short (`short int`): Smaller range than `int`.
|
||||
* long (`long int`): Larger range than `int`.
|
||||
* long long (`long long int`): Even larger range than `long int`.
|
||||
|
||||
Floating-Point (float, double)
|
||||
------------------------------
|
||||
|
||||
Floating-point types represent real numbers, i.e., numbers with a decimal point. There are two main floating-point types:
|
||||
|
||||
- **float**: Provides single-precision floating-point numbers. It typically occupies 4 bytes of memory.
|
||||
* **float**: Provides single-precision floating-point numbers. It typically occupies 4 bytes of memory.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
float pi = 3.14f;
|
||||
```
|
||||
|
||||
- **double**: Provides double-precision floating-point numbers. It consumes more memory (usually 8 bytes) but has a higher precision than `float`.
|
||||
float pi = 3.14f;
|
||||
|
||||
|
||||
* **double**: Provides double-precision floating-point numbers. It consumes more memory (usually 8 bytes) but has a higher precision than `float`.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
double pi_high_precision = 3.1415926535;
|
||||
```
|
||||
|
||||
## Character (char)
|
||||
double pi_high_precision = 3.1415926535;
|
||||
|
||||
|
||||
Character (char)
|
||||
----------------
|
||||
|
||||
Characters represent a single character, such as a letter, digit, or symbol. They are stored using the ASCII value of the symbol and typically occupy 1 byte of memory.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
char letter = 'A';
|
||||
```
|
||||
|
||||
## Boolean (bool)
|
||||
char letter = 'A';
|
||||
|
||||
|
||||
Boolean (bool)
|
||||
--------------
|
||||
|
||||
Booleans represent logical values: `true` or `false`. They usually occupy 1 byte of memory.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
bool is_cpp_great = true;
|
||||
```
|
||||
|
||||
## Derived Data Types
|
||||
bool is_cpp_great = true;
|
||||
|
||||
|
||||
Derived Data Types
|
||||
------------------
|
||||
|
||||
Derived data types are types that are derived from fundamental data types. Some examples include:
|
||||
|
||||
## Arrays
|
||||
Arrays
|
||||
------
|
||||
|
||||
Arrays are used to store multiple values of the same data type in consecutive memory locations.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int numbers[5] = {1, 2, 3, 4, 5};
|
||||
int scores[10] = {100,95,98}; // Partially initialized array. 100,95,98 initialized on first 3 indexes, rest indexes are initialized with 0
|
||||
int allZero[0] = {0}; // initialized all to zero
|
||||
```
|
||||
|
||||
## Pointers
|
||||
int numbers[5] = {1, 2, 3, 4, 5};
|
||||
int scores[10] = {100,95,98}; // Partially initialized array. 100,95,98 initialized on first 3 indexes, rest indexes are initialized with 0
|
||||
int allZero[0] = {0}; // initialized all to zero
|
||||
|
||||
|
||||
Pointers
|
||||
--------
|
||||
|
||||
Pointers are used to store the memory address of a variable.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int num = 42;
|
||||
int* pNum = #
|
||||
```
|
||||
|
||||
## References
|
||||
int num = 42;
|
||||
int* pNum = #
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
References are an alternative way to share memory locations between variables, allowing you to create an alias for another variable.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int num = 42;
|
||||
int& numRef = num;
|
||||
```
|
||||
|
||||
## User-Defined Data Types
|
||||
int num = 42;
|
||||
int& numRef = num;
|
||||
|
||||
|
||||
User-Defined Data Types
|
||||
-----------------------
|
||||
|
||||
User-defined data types are types that are defined by the programmer, such as structures, classes, and unions.
|
||||
|
||||
## Structures (struct)
|
||||
Structures (struct)
|
||||
-------------------
|
||||
|
||||
Structures are used to store different data types under a single variable and accessibility of member variables and methods are public.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
struct Person {
|
||||
std::string name;
|
||||
int age;
|
||||
float height;
|
||||
};
|
||||
|
||||
Person p1 = {"John Doe", 30, 5.9};
|
||||
```
|
||||
struct Person {
|
||||
std::string name;
|
||||
int age;
|
||||
float height;
|
||||
};
|
||||
|
||||
Person p1 = {"John Doe", 30, 5.9};
|
||||
|
||||
|
||||
Classes (class)
|
||||
---------------
|
||||
|
||||
## Classes (class)
|
||||
Classes are similar to structures, but the accessibility of the member data and function are governed by access specifiers. By default access to members of a class is private.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
class Person {
|
||||
public:
|
||||
std::string name;
|
||||
int age;
|
||||
|
||||
void printInfo() {
|
||||
std::cout << "Name: " << name << ", Age: " << age << '\n';
|
||||
class Person {
|
||||
public:
|
||||
std::string name;
|
||||
int age;
|
||||
|
||||
void printInfo() {
|
||||
std::cout << "Name: " << name << ", Age: " << age << '\n';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Person p1;
|
||||
p1.name = "John Doe";
|
||||
p1.age = 30;
|
||||
|
||||
|
||||
Person p1;
|
||||
p1.name = "John Doe";
|
||||
p1.age = 30;
|
||||
```
|
||||
Unions (union)
|
||||
--------------
|
||||
|
||||
## Unions (union)
|
||||
Unions are used to store different data types in the same memory location.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
union Data {
|
||||
int num;
|
||||
char letter;
|
||||
float decimal;
|
||||
};
|
||||
|
||||
Data myData;
|
||||
myData.num = 42;
|
||||
```
|
||||
union Data {
|
||||
int num;
|
||||
char letter;
|
||||
float decimal;
|
||||
};
|
||||
|
||||
Data myData;
|
||||
myData.num = 42;
|
||||
@@ -2,89 +2,89 @@
|
||||
|
||||
In C++, you can work with dates and times using the `chrono` library, which is part of the Standard Library (STL). The `chrono` library provides various data types and functions to represent and manipulate time durations, time points, and clocks.
|
||||
|
||||
## Duration
|
||||
Duration
|
||||
--------
|
||||
|
||||
A `duration` represents a span of time, which can be expressed in various units such as seconds, minutes, hours, etc. To create a duration, use the `std::chrono::duration` template class. Common predefined duration types are:
|
||||
|
||||
- `std::chrono::seconds`
|
||||
- `std::chrono::minutes`
|
||||
- `std::chrono::hours`
|
||||
* `std::chrono::seconds`
|
||||
* `std::chrono::minutes`
|
||||
* `std::chrono::hours`
|
||||
|
||||
**Example:**
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
int main() {
|
||||
std::chrono::seconds sec(5);
|
||||
std::chrono::minutes min(2);
|
||||
std::chrono::hours hr(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
std::chrono::seconds sec(5);
|
||||
std::chrono::minutes min(2);
|
||||
std::chrono::hours hr(1);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Time Point
|
||||
Time Point
|
||||
----------
|
||||
|
||||
A `time_point` represents a specific point in time. It is usually created using a combination of duration and a clock. In C++, there are three clock types provided by the `chrono` library:
|
||||
|
||||
- `std::chrono::system_clock`: Represents the system-wide real time wall clock.
|
||||
- `std::chrono::steady_clock`: Represents a monotonic clock that is guaranteed to never be adjusted.
|
||||
- `std::chrono::high_resolution_clock`: Represents the clock with the shortest tick period.
|
||||
* `std::chrono::system_clock`: Represents the system-wide real time wall clock.
|
||||
* `std::chrono::steady_clock`: Represents a monotonic clock that is guaranteed to never be adjusted.
|
||||
* `std::chrono::high_resolution_clock`: Represents the clock with the shortest tick period.
|
||||
|
||||
**Example:**
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
int main() {
|
||||
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Clock
|
||||
Clock
|
||||
-----
|
||||
|
||||
A clock provides access to the current time. It consists of the following elements:
|
||||
|
||||
- `time_point`: A specific point in time.
|
||||
- `duration`: The time duration between two time points.
|
||||
- `now()`: A static function that returns the current time point.
|
||||
* `time_point`: A specific point in time.
|
||||
* `duration`: The time duration between two time points.
|
||||
* `now()`: A static function that returns the current time point.
|
||||
|
||||
**Example:**
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
int main() {
|
||||
// Get the current time_point using system_clock
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
|
||||
// Get the time_point 1 hour from now
|
||||
std::chrono::system_clock::time_point one_hour_from_now = now + std::chrono::hours(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
// Get the current time_point using system_clock
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
|
||||
// Get the time_point 1 hour from now
|
||||
std::chrono::system_clock::time_point one_hour_from_now = now + std::chrono::hours(1);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Converting Time Points to Calendar Time
|
||||
Converting Time Points to Calendar Time
|
||||
---------------------------------------
|
||||
|
||||
To convert a time point to calendar representation, you can use the `std::chrono::system_clock::to_time_t` function.
|
||||
|
||||
**Example:**
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
int main() {
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
|
||||
std::cout << "Current time: " << std::ctime(&now_c) << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
|
||||
std::cout << "Current time: " << std::ctime(&now_c) << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
This summarizes the basic functionality of working with date and time in C++ using the `chrono` library. You can find more advanced features, such as casting durations and time arithmetic, in the [C++ reference](https://en.cppreference.com/w/cpp/chrono).
|
||||
This summarizes the basic functionality of working with date and time in C++ using the `chrono` library. You can find more advanced features, such as casting durations and time arithmetic, in the [C++ reference](https://en.cppreference.com/w/cpp/chrono).
|
||||
@@ -2,41 +2,43 @@
|
||||
|
||||
Debuggers are essential tools for any C++ programmer, as they help in detecting, diagnosing, and fixing bugs in the code. They serve as an invaluable resource in identifying and understanding potential errors in the program.
|
||||
|
||||
## Types of Debuggers
|
||||
Types of Debuggers
|
||||
------------------
|
||||
|
||||
There are several debuggers available for use with C++:
|
||||
|
||||
- **GDB (GNU Debugger):** This is the most widely used C++ debugger in the Linux environment. It can debug many languages, including C and C++.
|
||||
|
||||
* **GDB (GNU Debugger):** This is the most widely used C++ debugger in the Linux environment. It can debug many languages, including C and C++.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
g++ -g main.cpp -o main # compile the code with debug info
|
||||
gdb ./main # start gdb session
|
||||
b main # set a breakpoint at the start of the main function
|
||||
run # run the program
|
||||
next # step to the next line
|
||||
```
|
||||
|
||||
- **LLDB:** This is the debugger developed by LLVM. It supports multiple languages and is popular among macOS and iOS developers.
|
||||
|
||||
|
||||
g++ -g main.cpp -o main # compile the code with debug info
|
||||
gdb ./main # start gdb session
|
||||
b main # set a breakpoint at the start of the main function
|
||||
run # run the program
|
||||
next # step to the next line
|
||||
|
||||
|
||||
* **LLDB:** This is the debugger developed by LLVM. It supports multiple languages and is popular among macOS and iOS developers.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
clang++ -g main.cpp -o main # compile the code with debug info
|
||||
lldb ./main # start lldb session
|
||||
breakpoint set --name main # set a breakpoint at the start of the main function
|
||||
run # run the program
|
||||
next # step to the next line
|
||||
```
|
||||
|
||||
- **Microsoft Visual Studio Debugger:** This debugger is built into Visual Studio and is typically used in a graphical interface on Windows systems.
|
||||
|
||||
|
||||
clang++ -g main.cpp -o main # compile the code with debug info
|
||||
lldb ./main # start lldb session
|
||||
breakpoint set --name main # set a breakpoint at the start of the main function
|
||||
run # run the program
|
||||
next # step to the next line
|
||||
|
||||
|
||||
* **Microsoft Visual Studio Debugger:** This debugger is built into Visual Studio and is typically used in a graphical interface on Windows systems.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
Open your Visual Studio project and go to Debug > Start Debugging. Then use the step over (F10), step into (F11), or continue (F5) commands to navigate through the code.
|
||||
```
|
||||
|
||||
- **Intel Debugger (IDB):** This debugger is part of Intel's parallel development suite and is popular for high-performance applications.
|
||||
|
||||
- **TotalView Debugger:** Developed by Rogue Wave Software, TotalView Debugger is a commercial debugger designed for parallel, high-performance, and enterprise applications.
|
||||
|
||||
Open your Visual Studio project and go to Debug > Start Debugging. Then use the step over (F10), step into (F11), or continue (F5) commands to navigate through the code.
|
||||
|
||||
|
||||
* **Intel Debugger (IDB):** This debugger is part of Intel's parallel development suite and is popular for high-performance applications.
|
||||
|
||||
* **TotalView Debugger:** Developed by Rogue Wave Software, TotalView Debugger is a commercial debugger designed for parallel, high-performance, and enterprise applications.
|
||||
|
||||
|
||||
Each debugger has its advantages and unique features, so it's essential to choose the one that best suits your needs and works well with your development environment.
|
||||
@@ -4,11 +4,13 @@ Debugger symbols are additional information embedded within the compiled program
|
||||
|
||||
There are generally two types of debugging symbols:
|
||||
|
||||
- **Internal Debugging Symbols**: These symbols reside within the compiled binary code itself. When using internal debugging symbols, it is essential to note that the size of the binary increases, which may not be desirable for production environments.
|
||||
* **Internal Debugging Symbols**: These symbols reside within the compiled binary code itself. When using internal debugging symbols, it is essential to note that the size of the binary increases, which may not be desirable for production environments.
|
||||
|
||||
* **External Debugging Symbols**: The debugging symbols are kept in separate files apart from the binary code, usually with file extensions such as `.pdb` (Program Database) in Windows or `.dSYM` (DWARF Symbol Information) in macOS.
|
||||
|
||||
|
||||
- **External Debugging Symbols**: The debugging symbols are kept in separate files apart from the binary code, usually with file extensions such as `.pdb` (Program Database) in Windows or `.dSYM` (DWARF Symbol Information) in macOS.
|
||||
|
||||
## Generating Debugger Symbols
|
||||
Generating Debugger Symbols
|
||||
---------------------------
|
||||
|
||||
To generate debugger symbols in C++, you need to specify specific options during the compilation process. We will use `g++` compiler as an example.
|
||||
|
||||
@@ -16,9 +18,8 @@ To generate debugger symbols in C++, you need to specify specific options during
|
||||
|
||||
To create a debug build with internal debugging symbols, use the `-g` flag:
|
||||
|
||||
```bash
|
||||
g++ -g -o my_program my_program.cpp
|
||||
```
|
||||
g++ -g -o my_program my_program.cpp
|
||||
|
||||
|
||||
This command compiles `my_program.cpp` into an executable named `my_program` with internal debugging symbols.
|
||||
|
||||
@@ -26,17 +27,15 @@ This command compiles `my_program.cpp` into an executable named `my_program` wit
|
||||
|
||||
In case you want to generate a separate file containing debugging symbols, you can use the `-gsplit-dwarf` flag:
|
||||
|
||||
```bash
|
||||
g++ -g -gsplit-dwarf -o my_program my_program.cpp
|
||||
```
|
||||
g++ -g -gsplit-dwarf -o my_program my_program.cpp
|
||||
|
||||
|
||||
This command compiles `my_program.cpp` into an executable named `my_program` and generates a separate file named `my_program.dwo` containing the debugging symbols.
|
||||
|
||||
When sharing your compiled binary to end-users, you can remove the debugging symbols using the `strip` command:
|
||||
|
||||
```bash
|
||||
strip --strip-debug my_program
|
||||
```
|
||||
strip --strip-debug my_program
|
||||
|
||||
|
||||
This command removes internal debug symbols, resulting in a smaller binary size while keeping the `.dwo` file for debugging purposes when needed.
|
||||
|
||||
|
||||
@@ -4,48 +4,47 @@ Diamond inheritance is a specific scenario in multiple inheritance where a class
|
||||
|
||||
To resolve this ambiguity, you can use virtual inheritance. A virtual base class is a class that is shared by multiple classes using `virtual` keyword in C++. This ensures that only one copy of the base class is inherited in the final derived class, and thus, resolves the diamond inheritance problem.
|
||||
|
||||
*Example:*
|
||||
_Example:_
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
class Base {
|
||||
public:
|
||||
void print() {
|
||||
std::cout << "Base class\n";
|
||||
#include <iostream>
|
||||
|
||||
class Base {
|
||||
public:
|
||||
void print() {
|
||||
std::cout << "Base class\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived1 : virtual public Base {
|
||||
public:
|
||||
void derived1Print() {
|
||||
std::cout << "Derived1 class\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived2 : virtual public Base {
|
||||
public:
|
||||
void derived2Print() {
|
||||
std::cout << "Derived2 class\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived3 : public Derived1, public Derived2 {
|
||||
public:
|
||||
void derived3Print() {
|
||||
std::cout << "Derived3 class\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Derived3 d3;
|
||||
d3.print(); // Now, there is no ambiguity in calling the base class function
|
||||
d3.derived1Print();
|
||||
d3.derived2Print();
|
||||
d3.derived3Print();
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Derived1 : virtual public Base {
|
||||
public:
|
||||
void derived1Print() {
|
||||
std::cout << "Derived1 class\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived2 : virtual public Base {
|
||||
public:
|
||||
void derived2Print() {
|
||||
std::cout << "Derived2 class\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived3 : public Derived1, public Derived2 {
|
||||
public:
|
||||
void derived3Print() {
|
||||
std::cout << "Derived3 class\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Derived3 d3;
|
||||
d3.print(); // Now, there is no ambiguity in calling the base class function
|
||||
d3.derived1Print();
|
||||
d3.derived2Print();
|
||||
d3.derived3Print();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
In the code above, `Derived1` and `Derived2` are derived from the `Base` class using virtual inheritance. So, when we create an object of `Derived3` and call the `print()` function from the `Base` class, there is no ambiguity, and the code executes without any issues.
|
||||
In the code above, `Derived1` and `Derived2` are derived from the `Base` class using virtual inheritance. So, when we create an object of `Derived3` and call the `print()` function from the `Base` class, there is no ambiguity, and the code executes without any issues.
|
||||
@@ -4,63 +4,62 @@ Dynamic polymorphism is a programming concept in object-oriented languages like
|
||||
|
||||
Dynamic polymorphism is achieved through **virtual functions**, which are member functions of a base class marked with the `virtual` keyword. When you specify a virtual function in a base class, it can be overridden in any derived class to provide a different implementation.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
Here's an example in C++ demonstrating dynamic polymorphism.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
// Base class
|
||||
class Shape {
|
||||
public:
|
||||
virtual void draw() {
|
||||
std::cout << "Drawing a shape\n";
|
||||
#include <iostream>
|
||||
|
||||
// Base class
|
||||
class Shape {
|
||||
public:
|
||||
virtual void draw() {
|
||||
std::cout << "Drawing a shape\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Derived class 1
|
||||
class Circle : public Shape {
|
||||
public:
|
||||
void draw() override {
|
||||
std::cout << "Drawing a circle\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Derived class 2
|
||||
class Rectangle : public Shape {
|
||||
public:
|
||||
void draw() override {
|
||||
std::cout << "Drawing a rectangle\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Shape* shape;
|
||||
Circle circle;
|
||||
Rectangle rectangle;
|
||||
|
||||
// Storing the address of circle
|
||||
shape = &circle;
|
||||
|
||||
// Call circle draw function
|
||||
shape->draw();
|
||||
|
||||
// Storing the address of rectangle
|
||||
shape = &rectangle;
|
||||
|
||||
// Call rectangle draw function
|
||||
shape->draw();
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Derived class 1
|
||||
class Circle : public Shape {
|
||||
public:
|
||||
void draw() override {
|
||||
std::cout << "Drawing a circle\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Derived class 2
|
||||
class Rectangle : public Shape {
|
||||
public:
|
||||
void draw() override {
|
||||
std::cout << "Drawing a rectangle\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Shape* shape;
|
||||
Circle circle;
|
||||
Rectangle rectangle;
|
||||
|
||||
// Storing the address of circle
|
||||
shape = &circle;
|
||||
|
||||
// Call circle draw function
|
||||
shape->draw();
|
||||
|
||||
// Storing the address of rectangle
|
||||
shape = &rectangle;
|
||||
|
||||
// Call rectangle draw function
|
||||
shape->draw();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
This code defines a base class `Shape` with a virtual function `draw`. Two derived classes `Circle` and `Rectangle` both override the `draw` function to provide their own implementations. Then in the `main` function, a pointer of type `Shape` is used to call the respective `draw` functions of `Circle` and `Rectangle` objects. The output of this program will be:
|
||||
|
||||
```
|
||||
Drawing a circle
|
||||
Drawing a rectangle
|
||||
```
|
||||
Drawing a circle
|
||||
Drawing a rectangle
|
||||
|
||||
|
||||
As you can see, using dynamic polymorphism, we can determine at runtime which `draw` method should be called based on the type of object being used.
|
||||
@@ -4,57 +4,59 @@ C++ is known as a statically-typed language, which means the data types of its v
|
||||
|
||||
Here is a brief overview of two ways to achieve dynamic typing in C++:
|
||||
|
||||
## `void*` Pointers
|
||||
`void*` Pointers
|
||||
----------------
|
||||
|
||||
A `void*` pointer is a generic pointer that can point to objects of any data type. They can be used to store a reference to any type of object without knowing the specific type of the object.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int x = 42;
|
||||
float y = 3.14f;
|
||||
std::string z = "Hello, world!";
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int x = 42;
|
||||
float y = 3.14f;
|
||||
std::string z = "Hello, world!";
|
||||
|
||||
void* void_ptr;
|
||||
|
||||
void_ptr = &x;
|
||||
std::cout << "int value: " << *(static_cast<int*>(void_ptr)) << '\n';
|
||||
|
||||
void_ptr = &y;
|
||||
std::cout << "float value: " << *(static_cast<float*>(void_ptr)) << '\n';
|
||||
|
||||
void_ptr = &z;
|
||||
std::cout << "string value: " << *(static_cast<std::string*>(void_ptr)) << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void* void_ptr;
|
||||
|
||||
void_ptr = &x;
|
||||
std::cout << "int value: " << *(static_cast<int*>(void_ptr)) << '\n';
|
||||
|
||||
void_ptr = &y;
|
||||
std::cout << "float value: " << *(static_cast<float*>(void_ptr)) << '\n';
|
||||
|
||||
void_ptr = &z;
|
||||
std::cout << "string value: " << *(static_cast<std::string*>(void_ptr)) << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## `std::any` (C++17)
|
||||
`std::any` (C++17)
|
||||
------------------
|
||||
|
||||
C++17 introduced the `std::any` class which represents a generalized type-safe container for single values of any type.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <any>
|
||||
|
||||
int main() {
|
||||
std::any any_value;
|
||||
|
||||
any_value = 42;
|
||||
std::cout << "int value: " << std::any_cast<int>(any_value) << '\n';
|
||||
|
||||
any_value = 3.14;
|
||||
std::cout << "double value: " << std::any_cast<double>(any_value) << '\n';
|
||||
|
||||
any_value = std::string("Hello, world!");
|
||||
std::cout << "string value: " << std::any_cast<std::string>(any_value) << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
#include <any>
|
||||
|
||||
int main() {
|
||||
std::any any_value;
|
||||
|
||||
any_value = 42;
|
||||
std::cout << "int value: " << std::any_cast<int>(any_value) << '\n';
|
||||
|
||||
any_value = 3.14;
|
||||
std::cout << "double value: " << std::any_cast<double>(any_value) << '\n';
|
||||
|
||||
any_value = std::string("Hello, world!");
|
||||
std::cout << "string value: " << std::any_cast<std::string>(any_value) << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Keep in mind that both `void*` pointers and `std::any` have performance implications due to the additional type checking and casting that take place during runtime. They should be used carefully and only when absolutely necessary.
|
||||
@@ -4,37 +4,36 @@
|
||||
|
||||
Here is a basic example of how `dynamic_cast` can be used:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
class BaseClass {
|
||||
public:
|
||||
virtual void display() {
|
||||
std::cout << "BaseClass\n";
|
||||
#include <iostream>
|
||||
|
||||
class BaseClass {
|
||||
public:
|
||||
virtual void display() {
|
||||
std::cout << "BaseClass\n";
|
||||
}
|
||||
};
|
||||
|
||||
class DerivedClass : public BaseClass {
|
||||
public:
|
||||
void display() {
|
||||
std::cout << "DerivedClass\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
BaseClass *basePtr = new DerivedClass(); // Upcasting
|
||||
DerivedClass *derivedPtr;
|
||||
|
||||
derivedPtr = dynamic_cast<DerivedClass *>(basePtr); // Downcasting
|
||||
if (derivedPtr) {
|
||||
derivedPtr->display(); // Output: DerivedClass
|
||||
} else {
|
||||
std::cout << "Invalid type conversion.";
|
||||
}
|
||||
|
||||
delete basePtr;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class DerivedClass : public BaseClass {
|
||||
public:
|
||||
void display() {
|
||||
std::cout << "DerivedClass\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
BaseClass *basePtr = new DerivedClass(); // Upcasting
|
||||
DerivedClass *derivedPtr;
|
||||
|
||||
derivedPtr = dynamic_cast<DerivedClass *>(basePtr); // Downcasting
|
||||
if (derivedPtr) {
|
||||
derivedPtr->display(); // Output: DerivedClass
|
||||
} else {
|
||||
std::cout << "Invalid type conversion.";
|
||||
}
|
||||
|
||||
delete basePtr;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, a pointer to a `DerivedClass` object is assigned to a `BaseClass` pointer (`basePtr`). Then, we attempt to downcast it back to a `DerivedClass` pointer using `dynamic_cast`. If the casting is successful, we can access the `DerivedClass` functionality through the new pointer (`derivedPtr`).
|
||||
@@ -3,34 +3,33 @@
|
||||
The erase-remove idiom is a common C++ technique to efficiently remove elements from a container, particularly from standard sequence containers like `std::vector`, `std::list`, and `std::deque`. It leverages the standard library algorithms `std::remove` (or `std::remove_if`) and the member function `erase()`.
|
||||
|
||||
The idiom consists of two steps:
|
||||
- `std::remove` (or `std::remove_if`) moves the elements to be removed towards the end of the container and returns an iterator pointing to the first element to remove.
|
||||
- `container.erase()` removes the elements from the container using the iterator obtained in the previous step.
|
||||
|
||||
* `std::remove` (or `std::remove_if`) moves the elements to be removed towards the end of the container and returns an iterator pointing to the first element to remove.
|
||||
* `container.erase()` removes the elements from the container using the iterator obtained in the previous step.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```cpp
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::vector<int> numbers = {1, 3, 2, 4, 3, 5, 3};
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
// Remove all occurrences of 3 from the vector.
|
||||
numbers.erase(std::remove(numbers.begin(), numbers.end(), 3), numbers.end());
|
||||
|
||||
for (int number : numbers) {
|
||||
std::cout << number << " ";
|
||||
int main() {
|
||||
std::vector<int> numbers = {1, 3, 2, 4, 3, 5, 3};
|
||||
|
||||
// Remove all occurrences of 3 from the vector.
|
||||
numbers.erase(std::remove(numbers.begin(), numbers.end(), 3), numbers.end());
|
||||
|
||||
for (int number : numbers) {
|
||||
std::cout << number << " ";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
1 2 4 5
|
||||
```
|
||||
1 2 4 5
|
||||
|
||||
|
||||
In this example, we used the `std::remove` algorithm to remove all occurrences of the number 3 from the `std::vector<int> numbers`. After the removal, the vector contains only 1, 2, 4, and 5, as the output shows.
|
||||
@@ -4,80 +4,80 @@ Exception handling in C++ is a mechanism to handle errors, anomalies, or unexpec
|
||||
|
||||
C++ provides a set of keywords and constructs for implementing exception handling:
|
||||
|
||||
- `try`: Defines a block of code that should be monitored for exceptions.
|
||||
- `catch`: Specifies the type of exception to be caught and the block of code that shall be executed when that exception occurs.
|
||||
- `throw`: Throws an exception that will be caught and handled by the appropriate catch block.
|
||||
- `noexcept`: Specifies a function that doesn't throw exceptions or terminates the program if an exception is thrown within its scope.
|
||||
* `try`: Defines a block of code that should be monitored for exceptions.
|
||||
* `catch`: Specifies the type of exception to be caught and the block of code that shall be executed when that exception occurs.
|
||||
* `throw`: Throws an exception that will be caught and handled by the appropriate catch block.
|
||||
* `noexcept`: Specifies a function that doesn't throw exceptions or terminates the program if an exception is thrown within its scope.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
Here's an example demonstrating the basic usage of exception handling:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
throw "Division by zero!";
|
||||
#include <iostream>
|
||||
|
||||
int divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
throw "Division by zero!";
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int num1, num2;
|
||||
|
||||
std::cout << "Enter two numbers for division: ";
|
||||
std::cin >> num1 >> num2;
|
||||
|
||||
try {
|
||||
int result = divide(num1, num2);
|
||||
std::cout << "The result is: " << result << '\n';
|
||||
} catch (const char* msg) {
|
||||
std::cerr << "Error: " << msg << '\n';
|
||||
|
||||
int main() {
|
||||
int num1, num2;
|
||||
|
||||
std::cout << "Enter two numbers for division: ";
|
||||
std::cin >> num1 >> num2;
|
||||
|
||||
try {
|
||||
int result = divide(num1, num2);
|
||||
std::cout << "The result is: " << result << '\n';
|
||||
} catch (const char* msg) {
|
||||
std::cerr << "Error: " << msg << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, we define a function `divide` that throws an exception if `b` is zero. In the `main` function, we use a `try` block to call `divide` and output the result. If an exception is thrown, it is caught inside the `catch` block, which outputs an error message. This way, we can handle the error gracefully rather than letting the program crash when attempting to divide by zero.
|
||||
|
||||
## Standard Exceptions
|
||||
Standard Exceptions
|
||||
-------------------
|
||||
|
||||
C++ provides a standard set of exception classes under the `<stdexcept>` library which can be used as the exception type for more specific error handling. Some of these classes include:
|
||||
|
||||
- `std::exception`: Base class for all standard exceptions.
|
||||
- `std::logic_error`: Represents errors which can be detected statically by the program.
|
||||
- `std::runtime_error`: Represents errors occurring during the execution of a program.
|
||||
* `std::exception`: Base class for all standard exceptions.
|
||||
* `std::logic_error`: Represents errors which can be detected statically by the program.
|
||||
* `std::runtime_error`: Represents errors occurring during the execution of a program.
|
||||
|
||||
Here's an example showing how to use standard exceptions:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
int divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
throw std::runtime_error("Division by zero!");
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
int divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
throw std::runtime_error("Division by zero!");
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int num1, num2;
|
||||
|
||||
std::cout << "Enter two numbers for division: ";
|
||||
std::cin >> num1 >> num2;
|
||||
|
||||
try {
|
||||
int result = divide(num1, num2);
|
||||
std::cout << "The result is: " << result << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << '\n';
|
||||
|
||||
int main() {
|
||||
int num1, num2;
|
||||
|
||||
std::cout << "Enter two numbers for division: ";
|
||||
std::cin >> num1 >> num2;
|
||||
|
||||
try {
|
||||
int result = divide(num1, num2);
|
||||
std::cout << "The result is: " << result << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, we modified the `divide` function to throw a `std::runtime_error` instead of a simple string. The catch block now catches exceptions derived from `std::exception` and uses the member function `what()` to display the error message.
|
||||
@@ -2,49 +2,55 @@
|
||||
|
||||
Exception handling is a method used to tackle runtime errors so that normal flow of the program can be maintained. In C++, this is accomplished using three keywords: `try`, `catch`, and `throw`.
|
||||
|
||||
## try { ... }
|
||||
try { ... }
|
||||
-----------
|
||||
|
||||
In the `try` block, you write the code that can possibly generate an exception. If an exception is encountered, the control is passed to the relevant `catch` block to handle the issue.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
try {
|
||||
// code that might throw an exception
|
||||
}
|
||||
```
|
||||
|
||||
## catch (...) { ... }
|
||||
try {
|
||||
// code that might throw an exception
|
||||
}
|
||||
|
||||
|
||||
catch (...) { ... }
|
||||
-------------------
|
||||
|
||||
The `catch` block follows the `try` block and is responsible for handling the exceptions thrown by the `try` block. There can be multiple `catch` blocks to handle different types of exceptions.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
catch (int e) {
|
||||
// handle exception of type int
|
||||
}
|
||||
catch (char e) {
|
||||
// handle exception of type char
|
||||
}
|
||||
catch (...) {
|
||||
// handle any other exception
|
||||
}
|
||||
```
|
||||
|
||||
## throw ... ;
|
||||
catch (int e) {
|
||||
// handle exception of type int
|
||||
}
|
||||
catch (char e) {
|
||||
// handle exception of type char
|
||||
}
|
||||
catch (...) {
|
||||
// handle any other exception
|
||||
}
|
||||
|
||||
|
||||
throw ... ;
|
||||
-----------
|
||||
|
||||
In case an error occurs within the `try` block, you can use the `throw` keyword to generate an exception of the specific type. This will then be caught and handled by the corresponding `catch` block.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
try {
|
||||
int num1 = 10, num2 = 0;
|
||||
if (num2 == 0) {
|
||||
throw "Division by zero not allowed!";
|
||||
} else {
|
||||
int result = num1 / num2;
|
||||
std::cout << "Result: " << result << '\n';
|
||||
}
|
||||
}
|
||||
catch (const char* e) {
|
||||
std::cout << "Error: " << e << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
In summary, exception handling in C++ is a technique to handle runtime errors while maintaining the normal flow of the program. The `try`, `catch`, and `throw` keywords are used together to create the structure to deal with exceptions as they occur.
|
||||
try {
|
||||
int num1 = 10, num2 = 0;
|
||||
if (num2 == 0) {
|
||||
throw "Division by zero not allowed!";
|
||||
} else {
|
||||
int result = num1 / num2;
|
||||
std::cout << "Result: " << result << '\n';
|
||||
}
|
||||
}
|
||||
catch (const char* e) {
|
||||
std::cout << "Error: " << e << '\n';
|
||||
}
|
||||
|
||||
|
||||
In summary, exception handling in C++ is a technique to handle runtime errors while maintaining the normal flow of the program. The `try`, `catch`, and `throw` keywords are used together to create the structure to deal with exceptions as they occur.
|
||||
@@ -6,54 +6,54 @@ Exit codes, also known as "return codes" or "status codes", are numeric values t
|
||||
|
||||
In C++, you can return an exit code from the `main` function by using the `return` statement, or you can use the `exit()` function, which is part of the C++ Standard Library.
|
||||
|
||||
## Example: Using return in `main`
|
||||
Example: Using return in `main`
|
||||
-------------------------------
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
// Some code here...
|
||||
|
||||
if (/*some error condition*/) {
|
||||
std::cout << "An error occurred.\n";
|
||||
return 1;
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
// Some code here...
|
||||
|
||||
if (/*some error condition*/) {
|
||||
std::cout << "An error occurred.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// More code here...
|
||||
|
||||
if (/*another error condition*/) {
|
||||
std::cout << "Another error occurred.\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0; // Successful execution
|
||||
}
|
||||
|
||||
|
||||
// More code here...
|
||||
Example: Using the `exit()` function
|
||||
------------------------------------
|
||||
|
||||
if (/*another error condition*/) {
|
||||
std::cout << "Another error occurred.\n";
|
||||
return 2;
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
void some_function() {
|
||||
// Some code here...
|
||||
|
||||
if (/*some error condition*/) {
|
||||
std::cout << "An error occurred.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// More code here...
|
||||
}
|
||||
|
||||
return 0; // Successful execution
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Using the `exit()` function
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
void some_function() {
|
||||
// Some code here...
|
||||
|
||||
if (/*some error condition*/) {
|
||||
std::cout << "An error occurred.\n";
|
||||
std::exit(1);
|
||||
|
||||
int main() {
|
||||
some_function();
|
||||
|
||||
// Some other code here...
|
||||
|
||||
return 0; // Successful execution
|
||||
}
|
||||
|
||||
// More code here...
|
||||
}
|
||||
|
||||
int main() {
|
||||
some_function();
|
||||
|
||||
// Some other code here...
|
||||
|
||||
return 0; // Successful execution
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In both examples above, the program returns exit codes depending on different error conditions encountered during execution. The codes 1 and 2 are used to distinguish between the two error conditions.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,87 +2,84 @@
|
||||
|
||||
Loops are an essential concept in programming that allow you to execute a block of code repeatedly until a specific condition is met. In C++, there are three main types of loops: `for`, `while`, and `do-while`.
|
||||
|
||||
## For Loop
|
||||
For Loop
|
||||
--------
|
||||
|
||||
A `for` loop is used when you know the number of times you want to traverse through a block of code. It consists of an initialization statement, a condition, and an increment/decrement operation.
|
||||
|
||||
Here's the syntax for a `for` loop:
|
||||
|
||||
```cpp
|
||||
for (initialization; condition; increment/decrement) {
|
||||
// block of code to execute
|
||||
}
|
||||
```
|
||||
for (initialization; condition; increment/decrement) {
|
||||
// block of code to execute
|
||||
}
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::cout << "Iteration: " << i << '\n';
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::cout << "Iteration: " << i << '\n';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## While Loop
|
||||
While Loop
|
||||
----------
|
||||
|
||||
A `while` loop runs as long as a specified condition is `true`. The loop checks for the condition before entering the body of the loop.
|
||||
|
||||
Here's the syntax for a `while` loop:
|
||||
|
||||
```cpp
|
||||
while (condition) {
|
||||
// block of code to execute
|
||||
}
|
||||
```
|
||||
while (condition) {
|
||||
// block of code to execute
|
||||
}
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
std::cout << "Iteration: " << i << '\n';
|
||||
i++;
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
std::cout << "Iteration: " << i << '\n';
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Do-While Loop
|
||||
Do-While Loop
|
||||
-------------
|
||||
|
||||
A `do-while` loop is similar to a `while` loop, with the key difference being that the loop body is executed at least once, even when the condition is `false`.
|
||||
|
||||
Here's the syntax for a `do-while` loop:
|
||||
|
||||
```cpp
|
||||
do {
|
||||
// block of code to execute
|
||||
} while (condition);
|
||||
```
|
||||
do {
|
||||
// block of code to execute
|
||||
} while (condition);
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int i = 0;
|
||||
do {
|
||||
std::cout << "Iteration: " << i << '\n';
|
||||
i++;
|
||||
} while (i < 5);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int i = 0;
|
||||
do {
|
||||
std::cout << "Iteration: " << i << '\n';
|
||||
i++;
|
||||
} while (i < 5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
In summary, loops are an integral part of C++ programming that allow you to execute a block of code multiple times. The three types of loops in C++ are `for`, `while`, and `do-while`. Each type has its own specific use case and can be chosen depending on the desired behavior.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@C++ For Loop](https://www.w3schools.com/cpp/cpp_for_loop.asp)
|
||||
@@ -2,45 +2,45 @@
|
||||
|
||||
Forward declaration is a way of declaring a symbol (class, function, or variable) before defining it in the code. It helps the compiler understand the type, size, and existence of the symbol. This declaration is particularly useful when we have cyclic dependencies or to reduce compilation time by avoiding unnecessary header inclusions in the source file.
|
||||
|
||||
## Class Forward Declaration
|
||||
Class Forward Declaration
|
||||
-------------------------
|
||||
|
||||
To use a class type before it is defined, you can declare the class without defining its members, like this:
|
||||
|
||||
```cpp
|
||||
class ClassA; // forward declaration
|
||||
```
|
||||
class ClassA; // forward declaration
|
||||
|
||||
|
||||
You can then use pointers or references to the class in your code before defining the class itself:
|
||||
|
||||
```cpp
|
||||
void do_something (ClassA& obj);
|
||||
|
||||
class ClassB {
|
||||
public:
|
||||
void another_function(ClassA& obj);
|
||||
};
|
||||
```
|
||||
void do_something (ClassA& obj);
|
||||
|
||||
class ClassB {
|
||||
public:
|
||||
void another_function(ClassA& obj);
|
||||
};
|
||||
|
||||
|
||||
However, if you try to make an object of `ClassA` or call its member functions without defining the class, you will get a compilation error.
|
||||
|
||||
## Function Forward Declaration
|
||||
Function Forward Declaration
|
||||
----------------------------
|
||||
|
||||
Functions must be declared before using them, and a forward declaration can be used to declare a function without defining it:
|
||||
|
||||
```cpp
|
||||
int add(int a, int b); // forward declaration
|
||||
int add(int a, int b); // forward declaration
|
||||
|
||||
int main() {
|
||||
int result = add(2, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
int result = add(2, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
## Enum and Typedef Forward Declaration
|
||||
Enum and Typedef Forward Declaration
|
||||
------------------------------------
|
||||
|
||||
For `enum` and `typedef`, it is not possible to forward declare because they don't have separate declaration and definition stages.
|
||||
|
||||
|
||||
@@ -2,45 +2,47 @@
|
||||
|
||||
Full template specialization allows you to provide a specific implementation, or behavior, for a template when used with a certain set of type parameters. It is useful when you want to handle special cases or optimize your code for specific types.
|
||||
|
||||
## Syntax
|
||||
Syntax
|
||||
------
|
||||
|
||||
To create a full specialization of a template, you need to define the specific type for which the specialization should happen. The syntax looks as follows:
|
||||
|
||||
```cpp
|
||||
template <> //Indicates that this is a specialization
|
||||
className<specificType> //The specialized class for the specific type
|
||||
```
|
||||
template <> //Indicates that this is a specialization
|
||||
className<specificType> //The specialized class for the specific type
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
## Example
|
||||
Consider the following example to demonstrate full template specialization:
|
||||
|
||||
```cpp
|
||||
// Generic template
|
||||
template <typename T>
|
||||
class MyContainer {
|
||||
public:
|
||||
void print() {
|
||||
std::cout << "Generic container.\n";
|
||||
// Generic template
|
||||
template <typename T>
|
||||
class MyContainer {
|
||||
public:
|
||||
void print() {
|
||||
std::cout << "Generic container.\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Full template specialization for int
|
||||
template <>
|
||||
class MyContainer<int> {
|
||||
public:
|
||||
void print() {
|
||||
std::cout << "Container for integers.\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyContainer<double> d;
|
||||
MyContainer<int> i;
|
||||
|
||||
d.print(); // Output: Generic container.
|
||||
i.print(); // Output: Container for integers.
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Full template specialization for int
|
||||
template <>
|
||||
class MyContainer<int> {
|
||||
public:
|
||||
void print() {
|
||||
std::cout << "Container for integers.\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyContainer<double> d;
|
||||
MyContainer<int> i;
|
||||
|
||||
d.print(); // Output: Generic container.
|
||||
i.print(); // Output: Container for integers.
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, we defined a generic `MyContainer` template class along with a full specialization for `int` type. When we use the container with the `int` type, the specialized implementation's `print` method is called. For other types, the generic template implementation will be used.
|
||||
@@ -1,8 +1,7 @@
|
||||
# Function Overloading
|
||||
|
||||
Function overloading in C++ allows multiple functions to share the same name, provided they differ in the number or types of parameters. This facilitates compile-time polymorphism, enhancing code readability and maintainability by enabling functions to perform similar operations on different data types or argument counts.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@official@Function Overloading - Microsoft Learn](https://learn.microsoft.com/en-us/cpp/cpp/function-overloading)
|
||||
|
||||
# Function Overloading
|
||||
|
||||
Function overloading in C++ allows multiple functions to share the same name, provided they differ in the number or types of parameters. This facilitates compile-time polymorphism, enhancing code readability and maintainability by enabling functions to perform similar operations on different data types or argument counts.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@official@Function Overloading - Microsoft Learn](https://learn.microsoft.com/en-us/cpp/cpp/function-overloading)
|
||||
@@ -4,73 +4,74 @@ A **function** is a group of statements that perform a specific task, organized
|
||||
|
||||
There are mainly two types of functions in C++:
|
||||
|
||||
- **Standard library functions**: Pre-defined functions available in the C++ standard library, such as `sort()`, `strlen()`, `sqrt()`, and many more. These functions are part of the standard library, so you need to include the appropriate header file to use them.
|
||||
* **Standard library functions**: Pre-defined functions available in the C++ standard library, such as `sort()`, `strlen()`, `sqrt()`, and many more. These functions are part of the standard library, so you need to include the appropriate header file to use them.
|
||||
|
||||
* **User-defined functions**: Functions created by the programmer to perform a specific task. To create a user-defined function, you need to define the function and call it in your code.
|
||||
|
||||
|
||||
- **User-defined functions**: Functions created by the programmer to perform a specific task. To create a user-defined function, you need to define the function and call it in your code.
|
||||
|
||||
## Defining a Function
|
||||
Defining a Function
|
||||
-------------------
|
||||
|
||||
The general format for defining a function in C++ is:
|
||||
|
||||
```cpp
|
||||
return_type function_name(parameter list) {
|
||||
// function body
|
||||
}
|
||||
```
|
||||
return_type function_name(parameter list) {
|
||||
// function body
|
||||
}
|
||||
|
||||
|
||||
- `return_type`: Data type of the output produced by the function. It can be `void`, indicating that the function doesn't return any value.
|
||||
- `function_name`: Name given to the function, following C++ naming conventions.
|
||||
- `parameter list`: List of input parameters/arguments that are needed to perform the task. It is optional, you can leave it blank when no parameters are needed.
|
||||
* `return_type`: Data type of the output produced by the function. It can be `void`, indicating that the function doesn't return any value.
|
||||
* `function_name`: Name given to the function, following C++ naming conventions.
|
||||
* `parameter list`: List of input parameters/arguments that are needed to perform the task. It is optional, you can leave it blank when no parameters are needed.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
// Function to add two numbers
|
||||
int addNumbers(int a, int b) {
|
||||
int sum = a + b;
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int num1 = 5, num2 = 10;
|
||||
int result = addNumbers(num1, num2); // Calling the function
|
||||
std::cout << "The sum is: " << result << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
// Function to add two numbers
|
||||
int addNumbers(int a, int b) {
|
||||
int sum = a + b;
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int num1 = 5, num2 = 10;
|
||||
int result = addNumbers(num1, num2); // Calling the function
|
||||
std::cout << "The sum is: " << result << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
In this example, the function `addNumbers` takes two integer parameters, `a` and `b`, and returns the sum of the numbers. We then call this function from the `main()` function and display the result.
|
||||
|
||||
## Function Prototypes
|
||||
Function Prototypes
|
||||
-------------------
|
||||
|
||||
In some cases, you might want to use a function before actually defining it. To do this, you need to declare a **function prototype** at the beginning of your code.
|
||||
|
||||
A function prototype is a declaration of the function without its body, and it informs the compiler about the function's name, return type, and parameters.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
// Function prototype
|
||||
int multiplyNumbers(int x, int y);
|
||||
|
||||
int main() {
|
||||
int num1 = 3, num2 = 7;
|
||||
int result = multiplyNumbers(num1, num2); // Calling the function
|
||||
std::cout << "The product is: " << result << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function definition
|
||||
int multiplyNumbers(int x, int y) {
|
||||
int product = x * y;
|
||||
return product;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
// Function prototype
|
||||
int multiplyNumbers(int x, int y);
|
||||
|
||||
int main() {
|
||||
int num1 = 3, num2 = 7;
|
||||
int result = multiplyNumbers(num1, num2); // Calling the function
|
||||
std::cout << "The product is: " << result << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function definition
|
||||
int multiplyNumbers(int x, int y) {
|
||||
int product = x * y;
|
||||
return product;
|
||||
}
|
||||
|
||||
|
||||
In this example, we use a function prototype for `multiplyNumbers()` before defining it. This way, we can call the function from the `main()` function even though it hasn't been defined yet in the code.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@introduction to functions in c++](https://www.learncpp.com/cpp-tutorial/introduction-to-functions/)
|
||||
- [@article@introduction to functions in c++](https://www.learncpp.com/cpp-tutorial/introduction-to-functions/)
|
||||
@@ -2,79 +2,75 @@
|
||||
|
||||
GDB, or the GNU Project Debugger, is a powerful command-line debugger used primarily for C, C++, and other languages. It can help you find runtime errors, examine the program's execution state, and manipulate the flow to detect and fix bugs easily.
|
||||
|
||||
## Getting started with GDB
|
||||
Getting started with GDB
|
||||
------------------------
|
||||
|
||||
To start using GDB, you first need to compile your code with the `-g` flag, which includes debugging information in the executable:
|
||||
|
||||
```sh
|
||||
g++ -g myfile.cpp -o myfile
|
||||
```
|
||||
g++ -g myfile.cpp -o myfile
|
||||
|
||||
|
||||
Now, you can load your compiled program into GDB:
|
||||
|
||||
```sh
|
||||
gdb myfile
|
||||
```
|
||||
gdb myfile
|
||||
|
||||
|
||||
## Basic GDB Commands
|
||||
Basic GDB Commands
|
||||
------------------
|
||||
|
||||
Here are some common GDB commands you'll find useful when debugging:
|
||||
|
||||
- `run`: Start your program.
|
||||
- `break [function/line number]`: Set a breakpoint at the specified function or line.
|
||||
- `continue`: Continue the program execution after stopping on a breakpoint.
|
||||
- `next`: Execute the next line of code, stepping over function calls.
|
||||
- `step`: Execute the next line of code, entering function calls.
|
||||
- `print [expression]`: Evaluate an expression in the current context and display its value.
|
||||
- `backtrace`: Show the current call stack.
|
||||
- `frame [frame-number]`: Switch to a different stack frame.
|
||||
- `quit`: Exit GDB.
|
||||
* `run`: Start your program.
|
||||
* `break [function/line number]`: Set a breakpoint at the specified function or line.
|
||||
* `continue`: Continue the program execution after stopping on a breakpoint.
|
||||
* `next`: Execute the next line of code, stepping over function calls.
|
||||
* `step`: Execute the next line of code, entering function calls.
|
||||
* `print [expression]`: Evaluate an expression in the current context and display its value.
|
||||
* `backtrace`: Show the current call stack.
|
||||
* `frame [frame-number]`: Switch to a different stack frame.
|
||||
* `quit`: Exit GDB.
|
||||
|
||||
## Example Usage
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
Suppose you have a simple `cpp` file called `example.cpp`:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
void my_function(int i) {
|
||||
std::cout << "In my_function with i = " << i << '\n';
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
my_function(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
void my_function(int i) {
|
||||
std::cout << "In my_function with i = " << i << '\n';
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
my_function(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
First, compile the code with debugging symbols:
|
||||
|
||||
```sh
|
||||
g++ -g example.cpp -o example
|
||||
```
|
||||
g++ -g example.cpp -o example
|
||||
|
||||
|
||||
Start GDB and load the `example` program:
|
||||
|
||||
```sh
|
||||
gdb example
|
||||
```
|
||||
gdb example
|
||||
|
||||
|
||||
Set a breakpoint in the `my_function` function and run the program:
|
||||
|
||||
```
|
||||
(gdb) break my_function
|
||||
(gdb) run
|
||||
```
|
||||
(gdb) break my_function
|
||||
(gdb) run
|
||||
|
||||
|
||||
Once stopped at the breakpoint, use `next`, `print`, and `continue` to examine the program's state:
|
||||
|
||||
```
|
||||
(gdb) next
|
||||
(gdb) print i
|
||||
(gdb) continue
|
||||
```
|
||||
(gdb) next
|
||||
(gdb) print i
|
||||
(gdb) continue
|
||||
|
||||
|
||||
Finally, exit GDB with the `quit` command.
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -8,18 +8,17 @@ Header files, usually with the `.h` or `.hpp` extension, are responsible for dec
|
||||
|
||||
Example of a header file:
|
||||
|
||||
```cpp
|
||||
// example.h
|
||||
#ifndef EXAMPLE_H
|
||||
#define EXAMPLE_H
|
||||
|
||||
class Example {
|
||||
public:
|
||||
void printMessage();
|
||||
};
|
||||
|
||||
#endif
|
||||
```
|
||||
// example.h
|
||||
#ifndef EXAMPLE_H
|
||||
#define EXAMPLE_H
|
||||
|
||||
class Example {
|
||||
public:
|
||||
void printMessage();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
### Source Files (.cpp)
|
||||
|
||||
@@ -27,15 +26,14 @@ Source files, with the `.cpp` extension, are responsible for implementing the ac
|
||||
|
||||
Example of a source file:
|
||||
|
||||
```cpp
|
||||
// example.cpp
|
||||
#include "example.h"
|
||||
#include <iostream>
|
||||
|
||||
void Example::printMessage() {
|
||||
std::cout << "Hello, code splitting!\n";
|
||||
}
|
||||
```
|
||||
// example.cpp
|
||||
#include "example.h"
|
||||
#include <iostream>
|
||||
|
||||
void Example::printMessage() {
|
||||
std::cout << "Hello, code splitting!\n";
|
||||
}
|
||||
|
||||
|
||||
### Separate Compilation
|
||||
|
||||
@@ -43,13 +41,12 @@ C++ allows for separate compilation, which means that each source file can be co
|
||||
|
||||
Example of separate compilation and linking:
|
||||
|
||||
```sh
|
||||
# Compile each source file into an object file
|
||||
g++ -c main.cpp -o main.o
|
||||
g++ -c example.cpp -o example.o
|
||||
|
||||
# Link object files together to create the executable
|
||||
g++ main.o example.o -o my_program
|
||||
```
|
||||
# Compile each source file into an object file
|
||||
g++ -c main.cpp -o main.o
|
||||
g++ -c example.cpp -o example.o
|
||||
|
||||
# Link object files together to create the executable
|
||||
g++ main.o example.o -o my_program
|
||||
|
||||
|
||||
By following the code splitting technique, you can better organize your C++ codebase, making it more manageable and maintainable.
|
||||
@@ -2,103 +2,103 @@
|
||||
|
||||
C++ idioms are well-established patterns or techniques that are commonly used in C++ programming to achieve a specific outcome. They help make code efficient, maintainable, and less error-prone. Here are some of the common C++ idioms:
|
||||
|
||||
## 1. Resource Acquisition is Initialization (RAII)
|
||||
1\. Resource Acquisition is Initialization (RAII)
|
||||
-------------------------------------------------
|
||||
|
||||
This idiom ensures that resources are always properly acquired and released by tying their lifetime to the lifetime of an object. When the object gets created, it acquires the resources and when it gets destroyed, it releases them.
|
||||
|
||||
```cpp
|
||||
class Resource {
|
||||
public:
|
||||
Resource() { /* Acquire resource */ }
|
||||
~Resource() { /* Release resource */ }
|
||||
};
|
||||
class Resource {
|
||||
public:
|
||||
Resource() { /* Acquire resource */ }
|
||||
~Resource() { /* Release resource */ }
|
||||
};
|
||||
|
||||
void function() {
|
||||
Resource r; // Resource is acquired
|
||||
// ...
|
||||
} // Resource is released when r goes out of scope
|
||||
|
||||
|
||||
void function() {
|
||||
Resource r; // Resource is acquired
|
||||
// ...
|
||||
} // Resource is released when r goes out of scope
|
||||
```
|
||||
|
||||
## 2. Rule of Three
|
||||
2\. Rule of Three
|
||||
-----------------
|
||||
|
||||
If a class defines any one of the following, it should define all three: copy constructor, copy assignment operator, and destructor.
|
||||
|
||||
```cpp
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass();
|
||||
MyClass(const MyClass& other); // Copy constructor
|
||||
MyClass& operator=(const MyClass& other); // Copy assignment operator
|
||||
~MyClass(); // Destructor
|
||||
};
|
||||
```
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass();
|
||||
MyClass(const MyClass& other); // Copy constructor
|
||||
MyClass& operator=(const MyClass& other); // Copy assignment operator
|
||||
~MyClass(); // Destructor
|
||||
};
|
||||
|
||||
|
||||
## 3. Rule of Five
|
||||
3\. Rule of Five
|
||||
----------------
|
||||
|
||||
With C++11, the rule of three was extended to five, covering move constructor and move assignment operator.
|
||||
|
||||
```cpp
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass();
|
||||
MyClass(const MyClass& other); // Copy constructor
|
||||
MyClass(MyClass&& other); // Move constructor
|
||||
MyClass& operator=(const MyClass& other); // Copy assignment operator
|
||||
MyClass& operator=(MyClass&& other); // Move assignment operator
|
||||
~MyClass(); // Destructor
|
||||
};
|
||||
```
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass();
|
||||
MyClass(const MyClass& other); // Copy constructor
|
||||
MyClass(MyClass&& other); // Move constructor
|
||||
MyClass& operator=(const MyClass& other); // Copy assignment operator
|
||||
MyClass& operator=(MyClass&& other); // Move assignment operator
|
||||
~MyClass(); // Destructor
|
||||
};
|
||||
|
||||
|
||||
## 4. PImpl (Pointer to Implementation) Idiom
|
||||
4\. PImpl (Pointer to Implementation) Idiom
|
||||
-------------------------------------------
|
||||
|
||||
This idiom is used to separate the implementation details of a class from its interface, resulting in faster compile times and the ability to change implementation without affecting clients.
|
||||
|
||||
```cpp
|
||||
// header file
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass();
|
||||
~MyClass();
|
||||
void someMethod();
|
||||
// header file
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass();
|
||||
~MyClass();
|
||||
void someMethod();
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* pImpl;
|
||||
};
|
||||
|
||||
// implementation file
|
||||
class MyClass::Impl {
|
||||
public:
|
||||
void someMethod() { /* Implementation */ }
|
||||
};
|
||||
|
||||
MyClass::MyClass() : pImpl(new Impl()) {}
|
||||
MyClass::~MyClass() { delete pImpl; }
|
||||
void MyClass::someMethod() { pImpl->someMethod(); }
|
||||
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* pImpl;
|
||||
};
|
||||
|
||||
// implementation file
|
||||
class MyClass::Impl {
|
||||
public:
|
||||
void someMethod() { /* Implementation */ }
|
||||
};
|
||||
|
||||
MyClass::MyClass() : pImpl(new Impl()) {}
|
||||
MyClass::~MyClass() { delete pImpl; }
|
||||
void MyClass::someMethod() { pImpl->someMethod(); }
|
||||
```
|
||||
|
||||
## 5. Non-Virtual Interface (NVI)
|
||||
5\. Non-Virtual Interface (NVI)
|
||||
-------------------------------
|
||||
|
||||
This enforces a fixed public interface and allows subclasses to only override specific private or protected virtual methods.
|
||||
|
||||
```cpp
|
||||
class Base {
|
||||
public:
|
||||
void publicMethod() {
|
||||
// Common behavior
|
||||
privateMethod(); // Calls overridden implementation
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void privateMethod() = 0; // Pure virtual method
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
protected:
|
||||
virtual void privateMethod() override {
|
||||
// Derived implementation
|
||||
}
|
||||
};
|
||||
```
|
||||
class Base {
|
||||
public:
|
||||
void publicMethod() {
|
||||
// Common behavior
|
||||
privateMethod(); // Calls overridden implementation
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void privateMethod() = 0; // Pure virtual method
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
protected:
|
||||
virtual void privateMethod() override {
|
||||
// Derived implementation
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
These are just a few examples of the many idioms in C++ programming. They can provide guidance when designing and implementing your code, but it's essential to understand the underlying concepts to adapt them to different situations.
|
||||
@@ -6,89 +6,80 @@ C++ provides you with tools which helps you to control the way your program beha
|
||||
|
||||
Use if-else when you want your program to choose between two possible actions.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int age = 18;
|
||||
|
||||
if (age >= 18) {
|
||||
std::cout << "You can vote!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Too young to vote." << std::endl;
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int age = 18;
|
||||
|
||||
if (age >= 18) {
|
||||
std::cout << "You can vote!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Too young to vote." << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Explanation:
|
||||
The program checks the condition inside if.
|
||||
If it’s true, the first block runs.
|
||||
If it’s false, the else block runs.
|
||||
|
||||
Explanation: The program checks the condition inside if. If it’s true, the first block runs. If it’s false, the else block runs.
|
||||
|
||||
### switch
|
||||
|
||||
Use switch when you have multiple possible outcomes for one variable.
|
||||
It’s often cleaner than using many if-else statements.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int day = 3;
|
||||
|
||||
switch (day) {
|
||||
case 1: std::cout << "Monday"; break;
|
||||
case 2: std::cout << "Tuesday"; break;
|
||||
case 3: std::cout << "Wednesday"; break;
|
||||
default: std::cout << "Invalid day";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Use switch when you have multiple possible outcomes for one variable. It’s often cleaner than using many if-else statements.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int day = 3;
|
||||
|
||||
switch (day) {
|
||||
case 1: std::cout << "Monday"; break;
|
||||
case 2: std::cout << "Tuesday"; break;
|
||||
case 3: std::cout << "Wednesday"; break;
|
||||
default: std::cout << "Invalid day";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Explanation:
|
||||
|
||||
- The value of "day" is compared with each "case".
|
||||
- When a match is found, that block runs.
|
||||
- "break" stops further checking.
|
||||
- "default" runs if no case matches.
|
||||
|
||||
* The value of "day" is compared with each "case".
|
||||
* When a match is found, that block runs.
|
||||
* "break" stops further checking.
|
||||
* "default" runs if no case matches.
|
||||
|
||||
### goto
|
||||
|
||||
The goto statement allows you to jump directly to another part of your code.
|
||||
Although it works, it is not recommended because it makes programs harder to understand and maintain.
|
||||
The goto statement allows you to jump directly to another part of your code. Although it works, it is not recommended because it makes programs harder to understand and maintain.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int x = 5;
|
||||
|
||||
if (x == 5)
|
||||
goto label;
|
||||
|
||||
std::cout << "This line will be skipped." << std::endl;
|
||||
|
||||
label:
|
||||
std::cout << "Jumped to label!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
int x = 5;
|
||||
|
||||
if (x == 5)
|
||||
goto label;
|
||||
|
||||
std::cout << "This line will be skipped." << std::endl;
|
||||
|
||||
label:
|
||||
std::cout << "Jumped to label!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Explaination:
|
||||
- The keyword goto tells the program to jump to a specific place marked by a label (for example, label:).
|
||||
- When the program reaches goto label;, it skips everything in between and continues execution from the label.
|
||||
|
||||
---
|
||||
* The keyword goto tells the program to jump to a specific place marked by a label (for example, label:).
|
||||
* When the program reaches goto label;, it skips everything in between and continues execution from the label.
|
||||
|
||||
* * *
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [The 'if-else' Statement in C++](https://www.youtube.com/watch?v=9-BjXs1vMSc)
|
||||
- [Learn C++ With Me - Switch Statement](https://www.youtube.com/watch?v=uOlLs1OYSSI)
|
||||
- [Why is it illegal to use "goto"?](https://youtu.be/AKJhThyTmQw?si=gjEqAsDZVMDGVAT2)
|
||||
- [@article@The 'if-else' Statement in C++](https://www.youtube.com/watch?v=9-BjXs1vMSc)
|
||||
- [@article@Learn C++ With Me - Switch Statement](https://www.youtube.com/watch?v=uOlLs1OYSSI)
|
||||
- [@article@Why is it illegal to use "goto"?](https://youtu.be/AKJhThyTmQw?si=gjEqAsDZVMDGVAT2)
|
||||
@@ -1,49 +1,52 @@
|
||||
# Installing C++
|
||||
|
||||
Before you can start programming in C++, you will need to have a compiler installed on your system. A compiler is a program that converts the C++ code you write into an executable file that your computer can run. There are several popular C++ compilers to choose from, depending on your operating system and preference.
|
||||
|
||||
### Windows
|
||||
|
||||
For Windows, one popular option is to install the [Microsoft Visual Studio IDE](https://visualstudio.microsoft.com/vs/), which includes the Microsoft Visual C++ compiler (MSVC).
|
||||
|
||||
Alternatively, you can also install the [MinGW-w64](https://mingw-w64.org/) compiler system, which is a Windows port of the GNU Compiler Collection (GCC). To install MinGW-w64, follow these steps:
|
||||
|
||||
- Download the installer from [here](https://sourceforge.net/projects/mingw-w64/files/).
|
||||
- Run the installer and select your desired architecture, version, and install location.
|
||||
- Add the `bin` folder inside the installation directory to your system's `PATH` environment variable.
|
||||
* Download the installer from [here](https://sourceforge.net/projects/mingw-w64/files/).
|
||||
* Run the installer and select your desired architecture, version, and install location.
|
||||
* Add the `bin` folder inside the installation directory to your system's `PATH` environment variable.
|
||||
|
||||
### macOS
|
||||
|
||||
For macOS, you can install the Apple LLVM `clang` compiler which is part of the Xcode Command Line Tools. To do this, open a terminal and enter:
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
xcode-select --install
|
||||
|
||||
|
||||
This will prompt a dialog to install the Command Line Tools, which includes the `clang` compiler.
|
||||
|
||||
### Linux
|
||||
|
||||
On Linux, you can install the GNU Compiler Collection (GCC) through your distribution's package manager. Here are some examples for popular Linux distributions:
|
||||
|
||||
- Ubuntu, Debian, and derivatives:
|
||||
```
|
||||
sudo apt-get install g++ build-essential
|
||||
```
|
||||
* Ubuntu, Debian, and derivatives:
|
||||
|
||||
- Fedora, CentOS, RHEL, and derivatives:
|
||||
```
|
||||
sudo dnf install gcc-c++ make
|
||||
```
|
||||
sudo apt-get install g++ build-essential
|
||||
|
||||
|
||||
- Arch Linux and derivatives:
|
||||
```
|
||||
sudo pacman -S gcc make
|
||||
```
|
||||
* Fedora, CentOS, RHEL, and derivatives:
|
||||
|
||||
sudo dnf install gcc-c++ make
|
||||
|
||||
|
||||
* Arch Linux and derivatives:
|
||||
|
||||
sudo pacman -S gcc make
|
||||
|
||||
|
||||
### Checking the Installation
|
||||
|
||||
To confirm that the compiler is installed and available on your system, open a terminal/command prompt, and enter the following command:
|
||||
|
||||
```
|
||||
g++ --version
|
||||
```
|
||||
g++ --version
|
||||
|
||||
|
||||
You should see output displaying the version of your installed C++ compiler.
|
||||
|
||||
Now you're ready to start writing and compiling your C++ code!
|
||||
Now you're ready to start writing and compiling your C++ code!
|
||||
@@ -2,137 +2,138 @@
|
||||
|
||||
C++ is a general-purpose, high-performance programming language. It was developed by Bjarne Stroustrup at Bell Labs starting in 1979. C++ is an extension of the C programming language, adding features such as classes, objects, and exceptions.
|
||||
|
||||
## Basics of C++ Programming
|
||||
Basics of C++ Programming
|
||||
-------------------------
|
||||
|
||||
Here are some basic components and concepts in C++ programming:
|
||||
|
||||
## Including Libraries
|
||||
Including Libraries
|
||||
-------------------
|
||||
|
||||
In C++, we use the `#include` directive to include libraries or header files into our program. For example, to include the standard input/output library, we write:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
|
||||
## Main Function
|
||||
Main Function
|
||||
-------------
|
||||
|
||||
The entry point of a C++ program is the `main` function. Every C++ program must have a `main` function:
|
||||
|
||||
```cpp
|
||||
int main() {
|
||||
// Your code goes here
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
int main() {
|
||||
// Your code goes here
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
## Input/Output
|
||||
Input/Output
|
||||
------------
|
||||
|
||||
To perform input and output operations in C++, we can use the built-in objects `std::cin` for input and `std::cout` for output, available in the `iostream` library. Here's an example of reading an integer and printing its value:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int number;
|
||||
std::cout << "Enter an integer: ";
|
||||
std::cin >> number;
|
||||
std::cout << "You entered: " << number << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
int number;
|
||||
std::cout << "Enter an integer: ";
|
||||
std::cin >> number;
|
||||
std::cout << "You entered: " << number << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Variables and Data Types
|
||||
Variables and Data Types
|
||||
------------------------
|
||||
|
||||
C++ has several basic data types for representing integer, floating-point, and character values:
|
||||
|
||||
- `int`: integer values
|
||||
- `float`: single-precision floating-point values
|
||||
- `double`: double-precision floating-point values
|
||||
- `char`: single characters
|
||||
- `bool`: boolean values
|
||||
* `int`: integer values
|
||||
* `float`: single-precision floating-point values
|
||||
* `double`: double-precision floating-point values
|
||||
* `char`: single characters
|
||||
* `bool`: boolean values
|
||||
|
||||
Variables must be declared with a data type before they can be used:
|
||||
|
||||
```cpp
|
||||
int x;
|
||||
float y;
|
||||
double z;
|
||||
char c;
|
||||
bool b;
|
||||
```
|
||||
int x;
|
||||
float y;
|
||||
double z;
|
||||
char c;
|
||||
bool b;
|
||||
|
||||
|
||||
## Control Structures
|
||||
Control Structures
|
||||
------------------
|
||||
|
||||
C++ provides control structures for conditional execution and iteration, such as `if`, `else`, `while`, `for`, and `switch` statements.
|
||||
|
||||
### If-Else Statement
|
||||
```cpp
|
||||
if (condition) {
|
||||
// Code to execute if the condition is true
|
||||
} else {
|
||||
// Code to execute if the condition is false
|
||||
}
|
||||
```
|
||||
|
||||
if (condition) {
|
||||
// Code to execute if the condition is true
|
||||
} else {
|
||||
// Code to execute if the condition is false
|
||||
}
|
||||
|
||||
|
||||
### While Loop
|
||||
```cpp
|
||||
while (condition) {
|
||||
// Code to execute while the condition is true
|
||||
}
|
||||
```
|
||||
|
||||
while (condition) {
|
||||
// Code to execute while the condition is true
|
||||
}
|
||||
|
||||
|
||||
### For Loop
|
||||
```cpp
|
||||
for (initialization; condition; update) {
|
||||
// Code to execute while the condition is true
|
||||
}
|
||||
```
|
||||
|
||||
for (initialization; condition; update) {
|
||||
// Code to execute while the condition is true
|
||||
}
|
||||
|
||||
|
||||
### Switch Statement
|
||||
```cpp
|
||||
switch (variable) {
|
||||
case value1:
|
||||
// Code to execute if variable == value1
|
||||
break;
|
||||
case value2:
|
||||
// Code to execute if variable == value2
|
||||
break;
|
||||
// More cases...
|
||||
default:
|
||||
// Code to execute if variable does not match any case value
|
||||
}
|
||||
```
|
||||
|
||||
## Functions
|
||||
switch (variable) {
|
||||
case value1:
|
||||
// Code to execute if variable == value1
|
||||
break;
|
||||
case value2:
|
||||
// Code to execute if variable == value2
|
||||
break;
|
||||
// More cases...
|
||||
default:
|
||||
// Code to execute if variable does not match any case value
|
||||
}
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
Functions are reusable blocks of code that can be called with arguments to perform a specific task. Functions are defined with a return type, a name, a parameter list, and a body.
|
||||
|
||||
```cpp
|
||||
ReturnType functionName(ParameterType1 parameter1, ParameterType2 parameter2) {
|
||||
// Function body
|
||||
// ...
|
||||
return returnValue;
|
||||
}
|
||||
```
|
||||
ReturnType functionName(ParameterType1 parameter1, ParameterType2 parameter2) {
|
||||
// Function body
|
||||
// ...
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
For example, here's a function that adds two integers and returns the result:
|
||||
|
||||
```cpp
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int result = add(3, 4);
|
||||
std::cout << "3 + 4 = " << result << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int result = add(3, 4);
|
||||
std::cout << "3 + 4 = " << result << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
This basic introduction to C++ should provide you with a good foundation for further learning. Explore more topics such as classes, objects, inheritance, polymorphism, templates, and the Standard Template Library (STL) to deepen your understanding of C++ and start writing more advanced programs.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@LearnC++](https://www.learncpp.com/)
|
||||
- [@video@C++ Full Course by freeCodeCamp](https://youtu.be/vLnPwxZdW4Y)
|
||||
- [@video@C++ C++ Programming Course - Beginner to Advanced](https://www.youtube.com/watch?v=8jLOx1hD3_o).
|
||||
- [@video@C++ C++ Programming Course - Beginner to Advanced](https://www.youtube.com/watch?v=8jLOx1hD3_o)
|
||||
@@ -4,45 +4,41 @@
|
||||
|
||||
`iostream` includes the following classes:
|
||||
|
||||
- `istream`: for input operations from an input source.
|
||||
- `ostream`: for output operations to an output target.
|
||||
- `iostream`: a combination of `istream` and `ostream` for both input and output operations.
|
||||
* `istream`: for input operations from an input source.
|
||||
* `ostream`: for output operations to an output target.
|
||||
* `iostream`: a combination of `istream` and `ostream` for both input and output operations.
|
||||
|
||||
These classes inherit from base classes `ios` and `ios_base`.
|
||||
|
||||
Additionally, `iostream` defines several objects that are instances of these classes and represent the standard input and output streams:
|
||||
|
||||
- `cin`: an `istream` object to read from the standard input, typically corresponding to the keyboard.
|
||||
- `cout`: an `ostream` object to write to the standard output, typically the console.
|
||||
- `cerr`: an `ostream` object to write to the standard error output, typically used for displaying error messages.
|
||||
- `clog`: an `ostream` object, similar to `cerr`, but its output can be buffered.
|
||||
* `cin`: an `istream` object to read from the standard input, typically corresponding to the keyboard.
|
||||
* `cout`: an `ostream` object to write to the standard output, typically the console.
|
||||
* `cerr`: an `ostream` object to write to the standard error output, typically used for displaying error messages.
|
||||
* `clog`: an `ostream` object, similar to `cerr`, but its output can be buffered.
|
||||
|
||||
Here are some code examples on using `iostream` for input and output operations:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int a;
|
||||
std::cout << "Enter a number: ";
|
||||
std::cin >> a;
|
||||
std::cout << "You entered: " << a << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
int a;
|
||||
std::cout << "Enter a number: ";
|
||||
std::cin >> a;
|
||||
std::cout << "You entered: " << a << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cerr << "An error occurred.\n";
|
||||
std::clog << "Logging information.\n";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cerr << "An error occurred.\n";
|
||||
std::clog << "Logging information.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Remember to include the `iostream` header when using these features:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
```
|
||||
#include <iostream>
|
||||
@@ -4,91 +4,84 @@ Iterators are objects in the C++ Standard Library (`STL`) that help us traverse
|
||||
|
||||
There are different types of iterators which you would encounter depending on their use cases:
|
||||
|
||||
- **Input Iterator**: Used to read elements in a container only once, in a forward direction. They cannot modify elements.
|
||||
* **Input Iterator**: Used to read elements in a container only once, in a forward direction. They cannot modify elements.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
std::istream_iterator<int> input(std::cin);
|
||||
std::copy(input, std::istream_iterator<int>(), std::back_inserter(nums));
|
||||
```
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
std::istream_iterator<int> input(std::cin);
|
||||
std::copy(input, std::istream_iterator<int>(), std::back_inserter(nums));
|
||||
|
||||
|
||||
- **Output Iterator**: Used to write elements in a container only once, in a forward direction. They cannot re-write elements.
|
||||
* **Output Iterator**: Used to write elements in a container only once, in a forward direction. They cannot re-write elements.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
std::ostream_iterator<int> output(std::cout, ", ");
|
||||
std::copy(nums.begin(), nums.end(), output);
|
||||
```
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
std::ostream_iterator<int> output(std::cout, ", ");
|
||||
std::copy(nums.begin(), nums.end(), output);
|
||||
|
||||
|
||||
- **Forward Iterator**: Similar to input iterators but can be used for multiple passes over the elements in a container. They cannot move backward.
|
||||
* **Forward Iterator**: Similar to input iterators but can be used for multiple passes over the elements in a container. They cannot move backward.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::forward_list<int> nums = {1, 2, 3, 4};
|
||||
std::forward_list<int>::iterator itr = nums.begin();
|
||||
while (itr != nums.end()) {
|
||||
std::cout << *itr << " ";
|
||||
++itr;
|
||||
}
|
||||
```
|
||||
std::forward_list<int> nums = {1, 2, 3, 4};
|
||||
std::forward_list<int>::iterator itr = nums.begin();
|
||||
while (itr != nums.end()) {
|
||||
std::cout << *itr << " ";
|
||||
++itr;
|
||||
}
|
||||
|
||||
|
||||
**Reverse Iterator**: Similar to input iterators but can be used for multiple passes over the elements in a container. They cannot move forward.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::list<int> nums = {1, 2, 3, 4};
|
||||
std::list<int>::reverse_iterator itr = nums.rbegin();
|
||||
while (itr != nums.rend()) {
|
||||
std::cout << *itr << " ";
|
||||
++itr;
|
||||
}
|
||||
```
|
||||
std::list<int> nums = {1, 2, 3, 4};
|
||||
std::list<int>::reverse_iterator itr = nums.rbegin();
|
||||
while (itr != nums.rend()) {
|
||||
std::cout << *itr << " ";
|
||||
++itr;
|
||||
}
|
||||
|
||||
|
||||
- **Bidirectional Iterator**: These iterators offer the ability to move both forward and backward in a container. List and set containers have bi-directional iterators.
|
||||
* **Bidirectional Iterator**: These iterators offer the ability to move both forward and backward in a container. List and set containers have bi-directional iterators.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::list<int> nums = {1, 2, 3, 4};
|
||||
std::list<int>::iterator itr;
|
||||
for (itr = nums.begin(); itr != nums.end(); ++itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
for (--itr; itr != nums.begin(); --itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
```
|
||||
std::list<int> nums = {1, 2, 3, 4};
|
||||
std::list<int>::iterator itr;
|
||||
for (itr = nums.begin(); itr != nums.end(); ++itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
for (--itr; itr != nums.begin(); --itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
|
||||
|
||||
- **Random Access Iterator**: These iterators provide the most flexible ways to access elements in a container. They can move forwards, backwards, jump directly to other elements, and access elements at a given index.
|
||||
* **Random Access Iterator**: These iterators provide the most flexible ways to access elements in a container. They can move forwards, backwards, jump directly to other elements, and access elements at a given index.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
std::vector<int>::iterator itr;
|
||||
for (itr = nums.begin(); itr != nums.end(); ++itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
for (itr -= 1; itr != nums.begin() - 1; --itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
```
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
std::vector<int>::iterator itr;
|
||||
for (itr = nums.begin(); itr != nums.end(); ++itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
for (itr -= 1; itr != nums.begin() - 1; --itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
|
||||
|
||||
For most cases, you would want to start with the `auto` keyword and the appropriate container methods (like `begin()` and `end()`) to work with iterators.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
for (auto itr = nums.begin(); itr != nums.end(); ++itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
```
|
||||
std::vector<int> nums = {1, 2, 3, 4};
|
||||
for (auto itr = nums.begin(); itr != nums.end(); ++itr) {
|
||||
std::cout << *itr << " ";
|
||||
}
|
||||
|
||||
|
||||
When working with algorithms, remember that the C++ Standard Library provides various algorithms that already utilize iterators for tasks like searching, sorting, and manipulating elements.
|
||||
When working with algorithms, remember that the C++ Standard Library provides various algorithms that already utilize iterators for tasks like searching, sorting, and manipulating elements.
|
||||
@@ -2,67 +2,64 @@
|
||||
|
||||
A lambda function, or simply "lambda", is an anonymous (unnamed) function that is defined in place, within your source code, and with a concise syntax. Lambda functions were introduced in C++11 and have since become a widely used feature, especially in combination with the Standard Library algorithms.
|
||||
|
||||
## Syntax
|
||||
Syntax
|
||||
------
|
||||
|
||||
Here is a basic syntax of a lambda function in C++:
|
||||
|
||||
```cpp
|
||||
[capture-list](parameters) -> return_type {
|
||||
// function body
|
||||
};
|
||||
```
|
||||
[capture-list](parameters) -> return_type {
|
||||
// function body
|
||||
};
|
||||
|
||||
|
||||
- **capture-list**: A list of variables from the surrounding scope that the lambda function can access.
|
||||
- **parameters**: The list of input parameters, just like in a regular function. Optional.
|
||||
- **return\_type**: The type of the value that the lambda function will return. This part is optional, and the compiler can deduce it in many cases.
|
||||
- **function body**: The code that defines the operation of the lambda function.
|
||||
* **capture-list**: A list of variables from the surrounding scope that the lambda function can access.
|
||||
* **parameters**: The list of input parameters, just like in a regular function. Optional.
|
||||
* **return\_type**: The type of the value that the lambda function will return. This part is optional, and the compiler can deduce it in many cases.
|
||||
* **function body**: The code that defines the operation of the lambda function.
|
||||
|
||||
## Usage Examples
|
||||
Usage Examples
|
||||
--------------
|
||||
|
||||
Here are a few examples to demonstrate the use of lambda functions in C++:
|
||||
|
||||
### Lambda function with no capture, parameters, or return type.
|
||||
|
||||
```cpp
|
||||
auto printHello = []() {
|
||||
std::cout << "Hello, World!\n";
|
||||
};
|
||||
printHello(); // Output: Hello, World!
|
||||
```
|
||||
auto printHello = []() {
|
||||
std::cout << "Hello, World!\n";
|
||||
};
|
||||
printHello(); // Output: Hello, World!
|
||||
|
||||
|
||||
### Lambda function with parameters.
|
||||
|
||||
```cpp
|
||||
auto add = [](int a, int b) {
|
||||
return a + b;
|
||||
};
|
||||
int result = add(3, 4); // result = 7
|
||||
```
|
||||
auto add = [](int a, int b) {
|
||||
return a + b;
|
||||
};
|
||||
int result = add(3, 4); // result = 7
|
||||
|
||||
|
||||
### Lambda function with capture-by-value.
|
||||
|
||||
```cpp
|
||||
int multiplier = 3;
|
||||
auto times = [multiplier](int a) {
|
||||
return a * multiplier;
|
||||
};
|
||||
int result = times(5); // result = 15
|
||||
```
|
||||
int multiplier = 3;
|
||||
auto times = [multiplier](int a) {
|
||||
return a * multiplier;
|
||||
};
|
||||
int result = times(5); // result = 15
|
||||
|
||||
|
||||
### Lambda function with capture-by-reference.
|
||||
|
||||
```cpp
|
||||
int expiresInDays = 45;
|
||||
auto updateDays = [&expiresInDays](int newDays) {
|
||||
expiresInDays = newDays;
|
||||
};
|
||||
updateDays(30); // expiresInDays = 30
|
||||
```
|
||||
int expiresInDays = 45;
|
||||
auto updateDays = [&expiresInDays](int newDays) {
|
||||
expiresInDays = newDays;
|
||||
};
|
||||
updateDays(30); // expiresInDays = 30
|
||||
|
||||
|
||||
Note that, when using the capture by reference, any change made to the captured variable _inside_ the lambda function will affect its value in the surrounding scope.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@video@Lambdas in C++](https://youtu.be/MH8mLFqj-n8)
|
||||
- [@article@Lambda Expressions](https://en.cppreference.com/w/cpp/language/lambda)
|
||||
- [@feed@Explore top posts about AWS Lambda](https://app.daily.dev/tags/aws-lambda?ref=roadmapsh)
|
||||
- [@video@Lambdas in C++](https://youtu.be/MH8mLFqj-n8)
|
||||
- [@feed@Explore top posts about AWS Lambda](https://app.daily.dev/tags/aws-lambda?ref=roadmapsh)
|
||||
@@ -2,130 +2,146 @@
|
||||
|
||||
C++ is a powerful, high-level, object-oriented programming language that offers several key language concepts. These concepts provide the foundation upon which you can build efficient, reliable, and maintainable programs. Here's a brief summary of some important language concepts in C++.
|
||||
|
||||
## Variables and Data Types
|
||||
Variables and Data Types
|
||||
------------------------
|
||||
|
||||
C++ provides various fundamental data types such as `int`, `float`, `double`, `char`, and `bool` to declare and manipulate variables in a program.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int age = 25;
|
||||
float height = 1.7f;
|
||||
double salary = 50000.0;
|
||||
char grade = 'A';
|
||||
bool isEmployed = true;
|
||||
```
|
||||
|
||||
## Control Structures
|
||||
int age = 25;
|
||||
float height = 1.7f;
|
||||
double salary = 50000.0;
|
||||
char grade = 'A';
|
||||
bool isEmployed = true;
|
||||
|
||||
|
||||
Control Structures
|
||||
------------------
|
||||
|
||||
Control structures enable you to control the flow of execution of a program. Key control structures in C++ include:
|
||||
|
||||
- Conditional statement: `if`, `else`, and `else if`
|
||||
- Loop constructs: `for`, `while`, and `do-while`
|
||||
- Switch-case construct
|
||||
* Conditional statement: `if`, `else`, and `else if`
|
||||
* Loop constructs: `for`, `while`, and `do-while`
|
||||
* Switch-case construct
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
// If-else statement
|
||||
if (age > 18) {
|
||||
std::cout << "You are eligible to vote.";
|
||||
} else {
|
||||
std::cout << "You are not eligible to vote.";
|
||||
}
|
||||
|
||||
// For loop
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::cout << "Hello World!";
|
||||
}
|
||||
```
|
||||
// If-else statement
|
||||
if (age > 18) {
|
||||
std::cout << "You are eligible to vote.";
|
||||
} else {
|
||||
std::cout << "You are not eligible to vote.";
|
||||
}
|
||||
|
||||
// For loop
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::cout << "Hello World!";
|
||||
}
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
## Functions
|
||||
Functions in C++ allow you to break down a large program into small, manageable, and reusable pieces of code.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int sum = add(10, 20);
|
||||
std::cout << "The sum is: " << sum;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int sum = add(10, 20);
|
||||
std::cout << "The sum is: " << sum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Arrays and Vectors
|
||||
------------------
|
||||
|
||||
## Arrays and Vectors
|
||||
Arrays and Vectors are commonly used data structures to store and manipulate a collection of elements of the same datatype.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
// Array
|
||||
int marks[] = {90, 80, 95, 85};
|
||||
|
||||
// Vector
|
||||
std::vector<int> scores = {10, 20, 30, 40};
|
||||
```
|
||||
// Array
|
||||
int marks[] = {90, 80, 95, 85};
|
||||
|
||||
// Vector
|
||||
std::vector<int> scores = {10, 20, 30, 40};
|
||||
|
||||
|
||||
Pointers
|
||||
--------
|
||||
|
||||
## Pointers
|
||||
Pointers are variables that store memory addresses of other variables. They enable more efficient handling of memory, and are useful for working with dynamic data structures.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
int num = 10;
|
||||
int* p = # // p stores the address of num
|
||||
```
|
||||
|
||||
## Structures and Classes
|
||||
int num = 10;
|
||||
int* p = # // p stores the address of num
|
||||
|
||||
|
||||
Structures and Classes
|
||||
----------------------
|
||||
|
||||
Structures and Classes are user-defined data types that allow grouping of variables and functions under a single name.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
// Structure
|
||||
struct Student {
|
||||
std::string name;
|
||||
int age;
|
||||
};
|
||||
|
||||
// Class
|
||||
class Employee {
|
||||
public:
|
||||
std::string name;
|
||||
int age;
|
||||
void displayInfo() {
|
||||
std::cout << "Name: " << name << "\nAge: " << age;
|
||||
}
|
||||
};
|
||||
```
|
||||
// Structure
|
||||
struct Student {
|
||||
std::string name;
|
||||
int age;
|
||||
};
|
||||
|
||||
// Class
|
||||
class Employee {
|
||||
public:
|
||||
std::string name;
|
||||
int age;
|
||||
void displayInfo() {
|
||||
std::cout << "Name: " << name << "\nAge: " << age;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Inheritance and Polymorphism
|
||||
----------------------------
|
||||
|
||||
## Inheritance and Polymorphism
|
||||
Inheritance is a mechanism that allows a class to inherit properties and methods from a base class. Polymorphism enables you to use a base class type to represent derived class objects.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
class Base {
|
||||
public:
|
||||
void display() {
|
||||
std::cout << "This is the base class.";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
public:
|
||||
void display() {
|
||||
std::cout << "This is the derived class.";
|
||||
}
|
||||
};
|
||||
```
|
||||
class Base {
|
||||
public:
|
||||
void display() {
|
||||
std::cout << "This is the base class.";
|
||||
}
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
public:
|
||||
void display() {
|
||||
std::cout << "This is the derived class.";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Exception Handling
|
||||
------------------
|
||||
|
||||
## Exception Handling
|
||||
C++ provides a mechanism to handle exceptions(runtime errors) gracefully using `try`, `catch`, and `throw` constructs.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
try {
|
||||
// Code that might throw an exception
|
||||
int result = a / b;
|
||||
} catch (const exception &e) {
|
||||
std::cout << "Caught an exception: " << e.what();
|
||||
}
|
||||
```
|
||||
|
||||
These are some of the key language concepts in C++, which will help you to understand the language better and develop efficient and maintainable applications.
|
||||
try {
|
||||
// Code that might throw an exception
|
||||
int result = a / b;
|
||||
} catch (const exception &e) {
|
||||
std::cout << "Caught an exception: " << e.what();
|
||||
}
|
||||
|
||||
|
||||
These are some of the key language concepts in C++, which will help you to understand the language better and develop efficient and maintainable applications.
|
||||
@@ -2,36 +2,36 @@
|
||||
|
||||
In C++ programming, inclusion refers to incorporating external libraries, header files, or other code files into your program. This process allows developers to access pre-built functions, classes, and variable declarations that can be used in their own code. There are two types of inclusion in C++:
|
||||
|
||||
- Header Inclusion
|
||||
- Source Inclusion
|
||||
* Header Inclusion
|
||||
* Source Inclusion
|
||||
|
||||
### Header Inclusion
|
||||
|
||||
Header inclusion involves including header files using the preprocessor directive `#include`. Header files are typically used to provide function prototypes, class declarations, and constant definitions that can be shared across multiple source files. There are two ways to include header files in your program:
|
||||
|
||||
- Angle brackets `<>`: Used for including standard library headers, like `iostream`, `vector`, or `algorithm`.
|
||||
* Angle brackets `<>`: Used for including standard library headers, like `iostream`, `vector`, or `algorithm`.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
```
|
||||
|
||||
- Double quotes `""`: Used for including user-defined headers or headers provided by third-party libraries.
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
* Double quotes `""`: Used for including user-defined headers or headers provided by third-party libraries.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
#include "myHeader.h"
|
||||
#include "thirdPartyLibrary.h"
|
||||
```
|
||||
|
||||
#include "myHeader.h"
|
||||
#include "thirdPartyLibrary.h"
|
||||
|
||||
|
||||
### Source Inclusion
|
||||
|
||||
Source inclusion refers to including the content of a source file directly in another source file. This approach is generally not recommended as it can lead to multiple definitions and increased compile times but it can occasionally be useful for certain tasks (e.g., templates or simple small programs). To include a source file, you can use the `#include` directive with double quotes, just like with header files:
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
#include "mySourceFile.cpp"
|
||||
```
|
||||
|
||||
#include "mySourceFile.cpp"
|
||||
|
||||
|
||||
Remember, using source inclusion for large projects or in situations where it's not necessary can lead to unexpected issues and should be avoided.
|
||||
@@ -2,38 +2,40 @@
|
||||
|
||||
Licensing is a crucial aspect of working with libraries in C++ because it determines the rights and limitations on how you can use, modify, and distribute a given library. There are various types of licenses applied to open-source libraries. Below is a brief overview of three common licenses:
|
||||
|
||||
## MIT License
|
||||
MIT License
|
||||
-----------
|
||||
|
||||
The MIT License is a permissive license that allows users to do whatever they want with the software code. They only need to include the original copyright, license notice, and a disclaimer of warranty in their copies.
|
||||
|
||||
Example: Including the MIT License into your project can be done by simply adding the license file and a notice at the top of your source code files like:
|
||||
|
||||
```cpp
|
||||
/* Copyright (C) [year] [author]
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
```
|
||||
/* Copyright (C) [year] [author]
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
GNU General Public License (GPL)
|
||||
--------------------------------
|
||||
|
||||
## GNU General Public License (GPL)
|
||||
The GPL is a copyleft license that grants users the rights to use, study, share, and modify the software code. However, any changes made to the code or any software that uses GPL licensed code must also be distributed under the GPL license.
|
||||
|
||||
Example: To include a GPL license in your project, include a `COPYING` file with the full text of the license and place a notice in your source code files like:
|
||||
|
||||
```cpp
|
||||
/* Copyright (C) [year] [author]
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
```
|
||||
/* Copyright (C) [year] [author]
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
Apache License 2.0
|
||||
------------------
|
||||
|
||||
## Apache License 2.0
|
||||
The Apache License is a permissive license similar to the MIT license and allows users to do virtually anything with the software code. The primary difference is that it requires that any changes to the code are documented, and it provides specific terms for patent protection.
|
||||
|
||||
Example: To include the Apache License in your project, add a `LICENSE` file with the full text of the license. Add a notice to your source code files like:
|
||||
|
||||
```cpp
|
||||
/* Copyright (C) [year] [author]
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
```
|
||||
/* Copyright (C) [year] [author]
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
Please note that these are brief summaries of the licenses, and there are many other licenses available for use in software projects. When using third-party libraries, it is crucial to understand and adhere to the terms of their respective licenses to avoid legal complications.
|
||||
@@ -2,38 +2,35 @@
|
||||
|
||||
Object lifetime refers to the time during which an object exists, from the moment it is created until it is destroyed. In C++, an object's lifetime can be classified into four categories:
|
||||
|
||||
- **Static Storage Duration**: Objects with static storage duration exist for the entire run of the program. These objects are allocated at the beginning of the program's run and deallocated when the program terminates. Global variables, static data members, and static local variables fall into this category.
|
||||
|
||||
```cpp
|
||||
int global_var; // Static storage duration
|
||||
class MyClass {
|
||||
static int static_var; // Static storage duration
|
||||
};
|
||||
void myFunction() {
|
||||
static int local_var; // Static storage duration
|
||||
}
|
||||
```
|
||||
|
||||
- **Thread Storage Duration**: Objects with thread storage duration exist for the lifetime of the thread they belong to. They are created when a thread starts and destroyed when the thread exits. Thread storage duration can be specified using the `thread_local` keyword.
|
||||
|
||||
```cpp
|
||||
thread_local int my_var; // Thread storage duration
|
||||
```
|
||||
|
||||
- **Automatic Storage Duration**: Objects with automatic storage duration are created at the point of definition and destroyed when the scope in which they are declared is exited. These objects are also known as "local" or "stack" objects. Function parameters and local non-static variables fall into this category.
|
||||
|
||||
```cpp
|
||||
void myFunction() {
|
||||
int local_var; // Automatic storage duration
|
||||
}
|
||||
```
|
||||
|
||||
- **Dynamic Storage Duration**: Objects with dynamic storage duration are created at runtime, using memory allocation functions such as `new` or `malloc`. The lifetime of these objects must be managed manually, as they are not automatically deallocated when the scope is exited. Instead, it is the programmer's responsibility to destroy the objects using the `delete` or `free` functions when they are no longer needed, to avoid memory leaks.
|
||||
|
||||
```cpp
|
||||
int* ptr = new int; // Dynamic storage duration
|
||||
delete ptr;
|
||||
```
|
||||
* **Static Storage Duration**: Objects with static storage duration exist for the entire run of the program. These objects are allocated at the beginning of the program's run and deallocated when the program terminates. Global variables, static data members, and static local variables fall into this category.
|
||||
|
||||
int global_var; // Static storage duration
|
||||
class MyClass {
|
||||
static int static_var; // Static storage duration
|
||||
};
|
||||
void myFunction() {
|
||||
static int local_var; // Static storage duration
|
||||
}
|
||||
|
||||
|
||||
* **Thread Storage Duration**: Objects with thread storage duration exist for the lifetime of the thread they belong to. They are created when a thread starts and destroyed when the thread exits. Thread storage duration can be specified using the `thread_local` keyword.
|
||||
|
||||
thread_local int my_var; // Thread storage duration
|
||||
|
||||
|
||||
* **Automatic Storage Duration**: Objects with automatic storage duration are created at the point of definition and destroyed when the scope in which they are declared is exited. These objects are also known as "local" or "stack" objects. Function parameters and local non-static variables fall into this category.
|
||||
|
||||
void myFunction() {
|
||||
int local_var; // Automatic storage duration
|
||||
}
|
||||
|
||||
|
||||
* **Dynamic Storage Duration**: Objects with dynamic storage duration are created at runtime, using memory allocation functions such as `new` or `malloc`. The lifetime of these objects must be managed manually, as they are not automatically deallocated when the scope is exited. Instead, it is the programmer's responsibility to destroy the objects using the `delete` or `free` functions when they are no longer needed, to avoid memory leaks.
|
||||
|
||||
int* ptr = new int; // Dynamic storage duration
|
||||
delete ptr;
|
||||
|
||||
|
||||
|
||||
Understanding object lifetimes is essential for managing memory efficiently in C++ programs and avoiding common issues like memory leaks and undefined behavior.
|
||||
|
||||
|
||||
@@ -4,52 +4,53 @@ Logical operators are used to perform logical operations on the given expression
|
||||
|
||||
C++ provides the following logical operators:
|
||||
|
||||
- **AND Operator (&&)**
|
||||
The AND operator checks if both the operands/conditions are true, then the expression is true. If any one of the conditions is false, the whole expression will be false.
|
||||
```
|
||||
(expression1 && expression2)
|
||||
```
|
||||
Example:
|
||||
```cpp
|
||||
int a = 5, b = 10;
|
||||
if (a > 0 && b > 0) {
|
||||
std::cout << "Both values are positive.\n";
|
||||
}
|
||||
```
|
||||
- **OR Operator (||)**
|
||||
The OR operator checks if either of the operands/conditions are true, then the expression is true. If both the conditions are false, it will be false.
|
||||
```
|
||||
(expression1 || expression2)
|
||||
```
|
||||
Example:
|
||||
```cpp
|
||||
int a = 5, b = -10;
|
||||
if (a > 0 || b > 0) {
|
||||
std::cout << "At least one value is positive.\n";
|
||||
}
|
||||
```
|
||||
|
||||
- **NOT Operator (!)**
|
||||
The NOT operator reverses the result of the condition/expression it is applied on. If the condition is true, the NOT operator will make it false and vice versa.
|
||||
```
|
||||
!(expression)
|
||||
```
|
||||
Example:
|
||||
```cpp
|
||||
int a = 5;
|
||||
if (!(a < 0)) {
|
||||
std::cout << "The value is not negative.\n";
|
||||
}
|
||||
```
|
||||
* **AND Operator (&&)** The AND operator checks if both the operands/conditions are true, then the expression is true. If any one of the conditions is false, the whole expression will be false.
|
||||
|
||||
(expression1 && expression2)
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
int a = 5, b = 10;
|
||||
if (a > 0 && b > 0) {
|
||||
std::cout << "Both values are positive.\n";
|
||||
}
|
||||
|
||||
|
||||
* **OR Operator (||)** The OR operator checks if either of the operands/conditions are true, then the expression is true. If both the conditions are false, it will be false.
|
||||
|
||||
(expression1 || expression2)
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
int a = 5, b = -10;
|
||||
if (a > 0 || b > 0) {
|
||||
std::cout << "At least one value is positive.\n";
|
||||
}
|
||||
|
||||
|
||||
* **NOT Operator (!)** The NOT operator reverses the result of the condition/expression it is applied on. If the condition is true, the NOT operator will make it false and vice versa.
|
||||
|
||||
!(expression)
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
int a = 5;
|
||||
if (!(a < 0)) {
|
||||
std::cout << "The value is not negative.\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
Using these operators, you can create more complex logical expressions, for example:
|
||||
|
||||
```cpp
|
||||
int a = 5, b = -10, c = 15;
|
||||
int a = 5, b = -10, c = 15;
|
||||
|
||||
if (a > 0 && (b > 0 || c > 0)) {
|
||||
std::cout << "At least two values are positive.\n";
|
||||
}
|
||||
|
||||
|
||||
if (a > 0 && (b > 0 || c > 0)) {
|
||||
std::cout << "At least two values are positive.\n";
|
||||
}
|
||||
```
|
||||
|
||||
This covers the essential information about logical operators in C++.
|
||||
This covers the essential information about logical operators in C++.
|
||||
@@ -4,52 +4,50 @@ Macros are preprocessing directives in C++ used by the preprocessor to perform t
|
||||
|
||||
Macros can be used to define constants, create function-like macros, or perform conditional compilation.
|
||||
|
||||
## Constant Macros
|
||||
Constant Macros
|
||||
---------------
|
||||
|
||||
Constant macros are used to define symbolic constants for use in code. They do not use any memory and are replaced by the preprocessor before the compilation process.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#define PI 3.14159
|
||||
```
|
||||
#define PI 3.14159
|
||||
|
||||
|
||||
This macro defines a symbolic constant `PI`. You can use it in your code as if it were a regular variable.
|
||||
|
||||
```cpp
|
||||
double circumference = 2 * PI * radius;
|
||||
```
|
||||
double circumference = 2 * PI * radius;
|
||||
|
||||
|
||||
## Function-like Macros
|
||||
Function-like Macros
|
||||
--------------------
|
||||
|
||||
Function-like macros are similar to regular functions. They take a list of arguments and perform text substitution.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#define SQUARE(x) ((x) * (x))
|
||||
```
|
||||
#define SQUARE(x) ((x) * (x))
|
||||
|
||||
|
||||
This macro defines a function-like macro `SQUARE` that calculates the square of a number.
|
||||
|
||||
```cpp
|
||||
int square_of_five = SQUARE(5); // expands to ((5) * (5))
|
||||
```
|
||||
int square_of_five = SQUARE(5); // expands to ((5) * (5))
|
||||
|
||||
|
||||
## Conditional Compilation
|
||||
Conditional Compilation
|
||||
-----------------------
|
||||
|
||||
Macros can be used for conditional compilation using the `#ifdef`, `#ifndef`, `#if`, `#else`, `#elif`, and `#endif` directives.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#define DEBUG_MODE
|
||||
|
||||
#ifdef DEBUG_MODE
|
||||
// Code to be compiled only in debug mode
|
||||
#else
|
||||
// Code to be compiled only if DEBUG_MODE is not defined
|
||||
#endif
|
||||
```
|
||||
#define DEBUG_MODE
|
||||
|
||||
#ifdef DEBUG_MODE
|
||||
// Code to be compiled only in debug mode
|
||||
#else
|
||||
// Code to be compiled only if DEBUG_MODE is not defined
|
||||
#endif
|
||||
|
||||
|
||||
This example demonstrates how you can use macros to control the parts of code that are being compiled, depending on the presence or absence of a macro definition.
|
||||
@@ -4,52 +4,53 @@ A Makefile is a configuration file used by the `make` utility to automate the pr
|
||||
|
||||
Makefiles help developers save time, reduce errors, and ensure consistency in the build process. They achieve this by specifying the dependencies between different source files, and providing commands that generate output files (such as object files and executables) from input files (such as source code and headers).
|
||||
|
||||
## Structure of a Makefile
|
||||
Structure of a Makefile
|
||||
-----------------------
|
||||
|
||||
A typical Makefile has the following structure:
|
||||
|
||||
- **Variables**: Define variables to store commonly used values, such as compiler flags, directories, or target names.
|
||||
- **Rules**: Define how to generate output files from input files using a set of commands. Each rule has a *target*, a set of *prerequisites*, and a *recipe*.
|
||||
- **Phony targets**: Targets that do not represent actual files in the project but serve as a way to group related rules and invoke them using a single command.
|
||||
* **Variables**: Define variables to store commonly used values, such as compiler flags, directories, or target names.
|
||||
* **Rules**: Define how to generate output files from input files using a set of commands. Each rule has a _target_, a set of _prerequisites_, and a _recipe_.
|
||||
* **Phony targets**: Targets that do not represent actual files in the project but serve as a way to group related rules and invoke them using a single command.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
Consider a basic C++ project with the following directory structure:
|
||||
|
||||
```
|
||||
project/
|
||||
|-- include/
|
||||
| |-- header.h
|
||||
|-- src/
|
||||
| |-- main.cpp
|
||||
|-- Makefile
|
||||
```
|
||||
project/
|
||||
|-- include/
|
||||
| |-- header.h
|
||||
|-- src/
|
||||
| |-- main.cpp
|
||||
|-- Makefile
|
||||
|
||||
|
||||
A simple Makefile for this project could be as follows:
|
||||
|
||||
```make
|
||||
# Variables
|
||||
CXX = g++
|
||||
CXXFLAGS = -Wall -Iinclude
|
||||
SRC = src/main.cpp
|
||||
OBJ = main.o
|
||||
EXE = my_program
|
||||
|
||||
# Rules
|
||||
$(EXE): $(OBJ)
|
||||
$(CXX) $(CXXFLAGS) -o $(EXE) $(OBJ)
|
||||
|
||||
$(OBJ): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -c $(SRC)
|
||||
|
||||
# Phony targets
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(OBJ) $(EXE)
|
||||
```
|
||||
# Variables
|
||||
CXX = g++
|
||||
CXXFLAGS = -Wall -Iinclude
|
||||
SRC = src/main.cpp
|
||||
OBJ = main.o
|
||||
EXE = my_program
|
||||
|
||||
# Rules
|
||||
$(EXE): $(OBJ)
|
||||
$(CXX) $(CXXFLAGS) -o $(EXE) $(OBJ)
|
||||
|
||||
$(OBJ): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -c $(SRC)
|
||||
|
||||
# Phony targets
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(OBJ) $(EXE)
|
||||
|
||||
|
||||
With this Makefile, you can simply run `make` in the terminal to build the project, and `make clean` to remove the output files. The Makefile specifies the dependencies between the source code, object files, and the final executable, as well as the commands to compile and link them.
|
||||
|
||||
## Summary
|
||||
Summary
|
||||
-------
|
||||
|
||||
Makefiles provide a powerful way to automate building C++ projects using the `make` utility. They describe the dependencies and commands required to generate output files from source code, saving time and ensuring consistency in the build process.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,51 +2,51 @@
|
||||
|
||||
The memory model in C++ defines how the program stores and accesses data in computer memory. It consists of different segments, such as the Stack, Heap, Data and Code segments. Each of these segments is used to store different types of data and has specific characteristics.
|
||||
|
||||
## Stack Memory
|
||||
Stack Memory
|
||||
------------
|
||||
|
||||
Stack memory is used for automatic storage duration variables, such as local variables and function call data. Stack memory is managed by the compiler, and it's allocation and deallocation are done automatically. The stack memory is also a LIFO (Last In First Out) data structure, meaning that the most recent data allocated is the first to be deallocated.
|
||||
|
||||
```cpp
|
||||
void functionExample() {
|
||||
int x = 10; // x is stored in the stack memory
|
||||
}
|
||||
```
|
||||
void functionExample() {
|
||||
int x = 10; // x is stored in the stack memory
|
||||
}
|
||||
|
||||
|
||||
## Heap Memory
|
||||
Heap Memory
|
||||
-----------
|
||||
|
||||
Heap memory is used for dynamic storage duration variables, such as objects created using the `new` keyword. The programmer has control over the allocation and deallocation of heap memory using `new` and `delete` operators. Heap memory is a larger pool of memory than the stack, but has a slower access time.
|
||||
|
||||
```cpp
|
||||
void functionExample() {
|
||||
int* p = new int; // dynamically allocated int in heap memory
|
||||
*p = 10;
|
||||
// more code
|
||||
delete p; // deallocate memory
|
||||
}
|
||||
```
|
||||
void functionExample() {
|
||||
int* p = new int; // dynamically allocated int in heap memory
|
||||
*p = 10;
|
||||
// more code
|
||||
delete p; // deallocate memory
|
||||
}
|
||||
|
||||
|
||||
## Data Segment
|
||||
Data Segment
|
||||
------------
|
||||
|
||||
The Data segment is composed of two parts: the initialized data segment and the uninitialized data segment. The initialized data segment stores global, static, and constant variables with initial values, whereas the uninitialized segment stores uninitialized global and static variables.
|
||||
|
||||
```cpp
|
||||
// Initialized data segment
|
||||
int globalVar = 10; // global variables
|
||||
static int staticVar = 10; // static local variables
|
||||
const int constVar = 10; // constant variables with value
|
||||
// Initialized data segment
|
||||
int globalVar = 10; // global variables
|
||||
static int staticVar = 10; // static local variables
|
||||
const int constVar = 10; // constant variables with value
|
||||
|
||||
// Uninitialized data segment
|
||||
int globalVar; // uninitialized global variables
|
||||
|
||||
|
||||
// Uninitialized data segment
|
||||
int globalVar; // uninitialized global variables
|
||||
```
|
||||
|
||||
## Code Segment
|
||||
Code Segment
|
||||
------------
|
||||
|
||||
The Code segment (also known as the Text segment) stores the executable code (machine code) of the program. It's usually located in a read-only area of memory to prevent accidental modification.
|
||||
|
||||
```cpp
|
||||
void functionExample() {
|
||||
// The machine code for this function is stored in the code segment.
|
||||
}
|
||||
```
|
||||
void functionExample() {
|
||||
// The machine code for this function is stored in the code segment.
|
||||
}
|
||||
|
||||
|
||||
In summary, understanding the memory model in C++ helps to optimize the usage of memory resources and improves overall program performance.
|
||||
@@ -4,72 +4,73 @@ Multiple inheritance is a feature in C++ where a class can inherit characteristi
|
||||
|
||||
When a class inherits multiple base classes, it becomes a mixture of their properties and behaviors, and can override or extend them as needed.
|
||||
|
||||
## Syntax
|
||||
Syntax
|
||||
------
|
||||
|
||||
Here is the syntax to declare a class with multiple inheritance:
|
||||
|
||||
```cpp
|
||||
class DerivedClass : access-specifier BaseClass1, access-specifier BaseClass2, ...
|
||||
{
|
||||
// class body
|
||||
};
|
||||
```
|
||||
class DerivedClass : access-specifier BaseClass1, access-specifier BaseClass2, ...
|
||||
{
|
||||
// class body
|
||||
};
|
||||
|
||||
|
||||
The `DerivedClass` will inherit members from both `BaseClass1` and `BaseClass2`. The `access-specifier` (like `public`, `protected`, or `private`) determines the accessibility of the inherited members.
|
||||
|
||||
## Example
|
||||
Example
|
||||
-------
|
||||
|
||||
Here is an example of multiple inheritance in action:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
// Base class 1
|
||||
class Animal
|
||||
{
|
||||
public:
|
||||
void eat()
|
||||
{
|
||||
std::cout << "I can eat!\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Base class 2
|
||||
class Mammal
|
||||
{
|
||||
public:
|
||||
void breath()
|
||||
{
|
||||
std::cout << "I can breathe!\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Derived class inheriting from both Animal and Mammal
|
||||
class Dog : public Animal, public Mammal
|
||||
{
|
||||
public:
|
||||
void bark()
|
||||
{
|
||||
std::cout << "I can bark! Woof woof!\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Dog myDog;
|
||||
|
||||
// Calling members from both base classes
|
||||
myDog.eat();
|
||||
myDog.breath();
|
||||
#include <iostream>
|
||||
|
||||
// Base class 1
|
||||
class Animal
|
||||
{
|
||||
public:
|
||||
void eat()
|
||||
{
|
||||
std::cout << "I can eat!\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Base class 2
|
||||
class Mammal
|
||||
{
|
||||
public:
|
||||
void breath()
|
||||
{
|
||||
std::cout << "I can breathe!\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Derived class inheriting from both Animal and Mammal
|
||||
class Dog : public Animal, public Mammal
|
||||
{
|
||||
public:
|
||||
void bark()
|
||||
{
|
||||
std::cout << "I can bark! Woof woof!\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Dog myDog;
|
||||
|
||||
// Calling members from both base classes
|
||||
myDog.eat();
|
||||
myDog.breath();
|
||||
|
||||
// Calling a member from the derived class
|
||||
myDog.bark();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calling a member from the derived class
|
||||
myDog.bark();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Note
|
||||
Note
|
||||
----
|
||||
|
||||
In some cases, multiple inheritance can lead to complications such as ambiguity and the "diamond problem". Ensure that you use multiple inheritance judiciously and maintain well-structured and modular classes to prevent issues.
|
||||
|
||||
|
||||
@@ -4,74 +4,74 @@ Multithreading is the concurrent execution of multiple threads within a single p
|
||||
|
||||
In C++, multithreading support is available through the `thread` library introduced in the C++11 standard.
|
||||
|
||||
## Basic Thread Creation
|
||||
Basic Thread Creation
|
||||
---------------------
|
||||
|
||||
To create a new thread, include the `<thread>` header file and create an instance of `std::thread` that takes a function as an argument. The function will be executed in a new thread.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
void my_function() {
|
||||
std::cout << "This function is executing in a separate thread\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(my_function);
|
||||
t.join(); // waits for the thread to complete
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void my_function() {
|
||||
std::cout << "This function is executing in a separate thread\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(my_function);
|
||||
t.join(); // waits for the thread to complete
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Thread with Arguments
|
||||
Thread with Arguments
|
||||
---------------------
|
||||
|
||||
You can pass arguments to the thread function by providing them as additional arguments to the `std::thread` constructor.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
void print_sum(int a, int b) {
|
||||
std::cout << "The sum is: " << a + b << '\n';
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(print_sum, 3, 5);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void print_sum(int a, int b) {
|
||||
std::cout << "The sum is: " << a + b << '\n';
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(print_sum, 3, 5);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Mutex and Locks
|
||||
Mutex and Locks
|
||||
---------------
|
||||
|
||||
When multiple threads access shared resources, there is a possibility of a data race. To avoid this, use mutex and locks to synchronize shared resource access.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
std::mutex mtx;
|
||||
|
||||
void print_block(int n, char c) {
|
||||
{
|
||||
std::unique_lock<std::mutex> locker(mtx);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
std::cout << c;
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
std::mutex mtx;
|
||||
|
||||
void print_block(int n, char c) {
|
||||
{
|
||||
std::unique_lock<std::mutex> locker(mtx);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
std::cout << c;
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t1(print_block, 50, '*');
|
||||
std::thread t2(print_block, 50, '$');
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
int main() {
|
||||
std::thread t1(print_block, 50, '*');
|
||||
std::thread t2(print_block, 50, '$');
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
This short introduction should help you get started with basic multithreading techniques in C++. There is a lot more to learn, such as thread pools, condition variables, and atomic operations for advanced synchronization and performance tuning.
|
||||
@@ -6,12 +6,11 @@ In C++, the compiler generates a mangled name for each function and variable bas
|
||||
|
||||
For example, suppose you have the following function:
|
||||
|
||||
```cpp
|
||||
int add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
int add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
The compiler might generate a mangled name such as `_Z3addii`, which encodes the function name `add` and its two `int` parameters.
|
||||
|
||||
@@ -19,9 +18,8 @@ The exact rules for name mangling are implementation and platform dependent. Dif
|
||||
|
||||
Some tools, such as c++filt (included in GCC and Clang), can be used to demangle a mangled name back to the original identifier, which can be useful when debugging or working with symbol tables.
|
||||
|
||||
```sh
|
||||
$ echo "_Z3addii" | c++filt
|
||||
add(int, int)
|
||||
```
|
||||
$ echo "_Z3addii" | c++filt
|
||||
add(int, int)
|
||||
|
||||
|
||||
In general, it is not necessary for you to understand the details of name mangling when writing code in C++, as the compiler handles it automatically. However, it can affect program behavior in some cases, such as when using external libraries or linking object files from different compilers.
|
||||
@@ -2,102 +2,100 @@
|
||||
|
||||
In C++, a namespace is a named scope or container that is used to organize and enclose a collection of code elements, such as variables, functions, classes, and other namespaces. They are mainly used to divide and manage the code base, giving developers control over name collisions and the specialization of code.
|
||||
|
||||
## Syntax
|
||||
Syntax
|
||||
------
|
||||
|
||||
Here's the syntax for declaring a namespace:
|
||||
|
||||
```cpp
|
||||
namespace identifier {
|
||||
// code elements
|
||||
}
|
||||
```
|
||||
namespace identifier {
|
||||
// code elements
|
||||
}
|
||||
|
||||
|
||||
## Using Namespaces
|
||||
Using Namespaces
|
||||
----------------
|
||||
|
||||
To access elements within a namespace, you can use the scope resolution operator `::`. Here are some examples:
|
||||
|
||||
### Declaring and accessing a namespace
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
namespace animals {
|
||||
std::string dog = "Bobby";
|
||||
std::string cat = "Lilly";
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Dog's name: " << animals::dog << '\n';
|
||||
std::cout << "Cat's name: " << animals::cat << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
namespace animals {
|
||||
std::string dog = "Bobby";
|
||||
std::string cat = "Lilly";
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Dog's name: " << animals::dog << '\n';
|
||||
std::cout << "Cat's name: " << animals::cat << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
### Nesting namespaces
|
||||
|
||||
Namespaces can be nested within other namespaces:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
namespace outer {
|
||||
int x = 10;
|
||||
|
||||
namespace inner {
|
||||
int y = 20;
|
||||
#include <iostream>
|
||||
|
||||
namespace outer {
|
||||
int x = 10;
|
||||
|
||||
namespace inner {
|
||||
int y = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Outer x: " << outer::x << '\n';
|
||||
std::cout << "Inner y: " << outer::inner::y << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
std::cout << "Outer x: " << outer::x << '\n';
|
||||
std::cout << "Inner y: " << outer::inner::y << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## `using` Keyword
|
||||
`using` Keyword
|
||||
---------------
|
||||
|
||||
You can use the `using` keyword to import namespaced elements into the current scope. However, this might lead to name conflicts if multiple namespaces have elements with the same name.
|
||||
|
||||
### Using a single element from a namespace
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
namespace animals {
|
||||
std::string dog = "Bobby";
|
||||
std::string cat = "Lilly";
|
||||
}
|
||||
|
||||
int main() {
|
||||
using animals::dog;
|
||||
#include <iostream>
|
||||
|
||||
namespace animals {
|
||||
std::string dog = "Bobby";
|
||||
std::string cat = "Lilly";
|
||||
}
|
||||
|
||||
int main() {
|
||||
using animals::dog;
|
||||
|
||||
std::cout << "Dog's name: " << dog << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "Dog's name: " << dog << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Using the entire namespace
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
namespace animals {
|
||||
std::string dog = "Bobby";
|
||||
std::string cat = "Lilly";
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace animals;
|
||||
#include <iostream>
|
||||
|
||||
namespace animals {
|
||||
std::string dog = "Bobby";
|
||||
std::string cat = "Lilly";
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace animals;
|
||||
|
||||
std::cout << "Dog's name: " << dog << '\n';
|
||||
std::cout << "Cat's name: " << cat << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "Dog's name: " << dog << '\n';
|
||||
std::cout << "Cat's name: " << cat << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
In conclusion, namespaces are a useful mechanism in C++ to organize code, avoid naming conflicts, and manage the visibility of code elements.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -1,90 +1,10 @@
|
||||
# C++ Newest Standard: C++20
|
||||
# C++23
|
||||
|
||||
C++20 is the newest standard of the C++ programming language, which was officially published in December 2020. It introduces many new features, enhancements, and improvements over the previous standards. Here is a brief summary of some key features in C++20.
|
||||
C++23 is the latest version of the C++ standard, building upon previous iterations to introduce new features, library enhancements, and language improvements. It aims to simplify development, improve performance, and provide more modern tools for C++ programmers. This standard incorporates features like `std::expected`, stackable coroutines, and improvements to the standard library, making C++ more robust and expressive.
|
||||
|
||||
- **Concepts**: Concepts provide a way to specify constraints on template parameters, ensuring that they meet a specific set of requirements. This allows for better compile-time error messages and code readability.
|
||||
Visit the following resources to learn more:
|
||||
|
||||
Example:
|
||||
```
|
||||
template<typename T>
|
||||
concept Printable = requires(T x) {
|
||||
{std::cout << x};
|
||||
};
|
||||
|
||||
template<Printable T>
|
||||
void print(const T& x) {
|
||||
std::cout << x << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
- **Ranges**: Ranges build on the iterator concept and provide a more usable and composable framework for dealing with sequences of values. They simplify the way algorithms can be applied to collections of data.
|
||||
|
||||
Example:
|
||||
```
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <ranges>
|
||||
|
||||
int main() {
|
||||
std::vector<int> numbers{1, 2, 3, 4, 5};
|
||||
auto even_view = numbers | std::views::filter([](int n) { return n % 2 == 0; });
|
||||
|
||||
for (int n : even_view) {
|
||||
std::cout << n << ' ';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Coroutines**: Coroutines offer a way to split complex, long-running functions into smaller, more manageable chunks, allowing them to be suspended and resumed at specific points.
|
||||
|
||||
Example:
|
||||
```
|
||||
#include <iostream>
|
||||
#include <coroutine>
|
||||
|
||||
std::generator<int> generator() {
|
||||
for (int i = 0; i < 5; ++i)
|
||||
co_yield i;
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int value : generator())
|
||||
std::cout << value << ' ';
|
||||
}
|
||||
```
|
||||
|
||||
- **Lambdas with template parameters**: C++20 enables using `auto` as a lambda parameter, allowing for generic lambdas with templated parameters.
|
||||
|
||||
Example:
|
||||
```
|
||||
auto sum = [](auto a, auto b) {
|
||||
return a + b;
|
||||
};
|
||||
|
||||
int res1 = sum(1, 2); // int
|
||||
double res2 = sum(1.0, 2.0); // double
|
||||
```
|
||||
|
||||
- **Constexpr enhancements**: `constexpr` support is extended with additional features, such as `constexpr` dynamic allocations, `constexpr` try-catch blocks, and `constexpr` lambdas.
|
||||
|
||||
Example:
|
||||
```
|
||||
struct Point {
|
||||
constexpr Point(int x, int y): x_{x}, y_{y} {}
|
||||
int x_, y_;
|
||||
};
|
||||
|
||||
constexpr auto create_points() {
|
||||
Point points[3]{};
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
points[i] = Point{i, i * i};
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
constexpr auto points = create_points();
|
||||
```
|
||||
|
||||
There are many other features in C++20, such as new standard library improvements, `std::format`, improvements to compile-time programming, and more. These are just a few highlights that showcase the versatility and power of the newest standard of C++.
|
||||
- [@official@The Standard](https://isocpp.org/std/the-standard)
|
||||
- [@article@C++23](https://en.wikipedia.org/wiki/C%2B%2B23)
|
||||
- [@article@Overview of New Features in C++23](https://medium.com/@threehappyer/overview-of-new-features-in-c-23-68c5bc668958)
|
||||
- [@video@C++23: An Overview of Almost All New and Updated Features - Marc Gregoire - CppCon 2023](https://www.youtube.com/watch?v=Cttb8vMuq-Y)
|
||||
@@ -4,31 +4,29 @@ Ninja is a small build system with a focus on speed. It is designed to handle la
|
||||
|
||||
Ninja build files are typically named `build.ninja` and contain rules, build statements, and variable declarations. Here's a simple example of a Ninja build file for a C++ project:
|
||||
|
||||
```
|
||||
# Variable declarations
|
||||
cxx = g++
|
||||
cflags = -Wall -Wextra -std=c++17
|
||||
|
||||
# Rule for compiling the C++ files
|
||||
rule cxx_compile
|
||||
command = $cxx $cflags -c $in -o $out
|
||||
|
||||
# Build statements for the source files
|
||||
build main.o: cxx_compile main.cpp
|
||||
build foo.o: cxx_compile foo.cpp
|
||||
|
||||
# Rule for linking the object files
|
||||
rule link
|
||||
command = $cxx $in -o $out
|
||||
|
||||
# Build statement for the final executable
|
||||
build my_program: link main.o foo.o
|
||||
```
|
||||
# Variable declarations
|
||||
cxx = g++
|
||||
cflags = -Wall -Wextra -std=c++17
|
||||
|
||||
# Rule for compiling the C++ files
|
||||
rule cxx_compile
|
||||
command = $cxx $cflags -c $in -o $out
|
||||
|
||||
# Build statements for the source files
|
||||
build main.o: cxx_compile main.cpp
|
||||
build foo.o: cxx_compile foo.cpp
|
||||
|
||||
# Rule for linking the object files
|
||||
rule link
|
||||
command = $cxx $in -o $out
|
||||
|
||||
# Build statement for the final executable
|
||||
build my_program: link main.o foo.o
|
||||
|
||||
|
||||
To build the project using this `build.ninja` file, simply run `ninja` in the terminal:
|
||||
|
||||
```bash
|
||||
$ ninja
|
||||
```
|
||||
$ ninja
|
||||
|
||||
|
||||
This will build the `my_program` executable by first compiling the `main.cpp` and `foo.cpp` files into object files, and then linking them together.
|
||||
@@ -6,26 +6,24 @@ To make a class non-copyable, you need to delete the copy constructor and the co
|
||||
|
||||
Here's an example of how to apply the non-copyable idiom to a class:
|
||||
|
||||
```cpp
|
||||
class NonCopyable {
|
||||
public:
|
||||
NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
// Delete the copy constructor
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
|
||||
// Delete the copy assignment operator
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
};
|
||||
```
|
||||
class NonCopyable {
|
||||
public:
|
||||
NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
// Delete the copy constructor
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
|
||||
// Delete the copy assignment operator
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
};
|
||||
|
||||
|
||||
To use the idiom, simply inherit from the `NonCopyable` class:
|
||||
|
||||
```cpp
|
||||
class MyClass : private NonCopyable {
|
||||
// MyClass is now non-copyable
|
||||
};
|
||||
```
|
||||
class MyClass : private NonCopyable {
|
||||
// MyClass is now non-copyable
|
||||
};
|
||||
|
||||
|
||||
This ensures that any attempt to copy or assign objects of `MyClass` will result in a compilation error, thus preventing unwanted behavior.
|
||||
@@ -10,21 +10,20 @@ You can use NuGet either as a command-line tool or integrated in your preferred
|
||||
|
||||
You can use NuGet to manage your C++ dependencies using the PackageReference format in vcxproj files:
|
||||
|
||||
- Tools > NuGet Package Manager > Manage NuGet Packages for Solution…
|
||||
- Package source should be set to "nuget.org"
|
||||
- Select the Projects tab
|
||||
- Use the search box to find packages
|
||||
* Tools > NuGet Package Manager > Manage NuGet Packages for Solution…
|
||||
* Package source should be set to "[nuget.org](http://nuget.org)"
|
||||
* Select the Projects tab
|
||||
* Use the search box to find packages
|
||||
|
||||
For example, to install a package called "PackageName" for all configurations:
|
||||
|
||||
```xml
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PackageName" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
...
|
||||
</Project>
|
||||
```
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PackageName" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
...
|
||||
</Project>
|
||||
|
||||
|
||||
### NuGet Command-Line
|
||||
|
||||
@@ -32,14 +31,12 @@ You can also use the command-line tool `nuget.exe` for more advanced scenarios o
|
||||
|
||||
Here's an example of installing a package using the command line:
|
||||
|
||||
```bash
|
||||
nuget install PackageName
|
||||
```
|
||||
nuget install PackageName
|
||||
|
||||
|
||||
And updating a package:
|
||||
|
||||
```bash
|
||||
nuget update PackageName
|
||||
```
|
||||
nuget update PackageName
|
||||
|
||||
|
||||
For more information and detailed examples on using NuGet in your projects, please refer to the [official documentation](https://docs.microsoft.com/en-us/nuget/guides/native-packages).
|
||||
@@ -2,119 +2,116 @@
|
||||
|
||||
Object-oriented programming (OOP) is a programming paradigm that uses objects, which are instances of classes, to perform operations and interact with each other. In C++, you can achieve OOP through the use of classes and objects.
|
||||
|
||||
## Classes
|
||||
Classes
|
||||
-------
|
||||
|
||||
A class is a blueprint for creating objects. It defines the structure (data members) and behavior (member functions) for a type of object. Here's an example of a simple class:
|
||||
|
||||
```cpp
|
||||
class Dog {
|
||||
public:
|
||||
std::string name;
|
||||
int age;
|
||||
|
||||
void bark() {
|
||||
std::cout << name << " barks!\n";
|
||||
}
|
||||
};
|
||||
```
|
||||
class Dog {
|
||||
public:
|
||||
std::string name;
|
||||
int age;
|
||||
|
||||
void bark() {
|
||||
std::cout << name << " barks!\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
This `Dog` class has two data members: `name` and `age`, and one member function `bark`. You can create an object of this class and access its members like this:
|
||||
|
||||
```cpp
|
||||
Dog myDog;
|
||||
myDog.name = "Fido";
|
||||
myDog.age = 3;
|
||||
myDog.bark(); // Output: Fido barks!
|
||||
```
|
||||
Dog myDog;
|
||||
myDog.name = "Fido";
|
||||
myDog.age = 3;
|
||||
myDog.bark(); // Output: Fido barks!
|
||||
|
||||
|
||||
## Encapsulation
|
||||
Encapsulation
|
||||
-------------
|
||||
|
||||
Encapsulation is the concept of bundling data and functions that operate on that data within a single unit, such as a class. It helps to hide the internal implementation details of a class and expose only the necessary information and functionalities. In C++, you can use access specifiers like `public`, `private`, and `protected` to control the visibility and accessibility of class members. For example:
|
||||
|
||||
```cpp
|
||||
class Dog {
|
||||
private:
|
||||
std::string name;
|
||||
int age;
|
||||
|
||||
public:
|
||||
void setName(std::string n) {
|
||||
name = n;
|
||||
}
|
||||
|
||||
void setAge(int a) {
|
||||
age = a;
|
||||
}
|
||||
|
||||
void bark() {
|
||||
std::cout << name << " barks!\n";
|
||||
}
|
||||
};
|
||||
```
|
||||
class Dog {
|
||||
private:
|
||||
std::string name;
|
||||
int age;
|
||||
|
||||
public:
|
||||
void setName(std::string n) {
|
||||
name = n;
|
||||
}
|
||||
|
||||
void setAge(int a) {
|
||||
age = a;
|
||||
}
|
||||
|
||||
void bark() {
|
||||
std::cout << name << " barks!\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
In this example, we've made the `name` and `age` data members `private` and added public member functions `setName` and `setAge` to modify them. This way, the internal data of the `Dog` class is protected and only accessible through the provided functions.
|
||||
|
||||
## Inheritance
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
Inheritance is the concept of deriving new classes from existing ones, which enables code reusability and organization. In C++, inheritance is achieved by using a colon `:` followed by the base class' access specifier and the base class name. For example:
|
||||
|
||||
```cpp
|
||||
class Animal {
|
||||
public:
|
||||
void breathe() {
|
||||
std::cout << "I can breathe\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Dog : public Animal {
|
||||
public:
|
||||
void bark() {
|
||||
std::cout << "Dog barks!\n";
|
||||
}
|
||||
};
|
||||
```
|
||||
class Animal {
|
||||
public:
|
||||
void breathe() {
|
||||
std::cout << "I can breathe\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Dog : public Animal {
|
||||
public:
|
||||
void bark() {
|
||||
std::cout << "Dog barks!\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
In this example, the `Dog` class inherits from the `Animal` class, so the `Dog` class can access the `breathe` function from the `Animal` class. When you create a `Dog` object, you can use both `breathe` and `bark` functions.
|
||||
|
||||
```cpp
|
||||
Dog myDog;
|
||||
myDog.breathe(); // Output: I can breathe
|
||||
myDog.bark(); // Output: Dog barks!
|
||||
```
|
||||
Dog myDog;
|
||||
myDog.breathe(); // Output: I can breathe
|
||||
myDog.bark(); // Output: Dog barks!
|
||||
|
||||
|
||||
## Polymorphism
|
||||
Polymorphism
|
||||
------------
|
||||
|
||||
Polymorphism allows you to use a single interface to represent different types. In C++, it's mainly achieved using function overloading, virtual functions, and overriding. For example:
|
||||
|
||||
```cpp
|
||||
class Animal {
|
||||
public:
|
||||
virtual void makeSound() {
|
||||
std::cout << "The Animal makes a sound\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Dog : public Animal {
|
||||
public:
|
||||
void makeSound() override {
|
||||
std::cout << "Dog barks!\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Cat : public Animal {
|
||||
public:
|
||||
void makeSound() override {
|
||||
std::cout << "Cat meows!\n";
|
||||
}
|
||||
};
|
||||
```
|
||||
class Animal {
|
||||
public:
|
||||
virtual void makeSound() {
|
||||
std::cout << "The Animal makes a sound\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Dog : public Animal {
|
||||
public:
|
||||
void makeSound() override {
|
||||
std::cout << "Dog barks!\n";
|
||||
}
|
||||
};
|
||||
|
||||
class Cat : public Animal {
|
||||
public:
|
||||
void makeSound() override {
|
||||
std::cout << "Cat meows!\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
In this example, we have an `Animal` base class with a virtual `makeSound` function. We then derive two classes, `Dog` and `Cat`, which override the `makeSound` function. This enables polymorphic behavior, where an `Animal` pointer or reference can be used to access the correct `makeSound` function depending on the derived class type.
|
||||
|
||||
```cpp
|
||||
Animal *animals[2] = {new Dog, new Cat};
|
||||
animals[0]->makeSound(); // Output: Dog barks!
|
||||
animals[1]->makeSound(); // Output: Cat meows!
|
||||
```
|
||||
Animal *animals[2] = {new Dog, new Cat};
|
||||
animals[0]->makeSound(); // Output: Dog barks!
|
||||
animals[1]->makeSound(); // Output: Cat meows!
|
||||
|
||||
|
||||
That's a brief overview of object-oriented programming concepts in C++.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -1,9 +1,8 @@
|
||||
# Operator Overloading in C++
|
||||
|
||||
Operator overloading in C++ is a feature that allows you to redefine the way operators work for user-defined types (such as classes and structs). It lets you specify how operators like +, -, *, ==, etc., behave when applied to objects of your class.
|
||||
Visit the following resources to learn more:
|
||||
Operator overloading in C++ is a feature that allows you to redefine the way operators work for user-defined types (such as classes and structs). It lets you specify how operators like +, -, \*, ==, etc., behave when applied to objects of your class. Visit the following resources to learn more:
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@official@Operator Overloading - Microsoft Learn](https://learn.microsoft.com/en-us/cpp/cpp/operator-overloading)
|
||||
- [@article@operator overloading - cppreference.com](https://en.cppreference.com/w/cpp/language/operators)
|
||||
- [@article@operator overloading - cppreference.com](https://en.cppreference.com/w/cpp/language/operators)
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -4,47 +4,47 @@ Package managers are tools that automate the process of installing, upgrading, a
|
||||
|
||||
Some popular package managers used in the C++ ecosystem include:
|
||||
|
||||
- **Conan**
|
||||
- **vcpkg**
|
||||
- **C++ Archive Network (cppan)**
|
||||
* **Conan**
|
||||
* **vcpkg**
|
||||
* **C++ Archive Network (cppan)**
|
||||
|
||||
## Conan
|
||||
Conan
|
||||
-----
|
||||
|
||||
[Conan](https://conan.io/) is an open-source, decentralized, cross-platform package manager for C and C++ developers. It simplifies managing dependencies and reusing code, which benefits multi-platform development projects.
|
||||
|
||||
For example, installing a library using Conan:
|
||||
|
||||
```sh
|
||||
conan install poco/1.9.4@
|
||||
```
|
||||
conan install poco/1.9.4@
|
||||
|
||||
|
||||
## vcpkg
|
||||
vcpkg
|
||||
-----
|
||||
|
||||
[vcpkg](https://github.com/microsoft/vcpkg) is a cross-platform package manager created by Microsoft. It is an open-source library management system for C++ developers to build and manage their projects.
|
||||
|
||||
For example, installing a package using vcpkg:
|
||||
|
||||
```sh
|
||||
./vcpkg install boost:x64-windows
|
||||
```
|
||||
./vcpkg install boost:x64-windows
|
||||
|
||||
|
||||
## C++ Archive Network (cppan)
|
||||
C++ Archive Network (cppan)
|
||||
---------------------------
|
||||
|
||||
[cppan](https://cppan.org/) is a package manager and software repository for C++ developers, simplifying the process of managing and distributing C++ libraries and tools. It's now part of [build2](https://build2.org/), a build toolchain that provides a package manager.
|
||||
|
||||
An example of a `cppan.yml` file:
|
||||
|
||||
```yaml
|
||||
#
|
||||
# cppan.yml
|
||||
#
|
||||
|
||||
project:
|
||||
api_version: 1
|
||||
|
||||
depend:
|
||||
- pvt.cppan.demo.sqlite3
|
||||
- pvt.cppan.demo.xz_utils.lzma
|
||||
```
|
||||
#
|
||||
# cppan.yml
|
||||
#
|
||||
|
||||
project:
|
||||
api_version: 1
|
||||
|
||||
depend:
|
||||
- pvt.cppan.demo.sqlite3
|
||||
- pvt.cppan.demo.xz_utils.lzma
|
||||
|
||||
|
||||
With these package managers, you can streamline your development process and easily manage dependencies in your C++ projects. In addition, you can easily reuse the code in your projects to improve code quality and accelerate development.
|
||||
@@ -6,43 +6,42 @@ Partial template specialization is achieved by providing a specialization of a t
|
||||
|
||||
Here is a code example that demonstrates partial template specialization:
|
||||
|
||||
```cpp
|
||||
// Primary template
|
||||
template <typename T>
|
||||
struct MyTemplate {
|
||||
static const char* name() {
|
||||
return "General case";
|
||||
// Primary template
|
||||
template <typename T>
|
||||
struct MyTemplate {
|
||||
static const char* name() {
|
||||
return "General case";
|
||||
}
|
||||
};
|
||||
|
||||
// Partial specialization for pointers
|
||||
template <typename T>
|
||||
struct MyTemplate<T*> {
|
||||
static const char* name() {
|
||||
return "Partial specialization for pointers";
|
||||
}
|
||||
};
|
||||
|
||||
// Full specialization for int
|
||||
template <>
|
||||
struct MyTemplate<int> {
|
||||
static const char* name() {
|
||||
return "Full specialization for int";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyTemplate<double> t1; // General case
|
||||
MyTemplate<double*> t2; // Partial specialization for pointers
|
||||
MyTemplate<int> t3; // Full specialization for int
|
||||
|
||||
std::cout << t1.name() << '\n';
|
||||
std::cout << t2.name() << '\n';
|
||||
std::cout << t3.name() << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Partial specialization for pointers
|
||||
template <typename T>
|
||||
struct MyTemplate<T*> {
|
||||
static const char* name() {
|
||||
return "Partial specialization for pointers";
|
||||
}
|
||||
};
|
||||
|
||||
// Full specialization for int
|
||||
template <>
|
||||
struct MyTemplate<int> {
|
||||
static const char* name() {
|
||||
return "Full specialization for int";
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
MyTemplate<double> t1; // General case
|
||||
MyTemplate<double*> t2; // Partial specialization for pointers
|
||||
MyTemplate<int> t3; // Full specialization for int
|
||||
|
||||
std::cout << t1.name() << '\n';
|
||||
std::cout << t2.name() << '\n';
|
||||
std::cout << t3.name() << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In the example above, we have defined a primary template `MyTemplate` with a single type parameter `T`. We then provide a partial template specialization for pointer types by specifying `MyTemplate<T*>`. This means that the partial specialization will be chosen when the type argument is a pointer type.
|
||||
|
||||
@@ -50,10 +49,9 @@ Lastly, we provide a full specialization for the `int` type by specifying `MyTem
|
||||
|
||||
When running this example, the output will be:
|
||||
|
||||
```
|
||||
General case
|
||||
Partial specialization for pointers
|
||||
Full specialization for int
|
||||
```
|
||||
General case
|
||||
Partial specialization for pointers
|
||||
Full specialization for int
|
||||
|
||||
|
||||
This demonstrates that the partial specialization works as expected, and is chosen for pointer types, while the full specialization is chosen for the `int` type.
|
||||
@@ -2,48 +2,49 @@
|
||||
|
||||
Pimpl (Pointer-to-Implementation) idiom, also known as a private class data, compiler firewall, or handle classes, is a technique used in C++ to hide the implementation details of a class by using a forward declaration to a private structure or class, keeping the public interface of the class clean, and reducing compile-time dependencies.
|
||||
|
||||
## Implementation
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
Here is a simple example illustrating the Pimpl idiom:
|
||||
|
||||
**my_class.h**
|
||||
```cpp
|
||||
class MyClass_Impl; // forward declaration
|
||||
**my\_class.h**
|
||||
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass();
|
||||
~MyClass();
|
||||
void some_method();
|
||||
|
||||
private:
|
||||
MyClass_Impl *pimpl; // pointer to the implementation
|
||||
};
|
||||
```
|
||||
|
||||
**my_class.cpp**
|
||||
```cpp
|
||||
#include "my_class.h"
|
||||
#include <iostream>
|
||||
|
||||
class MyClass_Impl // the actual implementation
|
||||
{
|
||||
public:
|
||||
void some_method()
|
||||
class MyClass_Impl; // forward declaration
|
||||
|
||||
class MyClass
|
||||
{
|
||||
std::cout << "Implementation method called!\n";
|
||||
public:
|
||||
MyClass();
|
||||
~MyClass();
|
||||
void some_method();
|
||||
|
||||
private:
|
||||
MyClass_Impl *pimpl; // pointer to the implementation
|
||||
};
|
||||
|
||||
|
||||
**my\_class.cpp**
|
||||
|
||||
#include "my_class.h"
|
||||
#include <iostream>
|
||||
|
||||
class MyClass_Impl // the actual implementation
|
||||
{
|
||||
public:
|
||||
void some_method()
|
||||
{
|
||||
std::cout << "Implementation method called!\n";
|
||||
}
|
||||
};
|
||||
|
||||
MyClass::MyClass() : pimpl(new MyClass_Impl()) {} // constructor
|
||||
|
||||
MyClass::~MyClass() { delete pimpl; } // destructor
|
||||
|
||||
void MyClass::some_method()
|
||||
{
|
||||
pimpl->some_method(); // delegation to the implementation
|
||||
}
|
||||
};
|
||||
|
||||
MyClass::MyClass() : pimpl(new MyClass_Impl()) {} // constructor
|
||||
|
||||
MyClass::~MyClass() { delete pimpl; } // destructor
|
||||
|
||||
void MyClass::some_method()
|
||||
{
|
||||
pimpl->some_method(); // delegation to the implementation
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Now, all the public methods of `MyClass` will delegate the calls to the corresponding methods of `MyClass_Impl`. By doing this, you can hide the details of class implementation, reduce the compile-time dependencies, and ease the maintenance of your code.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,139 +2,163 @@
|
||||
|
||||
A pointer is a variable that stores the memory address of another variable (or function). It points to the location of the variable in memory, and it allows you to access or modify the value indirectly. Here's a general format to declare a pointer:
|
||||
|
||||
```cpp
|
||||
dataType *pointerName;
|
||||
```
|
||||
dataType *pointerName;
|
||||
|
||||
|
||||
**Initializing a pointer:**
|
||||
|
||||
```cpp
|
||||
int num = 10;
|
||||
int *ptr = # // Pointer 'ptr' now points to the memory address of 'num'
|
||||
```
|
||||
int num = 10;
|
||||
int *ptr = # // Pointer 'ptr' now points to the memory address of 'num'
|
||||
|
||||
|
||||
**Accessing value using a pointer:**
|
||||
|
||||
```cpp
|
||||
int value = *ptr; // Value now contains the value of the variable that 'ptr' points to (i.e., 10)
|
||||
```
|
||||
int value = *ptr; // Value now contains the value of the variable that 'ptr' points to (i.e., 10)
|
||||
|
||||
|
||||
**Function pointer:**
|
||||
|
||||
```cpp
|
||||
int add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
int add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int (*funcptr) (int, int) = add; // Pointer 'funcptr' now points to the functions 'add'
|
||||
funcptr(4, 5); // Return 9
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
int (*funcptr) (int, int) = add; // Pointer 'funcptr' now points to the functions 'add'
|
||||
funcptr(4, 5); // Return 9
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
References
|
||||
----------
|
||||
|
||||
A reference is an alias for an existing variable, meaning it's a different name for the same memory location. Unlike pointers, references cannot be null, and they must be initialized when they are declared. Once a reference is initialized, it cannot be changed to refer to another variable.
|
||||
|
||||
Here's a general format to declare a reference:
|
||||
|
||||
```cpp
|
||||
dataType &referenceName = existingVariable;
|
||||
```
|
||||
dataType &referenceName = existingVariable;
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
```cpp
|
||||
int num = 10;
|
||||
int &ref = num; // Reference 'ref' is now an alias of 'num'
|
||||
```
|
||||
int num = 10;
|
||||
int &ref = num; // Reference 'ref' is now an alias of 'num'
|
||||
|
||||
|
||||
Modifying the value of `ref` will also modify the value of `num` because they share the same memory location.
|
||||
|
||||
**Note:** References are generally used when you want to pass a variable by reference in function arguments or when you want to create an alias for a variable without the need for pointer syntax.
|
||||
|
||||
# Constant Pointers and Pointers to Constants in C++
|
||||
Constant Pointers and Pointers to Constants in C++
|
||||
==================================================
|
||||
|
||||
In C++, the placement of the `const` keyword in a pointer declaration changes its meaning. Let’s go through the three main cases.
|
||||
|
||||
---
|
||||
* * *
|
||||
|
||||
1\. Constant Pointer
|
||||
--------------------
|
||||
|
||||
## 1. Constant Pointer
|
||||
A **constant pointer** means the pointer itself cannot change the address it holds after initialization, but the value at that address can still be modified.
|
||||
|
||||
**Syntax:**
|
||||
```cpp
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
int* const ptr = &x; // Constant pointer to int
|
||||
|
||||
*ptr = 15; // ✅ Allowed: we can change the value of x
|
||||
// ptr = &y; // ❌ Error: cannot make ptr point to y
|
||||
```
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
int* const ptr = &x; // Constant pointer to int
|
||||
|
||||
*ptr = 15; // ✅ Allowed: we can change the value of x
|
||||
// ptr = &y; // ❌ Error: cannot make ptr point to y
|
||||
|
||||
|
||||
👉 **Rule:**
|
||||
- You **cannot change** what it points to.
|
||||
- You **can change** the value at the address.
|
||||
👉 **Rule:**
|
||||
|
||||
---
|
||||
* You **cannot change** what it points to.
|
||||
* You **can change** the value at the address.
|
||||
|
||||
* * *
|
||||
|
||||
2\. Pointer to Constant
|
||||
-----------------------
|
||||
|
||||
## 2. Pointer to Constant
|
||||
A **pointer to constant** means the pointer can change to point to different addresses, but the value at the address cannot be modified through this pointer.
|
||||
|
||||
**Syntax:**
|
||||
```cpp
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
const int* ptr = &x; // Pointer to constant int
|
||||
|
||||
// *ptr = 15; // ❌ Error: cannot modify x through ptr
|
||||
ptr = &y; // ✅ Allowed: can point to another variable
|
||||
```
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
const int* ptr = &x; // Pointer to constant int
|
||||
|
||||
// *ptr = 15; // ❌ Error: cannot modify x through ptr
|
||||
ptr = &y; // ✅ Allowed: can point to another variable
|
||||
|
||||
|
||||
👉 **Rule:**
|
||||
- You **can change** what it points to.
|
||||
- You **cannot change** the value at the address through this pointer.
|
||||
👉 **Rule:**
|
||||
|
||||
---
|
||||
* You **can change** what it points to.
|
||||
* You **cannot change** the value at the address through this pointer.
|
||||
|
||||
* * *
|
||||
|
||||
3\. Constant Pointer to Constant
|
||||
--------------------------------
|
||||
|
||||
## 3. Constant Pointer to Constant
|
||||
A **constant pointer to constant** means neither the pointer nor the value it points to can be changed.
|
||||
|
||||
**Syntax:**
|
||||
```cpp
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
const int* const ptr = &x; // Constant pointer to constant int
|
||||
|
||||
// *ptr = 15; // ❌ Error: cannot modify x
|
||||
// ptr = &y; // ❌ Error: cannot make ptr point to y
|
||||
```
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
const int* const ptr = &x; // Constant pointer to constant int
|
||||
|
||||
// *ptr = 15; // ❌ Error: cannot modify x
|
||||
// ptr = &y; // ❌ Error: cannot make ptr point to y
|
||||
|
||||
|
||||
👉 **Rule:**
|
||||
- You **cannot change** what it points to.
|
||||
- You **cannot change** the value at the address through this pointer.
|
||||
👉 **Rule:**
|
||||
|
||||
---
|
||||
* You **cannot change** what it points to.
|
||||
* You **cannot change** the value at the address through this pointer.
|
||||
|
||||
## Summary Table
|
||||
* * *
|
||||
|
||||
| Declaration | Can change pointer? | Can change value? |
|
||||
|---------------------------|----------------------|-------------------|
|
||||
| `int* const ptr` | ❌ No | ✅ Yes |
|
||||
| `const int* ptr` | ✅ Yes | ❌ No |
|
||||
| `const int* const ptr` | ❌ No | ❌ No |
|
||||
Summary Table
|
||||
-------------
|
||||
|
||||
---
|
||||
Declaration
|
||||
|
||||
## Rule of Thumb
|
||||
Can change pointer?
|
||||
|
||||
- `int* const ptr` → constant **pointer**, mutable **pointee**.
|
||||
- `const int* ptr` → mutable **pointer**, constant **pointee**.
|
||||
- `const int* const ptr` → constant **pointer**, constant **pointee**.
|
||||
Can change value?
|
||||
|
||||
`int* const ptr`
|
||||
|
||||
Learn more from the following resources:
|
||||
❌ No
|
||||
|
||||
- [@article@Function Pointer in C++](https://www.scaler.com/topics/cpp/function-pointer-cpp/)
|
||||
✅ Yes
|
||||
|
||||
`const int* ptr`
|
||||
|
||||
✅ Yes
|
||||
|
||||
❌ No
|
||||
|
||||
`const int* const ptr`
|
||||
|
||||
❌ No
|
||||
|
||||
❌ No
|
||||
|
||||
* * *
|
||||
|
||||
Rule of Thumb
|
||||
-------------
|
||||
|
||||
* `int* const ptr` → constant **pointer**, mutable **pointee**.
|
||||
* `const int* ptr` → mutable **pointer**, constant **pointee**.
|
||||
* `const int* const ptr` → constant **pointer**, constant **pointee**.
|
||||
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@article@Function Pointer in C++](https://www.scaler.com/topics/cpp/function-pointer-cpp/)
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,72 +2,69 @@
|
||||
|
||||
RAII is a popular idiom in C++ that focuses on using the object's life cycle to manage resources. It encourages binding the resource lifetime to the scope of a corresponding object so that it's automatically acquired when an object is created and released when the object is destroyed. This helps in simplifying the code, avoiding leaks and managing resources efficiently.
|
||||
|
||||
## Code Examples
|
||||
Code Examples
|
||||
-------------
|
||||
|
||||
Here's an example of using RAII to manage resources, specifically a dynamically allocated array:
|
||||
|
||||
```cpp
|
||||
class ManagedArray {
|
||||
public:
|
||||
ManagedArray(size_t size) : size_(size), data_(new int[size]) {
|
||||
}
|
||||
|
||||
~ManagedArray() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
// Access function
|
||||
int& operator [](size_t i) {
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
int* data_;
|
||||
};
|
||||
```
|
||||
class ManagedArray {
|
||||
public:
|
||||
ManagedArray(size_t size) : size_(size), data_(new int[size]) {
|
||||
}
|
||||
|
||||
~ManagedArray() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
// Access function
|
||||
int& operator [](size_t i) {
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
int* data_;
|
||||
};
|
||||
|
||||
|
||||
Usages:
|
||||
|
||||
```cpp
|
||||
{
|
||||
ManagedArray arr(10);
|
||||
arr[0] = 42;
|
||||
|
||||
// No need to explicitly free memory, it will be automatically released when arr goes out of scope.
|
||||
}
|
||||
```
|
||||
{
|
||||
ManagedArray arr(10);
|
||||
arr[0] = 42;
|
||||
|
||||
// No need to explicitly free memory, it will be automatically released when arr goes out of scope.
|
||||
}
|
||||
|
||||
|
||||
Another common use case is managing a mutex lock:
|
||||
|
||||
```cpp
|
||||
class Lock {
|
||||
public:
|
||||
Lock(std::mutex& mtx) : mutex_(mtx) {
|
||||
mutex_.lock();
|
||||
}
|
||||
|
||||
~Lock() {
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex& mutex_;
|
||||
};
|
||||
```
|
||||
class Lock {
|
||||
public:
|
||||
Lock(std::mutex& mtx) : mutex_(mtx) {
|
||||
mutex_.lock();
|
||||
}
|
||||
|
||||
~Lock() {
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex& mutex_;
|
||||
};
|
||||
|
||||
|
||||
Usages:
|
||||
|
||||
```cpp
|
||||
std::mutex some_mutex;
|
||||
|
||||
void protected_function() {
|
||||
Lock lock(some_mutex);
|
||||
|
||||
// Do some work that must be synchronized
|
||||
|
||||
// No need to explicitly unlock the mutex, it will be automatically unlocked when lock goes out of scope.
|
||||
}
|
||||
```
|
||||
std::mutex some_mutex;
|
||||
|
||||
void protected_function() {
|
||||
Lock lock(some_mutex);
|
||||
|
||||
// Do some work that must be synchronized
|
||||
|
||||
// No need to explicitly unlock the mutex, it will be automatically unlocked when lock goes out of scope.
|
||||
}
|
||||
|
||||
|
||||
In both examples, the constructor acquires the resource (memory for the array and the lock for the mutex), and the destructor takes care of releasing them. This way, the resource management is tied to the object's lifetime, and the resource is correctly released even in case of an exception being thrown.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -1,110 +1,159 @@
|
||||
# References
|
||||
|
||||
A reference can be considered as a constant pointer (not to be confused with a pointer to a constant value) which always points to (references) the same object. They are declared using the `&` (ampersand) symbol.
|
||||
|
||||
## Declaration and Initialization
|
||||
Declaration and Initialization
|
||||
------------------------------
|
||||
|
||||
To declare a reference, use the variable type followed by the `&` symbol and the reference's name. Note that you must initialize a reference when you declare it.
|
||||
|
||||
```cpp
|
||||
int var = 10; // Declare an integer variable
|
||||
int& ref = var; // Declare a reference that "points to" var
|
||||
```
|
||||
int var = 10; // Declare an integer variable
|
||||
int& ref = var; // Declare a reference that "points to" var
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
## Usage
|
||||
You can use the reference just like you'd use the original variable. When you change the value of the reference, the value of the original variable also changes, because they both share the same memory location.
|
||||
|
||||
```cpp
|
||||
var = 20; // Sets the value of var to 20
|
||||
std::cout << ref << '\n'; // Outputs 20
|
||||
var = 20; // Sets the value of var to 20
|
||||
std::cout << ref << '\n'; // Outputs 20
|
||||
|
||||
ref = 30; // Sets the value of ref to 30
|
||||
std::cout << var << '\n'; // Outputs 30
|
||||
|
||||
|
||||
ref = 30; // Sets the value of ref to 30
|
||||
std::cout << var << '\n'; // Outputs 30
|
||||
```
|
||||
Function Parameters
|
||||
-------------------
|
||||
|
||||
## Function Parameters
|
||||
You can use references as function parameters to create an alias for an argument. This is commonly done when you need to modify the original variable or when passing an object of considerable size to avoid the cost of copying.
|
||||
```cpp
|
||||
void swap(int& a, int& b) {
|
||||
int temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int x = 5, y = 10;
|
||||
std::cout << "Before Swap: x = " << x << " y = " << y << '\n'; // Outputs 5 10
|
||||
|
||||
swap(x, y);
|
||||
std::cout << "After Swap: x = " << x << " y = " << y << '\n'; // Outputs 10 5
|
||||
}
|
||||
```
|
||||
void swap(int& a, int& b) {
|
||||
int temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int x = 5, y = 10;
|
||||
std::cout << "Before Swap: x = " << x << " y = " << y << '\n'; // Outputs 5 10
|
||||
|
||||
swap(x, y);
|
||||
std::cout << "After Swap: x = " << x << " y = " << y << '\n'; // Outputs 10 5
|
||||
}
|
||||
|
||||
|
||||
|
||||
## References in Range-based For Loops
|
||||
References in Range-based For Loops
|
||||
-----------------------------------
|
||||
|
||||
When iterating over containers such as `std::vector<std::string>`, the choice of `auto`, `auto &`, `auto const &`, or `const auto` makes a big difference:
|
||||
|
||||
```cpp
|
||||
std::vector<std::string> stooges {"xyz", "abc", "def"};
|
||||
|
||||
// Read-only, no copies
|
||||
for (auto const &str : stooges)
|
||||
std::cout << str << std::endl;
|
||||
|
||||
// Makes a copy of each string
|
||||
for (auto str : stooges)
|
||||
std::cout << str << std::endl;
|
||||
|
||||
// Direct reference, can modify original elements
|
||||
for (auto &str : stooges)
|
||||
str += "!";
|
||||
|
||||
// Makes a copy of each string, but prevents modification of the copy
|
||||
for (const auto str : stooges)
|
||||
std::cout << str << std::endl;
|
||||
```
|
||||
std::vector<std::string> stooges {"xyz", "abc", "def"};
|
||||
|
||||
// Read-only, no copies
|
||||
for (auto const &str : stooges)
|
||||
std::cout << str << std::endl;
|
||||
|
||||
// Makes a copy of each string
|
||||
for (auto str : stooges)
|
||||
std::cout << str << std::endl;
|
||||
|
||||
// Direct reference, can modify original elements
|
||||
for (auto &str : stooges)
|
||||
str += "!";
|
||||
|
||||
// Makes a copy of each string, but prevents modification of the copy
|
||||
for (const auto str : stooges)
|
||||
std::cout << str << std::endl;
|
||||
|
||||
|
||||
### Comparison
|
||||
|
||||
| Loop style | Copies made? | Can modify element? | Efficiency | Typical Use Case |
|
||||
|----------------------|--------------|-----------------------|----------------------------------|-------------------------------------|
|
||||
| `auto str` | ✅ Yes | ✅ Only the copy | Less efficient for large objects | When you need a local, mutable copy |
|
||||
| `auto const &str` | ❌ No | ❌ Read-only | Most efficient for read-only use | Safely read elements without copying |
|
||||
| `auto &str` | ❌ No | ✅ Modifies original | Efficient, mutates container | Modify elements in place |
|
||||
| `const auto str` | ✅ Yes | ❌ Read-only | Less efficient for large objects | Explicit read-only copy |
|
||||
Loop style
|
||||
|
||||
---
|
||||
Copies made?
|
||||
|
||||
## Example: Why `const auto str` Can Be Useful
|
||||
Can modify element?
|
||||
|
||||
Efficiency
|
||||
|
||||
Typical Use Case
|
||||
|
||||
`auto str`
|
||||
|
||||
✅ Yes
|
||||
|
||||
✅ Only the copy
|
||||
|
||||
Less efficient for large objects
|
||||
|
||||
When you need a local, mutable copy
|
||||
|
||||
`auto const &str`
|
||||
|
||||
❌ No
|
||||
|
||||
❌ Read-only
|
||||
|
||||
Most efficient for read-only use
|
||||
|
||||
Safely read elements without copying
|
||||
|
||||
`auto &str`
|
||||
|
||||
❌ No
|
||||
|
||||
✅ Modifies original
|
||||
|
||||
Efficient, mutates container
|
||||
|
||||
Modify elements in place
|
||||
|
||||
`const auto str`
|
||||
|
||||
✅ Yes
|
||||
|
||||
❌ Read-only
|
||||
|
||||
Less efficient for large objects
|
||||
|
||||
Explicit read-only copy
|
||||
|
||||
* * *
|
||||
|
||||
Example: Why `const auto str` Can Be Useful
|
||||
-------------------------------------------
|
||||
|
||||
Sometimes you want to make sure you **don’t accidentally modify** the loop variable, even though it’s just a copy. Declaring it `const` ensures this safety:
|
||||
|
||||
```cpp
|
||||
std::vector<std::string> stooges {"xyz", "abc", "def"};
|
||||
|
||||
for (const auto str : stooges) {
|
||||
// str += "!"; // ❌ Error: str is const, cannot be modified
|
||||
std::cout << str << std::endl; // Safe read-only copy
|
||||
}
|
||||
```
|
||||
std::vector<std::string> stooges {"xyz", "abc", "def"};
|
||||
|
||||
for (const auto str : stooges) {
|
||||
// str += "!"; // ❌ Error: str is const, cannot be modified
|
||||
std::cout << str << std::endl; // Safe read-only copy
|
||||
}
|
||||
|
||||
|
||||
This is useful when:
|
||||
- You **don’t need references** but still want to guarantee immutability inside the loop body.
|
||||
- You want to make your **intent explicit**: the loop variable should not be changed.
|
||||
|
||||
---
|
||||
* You **don’t need references** but still want to guarantee immutability inside the loop body.
|
||||
* You want to make your **intent explicit**: the loop variable should not be changed.
|
||||
|
||||
## Rule of Thumb
|
||||
* * *
|
||||
|
||||
- Use **`auto const &`** for **read-only access** (best efficiency).
|
||||
- Use **`auto &`** when you need to **modify elements in place**.
|
||||
- Use **`auto`** when you need a **mutable copy**.
|
||||
- Use **`const auto`** when you want a **read-only copy** inside the loop to prevent accidental modification.
|
||||
Rule of Thumb
|
||||
-------------
|
||||
|
||||
---
|
||||
* Use **`auto const &`** for **read-only access** (best efficiency).
|
||||
* Use **`auto &`** when you need to **modify elements in place**.
|
||||
* Use **`auto`** when you need a **mutable copy**.
|
||||
* Use **`const auto`** when you want a **read-only copy** inside the loop to prevent accidental modification.
|
||||
|
||||
## Performance Notes
|
||||
* * *
|
||||
|
||||
- For **large objects** like `std::string`, `std::vector`, or custom classes, prefer **`auto const &`** to avoid unnecessary copies.
|
||||
- For **small, cheap-to-copy types** like `int`, `char`, or `bool`, using `auto` or `const auto` is fine and often simpler.
|
||||
- `auto &` should only be used when you *intend* to modify the elements in place, since it directly mutates the container.
|
||||
Performance Notes
|
||||
-----------------
|
||||
|
||||
* For **large objects** like `std::string`, `std::vector`, or custom classes, prefer **`auto const &`** to avoid unnecessary copies.
|
||||
* For **small, cheap-to-copy types** like `int`, `char`, or `bool`, using `auto` or `const auto` is fine and often simpler.
|
||||
* `auto &` should only be used when you _intend_ to modify the elements in place, since it directly mutates the container.
|
||||
@@ -6,24 +6,23 @@ Using `reinterpret_cast` should be handled with care, as it can lead to undefine
|
||||
|
||||
Here's an example of usage:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int num = 42;
|
||||
int *num_ptr = #
|
||||
|
||||
// Disguise the integer pointer as a char pointer
|
||||
char *char_ptr = reinterpret_cast<char *>(num_ptr);
|
||||
|
||||
for (size_t i = 0; i < sizeof(int); ++i) {
|
||||
// Print the individual bytes of the integer as characters
|
||||
std::cout << "Byte " << i << ": " << char_ptr[i] << '\n';
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
int num = 42;
|
||||
int *num_ptr = #
|
||||
|
||||
// Disguise the integer pointer as a char pointer
|
||||
char *char_ptr = reinterpret_cast<char *>(num_ptr);
|
||||
|
||||
for (size_t i = 0; i < sizeof(int); ++i) {
|
||||
// Print the individual bytes of the integer as characters
|
||||
std::cout << "Byte " << i << ": " << char_ptr[i] << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, we're using `reinterpret_cast` to change the type of a pointer from `int *` to `char *`, effectively treating the integer as an array of characters and printing each byte.
|
||||
|
||||
|
||||
@@ -4,67 +4,69 @@ Run-Time Type Identification (RTTI) is a feature in C++ that allows you to obtai
|
||||
|
||||
There are two main mechanisms for RTTI in C++:
|
||||
|
||||
- `typeid` operator
|
||||
- `dynamic_cast` operator
|
||||
* `typeid` operator
|
||||
* `dynamic_cast` operator
|
||||
|
||||
## typeid operator
|
||||
typeid operator
|
||||
---------------
|
||||
|
||||
`typeid` is an operator that returns a reference to an object of type `std::type_info`, which contains information about the type of the object. The header file `<typeinfo>` should be included to use `typeid`.
|
||||
|
||||
Here is an example:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
|
||||
class Base { virtual void dummy() {} };
|
||||
class Derived : public Base { /* ... */ };
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
|
||||
class Base { virtual void dummy() {} };
|
||||
class Derived : public Base { /* ... */ };
|
||||
|
||||
int main() {
|
||||
Base* base_ptr = new Derived;
|
||||
|
||||
// Using typeid to get the type of the object
|
||||
std::cout << "Type: " << typeid(*base_ptr).name() << '\n';
|
||||
|
||||
delete base_ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
Base* base_ptr = new Derived;
|
||||
dynamic\_cast operator
|
||||
----------------------
|
||||
|
||||
// Using typeid to get the type of the object
|
||||
std::cout << "Type: " << typeid(*base_ptr).name() << '\n';
|
||||
|
||||
delete base_ptr;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## dynamic_cast operator
|
||||
|
||||
`dynamic_cast` is a type-casting operator that performs a runtime type check and safely downcasts a base pointer or reference to a derived pointer or reference. It returns null or throws a bad_cast exception (if casting references) when the casting fails.
|
||||
`dynamic_cast` is a type-casting operator that performs a runtime type check and safely downcasts a base pointer or reference to a derived pointer or reference. It returns null or throws a bad\_cast exception (if casting references) when the casting fails.
|
||||
|
||||
Here is an example:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
class Base { virtual void dummy() {} };
|
||||
class Derived1 : public Base { /* ... */ };
|
||||
class Derived2 : public Base { /* ... */ };
|
||||
|
||||
int main() {
|
||||
Base* base_ptr = new Derived1;
|
||||
|
||||
// Using dynamic_cast to safely downcast the pointer
|
||||
Derived1* derived1_ptr = dynamic_cast<Derived1*>(base_ptr);
|
||||
if (derived1_ptr) {
|
||||
std::cout << "Downcast to Derived1 successful\n";
|
||||
#include <iostream>
|
||||
|
||||
class Base { virtual void dummy() {} };
|
||||
class Derived1 : public Base { /* ... */ };
|
||||
class Derived2 : public Base { /* ... */ };
|
||||
|
||||
int main() {
|
||||
Base* base_ptr = new Derived1;
|
||||
|
||||
// Using dynamic_cast to safely downcast the pointer
|
||||
Derived1* derived1_ptr = dynamic_cast<Derived1*>(base_ptr);
|
||||
if (derived1_ptr) {
|
||||
std::cout << "Downcast to Derived1 successful\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "Downcast to Derived1 failed\n";
|
||||
}
|
||||
|
||||
Derived2* derived2_ptr = dynamic_cast<Derived2*>(base_ptr);
|
||||
if (derived2_ptr) {
|
||||
std::cout << "Downcast to Derived2 successful\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "Downcast to Derived2 failed\n";
|
||||
}
|
||||
|
||||
delete base_ptr;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
std::cout << "Downcast to Derived1 failed\n";
|
||||
}
|
||||
|
||||
Derived2* derived2_ptr = dynamic_cast<Derived2*>(base_ptr);
|
||||
if (derived2_ptr) {
|
||||
std::cout << "Downcast to Derived2 successful\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "Downcast to Derived2 failed\n";
|
||||
}
|
||||
|
||||
delete base_ptr;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Please note that the use of RTTI can have some performance overhead, as it requires additional compiler-generated information to be stored and processed during runtime.
|
||||
@@ -8,14 +8,13 @@ The Rule of Zero, Three, and Five is a set of guidelines for managing object res
|
||||
|
||||
The Rule of Zero states that if a class or structure does not explicitly manage resources, it should not define any of the special member functions, i.e., destructor, copy constructor, copy assignment operator, move constructor, and move assignment operator. The compiler will automatically generate these functions, and the behavior will be correct for managing resources like memory and file handles.
|
||||
|
||||
*Example:*
|
||||
_Example:_
|
||||
|
||||
```cpp
|
||||
struct MyResource {
|
||||
std::string name;
|
||||
int value;
|
||||
};
|
||||
```
|
||||
struct MyResource {
|
||||
std::string name;
|
||||
int value;
|
||||
};
|
||||
|
||||
|
||||
In this example, MyResource is a simple structure that does not manage any resources, so it does not define any special member functions. The compiler will generate them automatically, and the behavior will be correct.
|
||||
|
||||
@@ -23,37 +22,36 @@ In this example, MyResource is a simple structure that does not manage any resou
|
||||
|
||||
The Rule of Three states that a class or structure that manages resources should define the following three special member functions:
|
||||
|
||||
- Destructor
|
||||
- Copy constructor
|
||||
- Copy assignment operator
|
||||
* Destructor
|
||||
* Copy constructor
|
||||
* Copy assignment operator
|
||||
|
||||
These functions are necessary for proper resource management, such as releasing memory or correctly handling deep copies.
|
||||
|
||||
*Example:*
|
||||
_Example:_
|
||||
|
||||
```cpp
|
||||
class MyResource {
|
||||
public:
|
||||
// Constructor and destructor
|
||||
MyResource() : data(new int[100]) {}
|
||||
~MyResource() { delete[] data; }
|
||||
|
||||
// Copy constructor
|
||||
MyResource(const MyResource& other) : data(new int[100]) {
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
}
|
||||
|
||||
// Copy assignment operator
|
||||
MyResource& operator=(const MyResource& other) {
|
||||
if (&other == this) { return *this; }
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int* data;
|
||||
};
|
||||
```
|
||||
class MyResource {
|
||||
public:
|
||||
// Constructor and destructor
|
||||
MyResource() : data(new int[100]) {}
|
||||
~MyResource() { delete[] data; }
|
||||
|
||||
// Copy constructor
|
||||
MyResource(const MyResource& other) : data(new int[100]) {
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
}
|
||||
|
||||
// Copy assignment operator
|
||||
MyResource& operator=(const MyResource& other) {
|
||||
if (&other == this) { return *this; }
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int* data;
|
||||
};
|
||||
|
||||
|
||||
In this example, MyResource is a class that manages a resource (an array of integers), so it defines the destructor, copy constructor, and copy assignment operator.
|
||||
|
||||
@@ -61,49 +59,48 @@ In this example, MyResource is a class that manages a resource (an array of inte
|
||||
|
||||
The Rule of Five extends the Rule of Three to include two additional special member functions:
|
||||
|
||||
- Move constructor
|
||||
- Move assignment operator
|
||||
* Move constructor
|
||||
* Move assignment operator
|
||||
|
||||
Modern C++ introduces move semantics, which allows for more efficient handling of resources by transferring ownership without necessarily copying all the data.
|
||||
|
||||
*Example:*
|
||||
_Example:_
|
||||
|
||||
```cpp
|
||||
class MyResource {
|
||||
public:
|
||||
// Constructors and destructor
|
||||
MyResource() : data(new int[100]) {}
|
||||
~MyResource() { delete[] data; }
|
||||
|
||||
// Copy constructor
|
||||
MyResource(const MyResource& other) : data(new int[100]) {
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
}
|
||||
|
||||
// Copy assignment operator
|
||||
MyResource& operator=(const MyResource& other) {
|
||||
if (&other == this) { return *this; }
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
MyResource(MyResource&& other) noexcept : data(other.data) {
|
||||
other.data = nullptr;
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
MyResource& operator=(MyResource&& other) noexcept {
|
||||
if (&other == this) { return *this; }
|
||||
delete[] data;
|
||||
data = other.data;
|
||||
other.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int* data;
|
||||
};
|
||||
```
|
||||
class MyResource {
|
||||
public:
|
||||
// Constructors and destructor
|
||||
MyResource() : data(new int[100]) {}
|
||||
~MyResource() { delete[] data; }
|
||||
|
||||
// Copy constructor
|
||||
MyResource(const MyResource& other) : data(new int[100]) {
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
}
|
||||
|
||||
// Copy assignment operator
|
||||
MyResource& operator=(const MyResource& other) {
|
||||
if (&other == this) { return *this; }
|
||||
std::copy(other.data, other.data + 100, data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
MyResource(MyResource&& other) noexcept : data(other.data) {
|
||||
other.data = nullptr;
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
MyResource& operator=(MyResource&& other) noexcept {
|
||||
if (&other == this) { return *this; }
|
||||
delete[] data;
|
||||
data = other.data;
|
||||
other.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int* data;
|
||||
};
|
||||
|
||||
|
||||
In this example, MyResource is a class that manages a resource (an array of integers), so it defines all five special member functions for proper resource management and move semantics.
|
||||
@@ -2,57 +2,58 @@
|
||||
|
||||
In this section, we'll discuss the basic structure of a C++ program, walk you through your first program (the "Hello, World!" example), and provide additional explanations of its syntax.
|
||||
|
||||
## Hello, World!
|
||||
Hello, World!
|
||||
-------------
|
||||
|
||||
The first program that most people learn to write in any programming language is often a simple one that displays the message "Hello, World!" on the screen. Here's the classic "Hello, World!" program in C++:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!\n";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Let's break down the different components of this program:
|
||||
|
||||
## Header Files & Preprocessor Directives
|
||||
Header Files & Preprocessor Directives
|
||||
--------------------------------------
|
||||
|
||||
The first line of the program `#include <iostream>` is a [preprocessor directive](https://en.cppreference.com/w/cpp/preprocessor) that tells the compiler to include the header file `iostream`. Header files provide function and class declarations that we can use in our C++ programs.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
|
||||
## `main()` Function
|
||||
`main()` Function
|
||||
-----------------
|
||||
|
||||
In C++, the `main()` function serves as the entry point of your program. The operating system runs your program by calling this `main()` function. It should be defined only once in your program and must return an integer. The keyword `int` is the return type of this function which is an integer. Unlike C in C++ it is mandatory to have `int` as the return type for the `main` function.
|
||||
|
||||
```cpp
|
||||
int main() {
|
||||
// Your code goes here.
|
||||
}
|
||||
```
|
||||
int main() {
|
||||
// Your code goes here.
|
||||
}
|
||||
|
||||
|
||||
## Output to the Console
|
||||
Output to the Console
|
||||
---------------------
|
||||
|
||||
To output text to the console, we use the `std::cout` object and the insertion operator `<<`. In the "Hello, World!" example, we used the following line to print "Hello, World!" to the console:
|
||||
|
||||
```cpp
|
||||
std::cout << "Hello, World!\n";
|
||||
```
|
||||
- `std`: This is the namespace where C++ standard library entities (classes and functions) reside. It stands for "standard"
|
||||
- `std::cout`: The standard "character output" stream that writes to the console
|
||||
- `"Hello, World!"`: The string literal to print
|
||||
- `'\n'`: The "end line" manipulator that inserts a newline character and flushes the output buffer
|
||||
std::cout << "Hello, World!\n";
|
||||
|
||||
|
||||
## Return Statement
|
||||
* `std`: This is the namespace where C++ standard library entities (classes and functions) reside. It stands for "standard"
|
||||
* `std::cout`: The standard "character output" stream that writes to the console
|
||||
* `"Hello, World!"`: The string literal to print
|
||||
* `'\n'`: The "end line" manipulator that inserts a newline character and flushes the output buffer
|
||||
|
||||
Return Statement
|
||||
----------------
|
||||
|
||||
Lastly, the `return 0;` statement informs the operating system that the program executed successfully. Returning any other integer value indicates that an error occurred:
|
||||
|
||||
```cpp
|
||||
return 0;
|
||||
```
|
||||
return 0;
|
||||
|
||||
|
||||
Now that you understand the basic components of a C++ program, you can write your first program, compile it, and run it to see the "Hello, World!" message displayed on the screen.
|
||||
Now that you understand the basic components of a C++ program, you can write your first program, compile it, and run it to see the "Hello, World!" message displayed on the screen.
|
||||
@@ -2,69 +2,65 @@
|
||||
|
||||
**Scope** refers to the visibility and accessibility of variables, functions, classes, and other identifiers in a C++ program. It determines the lifetime and extent of these identifiers. In C++, there are four types of scope:
|
||||
|
||||
- **Global scope:** Identifiers declared outside any function or class have a global scope. They can be accessed from any part of the program (unless hidden by a local identifier with the same name). The lifetime of a global identifier is the entire duration of the program.
|
||||
* **Global scope:** Identifiers declared outside any function or class have a global scope. They can be accessed from any part of the program (unless hidden by a local identifier with the same name). The lifetime of a global identifier is the entire duration of the program.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <iostream>
|
||||
|
||||
int globalVar; // This is a global variable
|
||||
|
||||
int main() {
|
||||
std::cout << "Global variable: " << globalVar << '\n';
|
||||
}
|
||||
|
||||
|
||||
int globalVar; // This is a global variable
|
||||
* **Local scope:** Identifiers declared within a function or a block have a local scope. They can be accessed only within the function or the block they were declared in. Their lifetime is limited to the duration of the function/block execution.
|
||||
|
||||
int main() {
|
||||
std::cout << "Global variable: " << globalVar << '\n';
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
void localExample() {
|
||||
int localVar; // This is a local variable
|
||||
localVar = 5;
|
||||
std::cout << "Local variable: " << localVar << '\n';
|
||||
}
|
||||
|
||||
int main() {
|
||||
localExample();
|
||||
// std::cout << localVar << '\n'; //error: ‘localVar’ was not declared in this scope
|
||||
}
|
||||
|
||||
|
||||
- **Local scope:** Identifiers declared within a function or a block have a local scope. They can be accessed only within the function or the block they were declared in. Their lifetime is limited to the duration of the function/block execution.
|
||||
* **Namespace scope:** A namespace is a named scope that groups related identifiers together. Identifiers declared within a namespace have the namespace scope. They can be accessed using the namespace name and the scope resolution operator `::`.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <iostream>
|
||||
|
||||
namespace MyNamespace {
|
||||
int namespaceVar = 42;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Namespace variable: " << MyNamespace::namespaceVar << '\n';
|
||||
}
|
||||
|
||||
|
||||
void localExample() {
|
||||
int localVar; // This is a local variable
|
||||
localVar = 5;
|
||||
std::cout << "Local variable: " << localVar << '\n';
|
||||
}
|
||||
* **Class scope:** Identifiers declared within a class have a class scope. They can be accessed using the class name and the scope resolution operator `::` or, for non-static members, an object of the class and the dot `.` or arrow `->` operator.
|
||||
|
||||
int main() {
|
||||
localExample();
|
||||
// std::cout << localVar << '\n'; //error: ‘localVar’ was not declared in this scope
|
||||
}
|
||||
```
|
||||
|
||||
- **Namespace scope:** A namespace is a named scope that groups related identifiers together. Identifiers declared within a namespace have the namespace scope. They can be accessed using the namespace name and the scope resolution operator `::`.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
namespace MyNamespace {
|
||||
int namespaceVar = 42;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Namespace variable: " << MyNamespace::namespaceVar << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
- **Class scope:** Identifiers declared within a class have a class scope. They can be accessed using the class name and the scope resolution operator `::` or, for non-static members, an object of the class and the dot `.` or arrow `->` operator.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
class MyClass {
|
||||
public:
|
||||
static int staticMember;
|
||||
int nonStaticMember;
|
||||
|
||||
MyClass(int value) : nonStaticMember(value) {}
|
||||
};
|
||||
|
||||
int MyClass::staticMember = 7;
|
||||
|
||||
int main() {
|
||||
MyClass obj(10);
|
||||
std::cout << "Static member: " << MyClass::staticMember << '\n';
|
||||
std::cout << "Non-static member: " << obj.nonStaticMember << '\n';
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
class MyClass {
|
||||
public:
|
||||
static int staticMember;
|
||||
int nonStaticMember;
|
||||
|
||||
MyClass(int value) : nonStaticMember(value) {}
|
||||
};
|
||||
|
||||
int MyClass::staticMember = 7;
|
||||
|
||||
int main() {
|
||||
MyClass obj(10);
|
||||
std::cout << "Static member: " << MyClass::staticMember << '\n';
|
||||
std::cout << "Non-static member: " << obj.nonStaticMember << '\n';
|
||||
}
|
||||
|
||||
|
||||
Understanding various types of scope in C++ is essential for effective code structuring and management of resources in a codebase.
|
||||
@@ -2,55 +2,59 @@
|
||||
|
||||
Setting up C++ requires a few steps, including installing a compiler, configuring an Integrated Development Environment (IDE), and creating a new C++ project.
|
||||
|
||||
## 1. Installing a Compiler
|
||||
1\. Installing a Compiler
|
||||
-------------------------
|
||||
|
||||
A compiler is required to convert C++ code into machine language. Some popular C++ compilers include:
|
||||
|
||||
- GCC (GNU Compiler Collection) for Linux and macOS, but can also be used on Windows through MinGW
|
||||
- MSVC (Microsoft Visual C++) for Windows
|
||||
* GCC (GNU Compiler Collection) for Linux and macOS, but can also be used on Windows through MinGW
|
||||
* MSVC (Microsoft Visual C++) for Windows
|
||||
|
||||
To install a compiler, simply follow the instructions provided by the respective websites.
|
||||
|
||||
## 2. Configuring an IDE
|
||||
2\. Configuring an IDE
|
||||
----------------------
|
||||
|
||||
An IDE is a software application that provides facilities for programming, such as code editing, debugging, and building. Some popular C++ IDEs include:
|
||||
|
||||
- [@article@Visual Studio](https://visualstudio.microsoft.com/vs/features/cplusplus/) (Windows, macOS)
|
||||
- [@article@Eclipse](https://eclipse.org) (Windows, macOS, Linux)
|
||||
- [@article@Code::Blocks](http://www.codeblocks.org) (Windows, macOS, Linux)
|
||||
* [@article@Visual Studio](https://visualstudio.microsoft.com/vs/features/cplusplus/) (Windows, macOS)
|
||||
* [@article@Eclipse](https://eclipse.org) (Windows, macOS, Linux)
|
||||
* [@article@Code::Blocks](http://www.codeblocks.org) (Windows, macOS, Linux)
|
||||
|
||||
After downloading and installing an IDE, you might need to configure it to use the installed compiler. Check the documentation of the respective IDE for instructions on how to do this.
|
||||
|
||||
## 3. Creating a New C++ Project
|
||||
3\. Creating a New C++ Project
|
||||
------------------------------
|
||||
|
||||
Once you have your IDE and compiler set up, you can create a new C++ project and start writing code. In general, follow these steps to create a new C++ project:
|
||||
|
||||
- Open the IDE and create a new project.
|
||||
- Select the project type (C++ Application or Console Application).
|
||||
- Specify the project name and location.
|
||||
- Let the IDE generate the main.cpp and build files (such as Makefile or CMakeLists.txt) for you.
|
||||
* Open the IDE and create a new project.
|
||||
* Select the project type (C++ Application or Console Application).
|
||||
* Specify the project name and location.
|
||||
* Let the IDE generate the main.cpp and build files (such as Makefile or CMakeLists.txt) for you.
|
||||
|
||||
## Example: Hello World in C++
|
||||
Example: Hello World in C++
|
||||
---------------------------
|
||||
|
||||
Create a new file called `main.cpp` within your project and include this code:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!\n";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, World!\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Then, follow the IDE's instructions to build and run your program. You should see "Hello, World!" displayed in the console.
|
||||
|
||||
## Summary
|
||||
Summary
|
||||
-------
|
||||
|
||||
Setting up C++ involves:
|
||||
|
||||
- Installing a compiler (e.g. GCC, MinGW, or MSVC)
|
||||
- Configuring an IDE (e.g. Visual Studio, Eclipse, or Code::Blocks)
|
||||
- Creating a new C++ project and writing code
|
||||
* Installing a compiler (e.g. GCC, MinGW, or MSVC)
|
||||
* Configuring an IDE (e.g. Visual Studio, Eclipse, or Code::Blocks)
|
||||
* Creating a new C++ project and writing code
|
||||
|
||||
By following these steps, you'll be ready to start developing C++ applications!
|
||||
By following these steps, you'll be ready to start developing C++ applications!
|
||||
@@ -4,42 +4,42 @@ SFINAE is a principle in C++ template metaprogramming that allows the compiler t
|
||||
|
||||
The key idea behind SFINAE is that if a substitution error occurs, it is silently ignored, and the compiler continues to explore other template specializations or overloads. This allows you to write more flexible and generic code, as it enables you to have multiple specializations for different scenarios.
|
||||
|
||||
## Code Example
|
||||
Code Example
|
||||
------------
|
||||
|
||||
Here's an example that demonstrates SFINAE in action:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct foo_impl {
|
||||
void operator()(T t) {
|
||||
std::cout << "Called when T is not arithmetic\n";
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct foo_impl {
|
||||
void operator()(T t) {
|
||||
std::cout << "Called when T is not arithmetic\n";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct foo_impl<T, std::enable_if_t<std::is_arithmetic<T>::value>> {
|
||||
void operator()(T t) {
|
||||
std::cout << "Called when T is arithmetic\n";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void foo(T t) {
|
||||
foo_impl<T>()(t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct foo_impl<T, std::enable_if_t<std::is_arithmetic<T>::value>> {
|
||||
void operator()(T t) {
|
||||
std::cout << "Called when T is arithmetic\n";
|
||||
|
||||
int main() {
|
||||
int a = 5;
|
||||
foo(a); // output: Called when T is arithmetic
|
||||
|
||||
std::string s = "example";
|
||||
foo(s); // output: Called when T is not arithmetic
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void foo(T t) {
|
||||
foo_impl<T>()(t);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int a = 5;
|
||||
foo(a); // output: Called when T is arithmetic
|
||||
|
||||
std::string s = "example";
|
||||
foo(s); // output: Called when T is not arithmetic
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
In this example, we define two `foo_impl` functions are specialized based on the boolean value of `std::is_arithmetic<T>`. The first one is enabled when `T` is an arithmetic type, while the second one is enabled when `T` is not an arithmetic type. The `foo` function then calls the appropriate `foo_impl` specialization based on the result of the type trait.
|
||||
|
||||
When calling `foo(a)` with an integer, the first specialization is selected, and when calling `foo(s)` with a string, the second specialization is selected. If there is no valid specialization, the code would fail to compile.
|
||||
When calling `foo(a)` with an integer, the first specialization is selected, and when calling `foo(s)` with a string, the second specialization is selected. If there is no valid specialization, the code would fail to compile.
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,57 +2,53 @@
|
||||
|
||||
[Spack](https://spack.io/) is a flexible package manager designed to support multiple versions, configurations, platforms, and compilers. It is particularly useful in High Performance Computing (HPC) environments and for those who require fine control over their software stack. Spack is a popular choice in scientific computing due to its support for various platforms such as Linux, macOS, and many supercomputers. It is designed to automatically search for and install dependencies, making it easy to build complex software.
|
||||
|
||||
## Key Features
|
||||
Key Features
|
||||
------------
|
||||
|
||||
- **Multi-Version Support**: Spack allows for the installation of multiple versions of packages, enabling users to work with different configurations depending on their needs.
|
||||
- **Compiler Support**: Spack supports multiple compilers, including GCC, Clang, Intel, PGI, and others, allowing users to choose the best toolchain for their application.
|
||||
- **Platform Support**: Spack can run on Linux, macOS, and various supercomputers, and it can even target multiple architectures within a single package.
|
||||
- **Dependencies**: Spack takes care of dependencies, providing automatic installation and management of required packages.
|
||||
* **Multi-Version Support**: Spack allows for the installation of multiple versions of packages, enabling users to work with different configurations depending on their needs.
|
||||
* **Compiler Support**: Spack supports multiple compilers, including GCC, Clang, Intel, PGI, and others, allowing users to choose the best toolchain for their application.
|
||||
* **Platform Support**: Spack can run on Linux, macOS, and various supercomputers, and it can even target multiple architectures within a single package.
|
||||
* **Dependencies**: Spack takes care of dependencies, providing automatic installation and management of required packages.
|
||||
|
||||
## Basic Usage
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
- To install Spack, clone its Git repository and set up your environment:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/spack/spack.git
|
||||
cd spack
|
||||
. share/spack/setup-env.sh
|
||||
```
|
||||
|
||||
- Install a package using Spack:
|
||||
|
||||
```bash
|
||||
spack install <package-name>
|
||||
```
|
||||
|
||||
For example, to install `hdf5`:
|
||||
|
||||
```bash
|
||||
spack install hdf5
|
||||
```
|
||||
|
||||
- Load a package in your environment:
|
||||
|
||||
```bash
|
||||
spack load <package-name>
|
||||
```
|
||||
|
||||
For example, to load `hdf5`:
|
||||
|
||||
```bash
|
||||
spack load hdf5
|
||||
```
|
||||
|
||||
- List installed packages:
|
||||
|
||||
```bash
|
||||
spack find
|
||||
```
|
||||
|
||||
- Uninstall a package:
|
||||
|
||||
```bash
|
||||
spack uninstall <package-name>
|
||||
```
|
||||
* To install Spack, clone its Git repository and set up your environment:
|
||||
|
||||
git clone https://github.com/spack/spack.git
|
||||
cd spack
|
||||
. share/spack/setup-env.sh
|
||||
|
||||
|
||||
* Install a package using Spack:
|
||||
|
||||
spack install <package-name>
|
||||
|
||||
|
||||
For example, to install `hdf5`:
|
||||
|
||||
spack install hdf5
|
||||
|
||||
|
||||
* Load a package in your environment:
|
||||
|
||||
spack load <package-name>
|
||||
|
||||
|
||||
For example, to load `hdf5`:
|
||||
|
||||
spack load hdf5
|
||||
|
||||
|
||||
* List installed packages:
|
||||
|
||||
spack find
|
||||
|
||||
|
||||
* Uninstall a package:
|
||||
|
||||
spack uninstall <package-name>
|
||||
|
||||
|
||||
|
||||
For more advanced usage, like installing specific versions or using different compilers, consult the [Spack documentation](https://spack.readthedocs.io/).
|
||||
@@ -0,0 +1 @@
|
||||
# undefined
|
||||
@@ -2,59 +2,72 @@
|
||||
|
||||
The C++ Standard Template Library (STL) is a collection of header files that provide several data structures, algorithms, and functions to simplify your C++ coding experience. The primary purpose of the STL is to save time and increase efficiency by providing a ready-to-use set of useful tools. The most commonly used features of the STL can be divided into three main categories: containers, algorithms, and iterators.
|
||||
|
||||
## Containers
|
||||
Containers
|
||||
----------
|
||||
|
||||
Containers are the data structures used for data storage and manipulation in C++. They are classified into four types: sequence containers, associative containers, unordered associative containers, and container adaptors.
|
||||
|
||||
- **Sequence Containers**: These are linear data structures that store elements in a sequential manner. Examples include:
|
||||
- `std::vector`: A dynamic array that grows and shrinks at runtime.
|
||||
```cpp
|
||||
std::vector<int> my_vector;
|
||||
```
|
||||
- `std::list`: A doubly linked list.
|
||||
```cpp
|
||||
std::list<int> my_list;
|
||||
```
|
||||
- `std::deque`: A double-ended queue allowing insertion and deletion at both ends.
|
||||
```cpp
|
||||
std::deque<int> my_deque;
|
||||
```
|
||||
* **Sequence Containers**: These are linear data structures that store elements in a sequential manner. Examples include:
|
||||
|
||||
* `std::vector`: A dynamic array that grows and shrinks at runtime.
|
||||
|
||||
std::vector<int> my_vector;
|
||||
|
||||
|
||||
* `std::list`: A doubly linked list.
|
||||
|
||||
std::list<int> my_list;
|
||||
|
||||
|
||||
* `std::deque`: A double-ended queue allowing insertion and deletion at both ends.
|
||||
|
||||
std::deque<int> my_deque;
|
||||
|
||||
|
||||
* **Associative Containers**: These containers store data in a sorted manner with unique keys. Examples include:
|
||||
|
||||
* `std::set`: A collection of unique elements sorted by keys.
|
||||
|
||||
std::set<int> my_set;
|
||||
|
||||
|
||||
* `std::map`: A collection of key-value pairs sorted by keys.
|
||||
|
||||
std::map<std::string, int> my_map;
|
||||
|
||||
|
||||
* **Unordered Associative Containers**: These containers store data in an unordered manner using hash tables. Examples include:
|
||||
|
||||
* `std::unordered_set`: A collection of unique elements in no specific order.
|
||||
|
||||
std::unordered_set<int> my_unordered_set;
|
||||
|
||||
|
||||
* `std::unordered_map`: A collection of key-value pairs in no specific order.
|
||||
|
||||
std::unordered_map<std::string, int> my_unordered_map;
|
||||
|
||||
|
||||
* **Container Adaptors**: These are containers based on other existing containers. Examples include:
|
||||
|
||||
* `std::stack`: A LIFO data structure based on deque or list.
|
||||
|
||||
std::stack<int> my_stack;
|
||||
|
||||
|
||||
* `std::queue`: A FIFO data structure based on deque or list.
|
||||
|
||||
std::queue<int> my_queue;
|
||||
|
||||
|
||||
* `std::priority_queue`: A sorted queue based on vector or deque.
|
||||
|
||||
std::priority_queue<int> my_priority_queue;
|
||||
|
||||
|
||||
|
||||
- **Associative Containers**: These containers store data in a sorted manner with unique keys. Examples include:
|
||||
- `std::set`: A collection of unique elements sorted by keys.
|
||||
```cpp
|
||||
std::set<int> my_set;
|
||||
```
|
||||
- `std::map`: A collection of key-value pairs sorted by keys.
|
||||
```cpp
|
||||
std::map<std::string, int> my_map;
|
||||
```
|
||||
|
||||
- **Unordered Associative Containers**: These containers store data in an unordered manner using hash tables. Examples include:
|
||||
- `std::unordered_set`: A collection of unique elements in no specific order.
|
||||
```cpp
|
||||
std::unordered_set<int> my_unordered_set;
|
||||
```
|
||||
- `std::unordered_map`: A collection of key-value pairs in no specific order.
|
||||
```cpp
|
||||
std::unordered_map<std::string, int> my_unordered_map;
|
||||
```
|
||||
|
||||
- **Container Adaptors**: These are containers based on other existing containers. Examples include:
|
||||
- `std::stack`: A LIFO data structure based on deque or list.
|
||||
```cpp
|
||||
std::stack<int> my_stack;
|
||||
```
|
||||
- `std::queue`: A FIFO data structure based on deque or list.
|
||||
```cpp
|
||||
std::queue<int> my_queue;
|
||||
```
|
||||
- `std::priority_queue`: A sorted queue based on vector or deque.
|
||||
```cpp
|
||||
std::priority_queue<int> my_priority_queue;
|
||||
```
|
||||
|
||||
## Algorithms
|
||||
Algorithms
|
||||
----------
|
||||
|
||||
The STL provides several generic algorithms that can be used to perform various operations on the data stored in containers. They are divided into five categories: non-modifying sequence algorithms, modifying sequence algorithms, sorting algorithms, sorted range algorithms, and numeric algorithms.
|
||||
|
||||
@@ -62,12 +75,12 @@ Some examples include `std::find`, `std::replace`, `std::sort`, and `std::binary
|
||||
|
||||
For example, to sort a vector, you can use the following code:
|
||||
|
||||
```cpp
|
||||
std::vector<int> my_vec = {4, 2, 5, 1, 3};
|
||||
std::sort(my_vec.begin(), my_vec.end());
|
||||
```
|
||||
std::vector<int> my_vec = {4, 2, 5, 1, 3};
|
||||
std::sort(my_vec.begin(), my_vec.end());
|
||||
|
||||
|
||||
## Iterators
|
||||
Iterators
|
||||
---------
|
||||
|
||||
Iterators are a fundamental concept in the STL, as they provide a unified way to access elements in containers. Iterators can be thought of as an advanced form of pointers.
|
||||
|
||||
@@ -75,16 +88,15 @@ Each container has its own iterator type, which can be used to traverse elements
|
||||
|
||||
For example, to iterate through a vector and print its elements, you can use the following code:
|
||||
|
||||
```cpp
|
||||
std::vector<int> my_vec = {1, 2, 3, 4, 5};
|
||||
for (auto it = my_vec.begin(); it != my_vec.end(); ++it) {
|
||||
std::cout << *it << " ";
|
||||
}
|
||||
```
|
||||
std::vector<int> my_vec = {1, 2, 3, 4, 5};
|
||||
for (auto it = my_vec.begin(); it != my_vec.end(); ++it) {
|
||||
std::cout << *it << " ";
|
||||
}
|
||||
|
||||
|
||||
This is just a brief overview of the C++ Standard Template Library. There are many other features and functions available in the STL, and familiarizing yourself with them is crucial for efficient C++ programming.
|
||||
|
||||
Learn more from the following resources:
|
||||
Visit the following resources to learn more:
|
||||
|
||||
- [@video@C++ Standard Template Library (STL) Short Overview](https://www.youtube.com/watch?v=Id6ZEb_Lg58)
|
||||
- [@book@Mastering STL in C++23: New Features, Updates, and Best Practices](https://simplifycpp.org/books/Mastering_STL.pdf)
|
||||
- [@video@C++ Standard Template Library (STL) Short Overview](https://www.youtube.com/watch?v=Id6ZEb_Lg58)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user