C++ is a powerful, high-performance programming language that supports procedural, object-oriented, and generic programming. It's widely used in game development, systems programming, embedded systems, and high-performance applications.
Why C++? C++ provides low-level memory manipulation while offering high-level abstractions, making it ideal for performance-critical applications.
Your First C++ Program
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
Structure Breakdown:
#include <iostream> - Includes input/output library
using namespace std; - Uses standard namespace
int main() - Entry point of the program
cout - Output to console
return 0; - Indicates successful execution
Variables and Data Types
C++ is a strongly-typed language, meaning you must declare variable types explicitly.
// Integer types
int age = 25; // Standard integer
short smallNum = 100; // Smaller range
long bigNum = 1000000L; // Larger range
long long hugeNum = 1000000000LL;
// Floating-point types
float price = 19.99f; // Single precision
double pi = 3.14159265359; // Double precision
// Character and boolean
char grade = 'A'; // Single character
bool isStudent = true; // Boolean (true/false)
// String (requires <string> library)
string name = "Alice";
// Constants
const int MAX_SCORE = 100;
const double TAX_RATE = 0.08;
Input and Output
#include <iostream>
#include <string>
using namespace std;
int main() {
string name;
int age;
// Input
cout << "Enter your name: ";
getline(cin, name); // Read entire line with spaces
cout << "Enter your age: ";
cin >> age;
// Output
cout << "Hello, " << name << "!" << endl;
cout << "You are " << age << " years old." << endl;
return 0;
}
Operators
// Arithmetic operators
int a = 10, b = 3;
int sum = a + b; // 13
int diff = a - b; // 7
int product = a * b; // 30
int quotient = a / b; // 3 (integer division)
int remainder = a % b; // 1 (modulus)
// Increment and decrement
int x = 5;
x++; // x is now 6 (post-increment)
++x; // x is now 7 (pre-increment)
x--; // x is now 6 (post-decrement)
// Comparison operators
bool result = (10 == 10); // true (equal to)
result = (10 != 5); // true (not equal to)
result = (10 > 5); // true (greater than)
result = (10 < 5); // false (less than)
result = (10 >= 10); // true (greater or equal)
// Logical operators
bool p = true, q = false;
bool andResult = p && q; // false (AND)
bool orResult = p || q; // true (OR)
bool notResult = !p; // false (NOT)
Type Casting
// Implicit casting (automatic)
int num = 10;
double decimal = num; // int to double (10.0)
// Explicit casting (manual)
double pi = 3.14159;
int intPi = (int)pi; // C-style cast: 3
int intPi2 = int(pi); // Functional cast: 3
// Static cast (C++ style - recommended)
double value = 5.7;
int rounded = static_cast<int>(value); // 5
// Example with division
int a = 7, b = 2;
double result = (double)a / b; // 3.5 (not 3)
Common Pitfall: Integer division truncates the decimal part. To get a decimal result, cast at least one operand to double: (double)a / b
Comments
// Single-line comment
/*
Multi-line comment
Can span multiple lines
*/
int x = 5; // Inline comment
Basic Syntax Rules
Every statement ends with a semicolon ;
C++ is case-sensitive (age and Age are different)
Variable names must start with a letter or underscore
Use curly braces {} to define code blocks
Indentation is not required but strongly recommended for readability
Best Practice: Use meaningful variable names like studentCount instead of sc. Follow camelCase or snake_case consistently.
Test Your Knowledge - Lesson 1
1. Which header file is required for input/output operations in C++?
2. What is the result of 7 / 2 in C++ (both integers)?
3. Which keyword is used to declare a constant in C++?
Lesson 2: Control Flow
If/Else Statements
Conditional statements allow your program to make decisions based on conditions.
// Simple if statement
int age = 18;
if (age >= 18) {
cout << "You are an adult" << endl;
}
// If-else statement
int score = 75;
if (score >= 60) {
cout << "You passed!" << endl;
} else {
cout << "You failed" << endl;
}
// If-else if-else ladder
int grade = 85;
if (grade >= 90) {
cout << "Grade: A" << endl;
} else if (grade >= 80) {
cout << "Grade: B" << endl;
} else if (grade >= 70) {
cout << "Grade: C" << endl;
} else if (grade >= 60) {
cout << "Grade: D" << endl;
} else {
cout << "Grade: F" << endl;
}
// Nested if statements
int x = 10, y = 20;
if (x > 0) {
if (y > 0) {
cout << "Both are positive" << endl;
}
}
Ternary Operator
// Ternary operator: condition ? value_if_true : value_if_false
int age = 20;
string status = (age >= 18) ? "Adult" : "Minor";
cout << status << endl; // Output: Adult
// Can be used in expressions
int a = 5, b = 10;
int max = (a > b) ? a : b;
cout << "Max: " << max << endl; // Output: Max: 10
Switch Statement
int day = 3;
switch (day) {
case 1:
cout << "Monday" << endl;
break;
case 2:
cout << "Tuesday" << endl;
break;
case 3:
cout << "Wednesday" << endl;
break;
case 4:
cout << "Thursday" << endl;
break;
case 5:
cout << "Friday" << endl;
break;
case 6:
case 7:
cout << "Weekend" << endl;
break;
default:
cout << "Invalid day" << endl;
}
// Character switch
char operation = '+';
int a = 10, b = 5;
switch (operation) {
case '+':
cout << a + b << endl;
break;
case '-':
cout << a - b << endl;
break;
case '*':
cout << a * b << endl;
break;
case '/':
cout << a / b << endl;
break;
default:
cout << "Invalid operation" << endl;
}
Important: Always include break statements in switch cases to prevent fall-through behavior, unless intentional.
For Loops
// Basic for loop
for (int i = 0; i < 5; i++) {
cout << i << " "; // Output: 0 1 2 3 4
}
cout << endl;
// Loop with different step
for (int i = 0; i < 10; i += 2) {
cout << i << " "; // Output: 0 2 4 6 8
}
cout << endl;
// Countdown loop
for (int i = 5; i > 0; i--) {
cout << i << " "; // Output: 5 4 3 2 1
}
cout << endl;
// Nested loops
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
cout << i * j << " ";
}
cout << endl;
}
// Range-based for loop (C++11)
int numbers[] = {1, 2, 3, 4, 5};
for (int num : numbers) {
cout << num << " ";
}
cout << endl;
While Loops
// Basic while loop
int count = 0;
while (count < 5) {
cout << count << " ";
count++;
}
cout << endl; // Output: 0 1 2 3 4
// While loop with condition
int sum = 0;
int i = 1;
while (i <= 10) {
sum += i;
i++;
}
cout << "Sum: " << sum << endl; // Sum: 55
// User input loop
int number;
cout << "Enter positive numbers (0 to stop):" << endl;
while (true) {
cin >> number;
if (number == 0) break;
cout << "You entered: " << number << endl;
}
Do-While Loops
// Do-while executes at least once
int num = 0;
do {
cout << num << " ";
num++;
} while (num < 5);
cout << endl; // Output: 0 1 2 3 4
// Menu example
int choice;
do {
cout << "\n--- Menu ---" << endl;
cout << "1. Option 1" << endl;
cout << "2. Option 2" << endl;
cout << "3. Exit" << endl;
cout << "Enter choice: ";
cin >> choice;
switch (choice) {
case 1:
cout << "You selected Option 1" << endl;
break;
case 2:
cout << "You selected Option 2" << endl;
break;
case 3:
cout << "Exiting..." << endl;
break;
default:
cout << "Invalid choice" << endl;
}
} while (choice != 3);
Break and Continue
// Break - exit the loop
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // Exit loop when i is 5
}
cout << i << " ";
}
cout << endl; // Output: 0 1 2 3 4
// Continue - skip current iteration
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
cout << i << " ";
}
cout << endl; // Output: 1 3 5 7 9
// Finding prime number
int n = 29;
bool isPrime = true;
for (int i = 2; i < n; i++) {
if (n % i == 0) {
isPrime = false;
break; // No need to check further
}
}
cout << n << (isPrime ? " is prime" : " is not prime") << endl;
Functions
// Function declaration
void greet(); // Function prototype
// Function definition
void greet() {
cout << "Hello, World!" << endl;
}
// Function with parameters
void greetPerson(string name) {
cout << "Hello, " << name << "!" << endl;
}
// Function with return value
int add(int a, int b) {
return a + b;
}
// Function with default parameters
int multiply(int a, int b = 2) {
return a * b;
}
// Main function
int main() {
greet(); // Hello, World!
greetPerson("Alice"); // Hello, Alice!
int sum = add(5, 3);
cout << "Sum: " << sum << endl; // Sum: 8
cout << multiply(5) << endl; // 10 (uses default)
cout << multiply(5, 3) << endl; // 15
return 0;
}
Function Overloading
// Multiple functions with same name but different parameters
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
int main() {
cout << add(5, 3) << endl; // Calls int version: 8
cout << add(2.5, 3.7) << endl; // Calls double version: 6.2
cout << add(1, 2, 3) << endl; // Calls 3-parameter version: 6
return 0;
}
Best Practice: Functions should perform a single, well-defined task. Use descriptive names that indicate what the function does.
Test Your Knowledge - Lesson 2
1. What is the output of this code? for (int i = 1; i < 4; i++) { cout << i * 2 << " "; }
2. Which statement prevents fall-through in a switch statement?
3. What is the main difference between while and do-while loops?
Lesson 3: Arrays and Strings
Arrays
Arrays are collections of elements of the same type stored in contiguous memory locations.
// Array declaration and initialization
int numbers[5]; // Declares array of 5 integers
// Initialize with values
int scores[5] = {85, 90, 78, 92, 88};
// Size can be omitted if initialized
int values[] = {1, 2, 3, 4, 5}; // Size is 5
// Accessing elements (0-indexed)
cout << scores[0] << endl; // 85 (first element)
cout << scores[4] << endl; // 88 (last element)
// Modifying elements
scores[2] = 95;
// Looping through array
for (int i = 0; i < 5; i++) {
cout << scores[i] << " ";
}
cout << endl;
// Range-based for loop (C++11)
for (int score : scores) {
cout << score << " ";
}
cout << endl;
Warning: Array indices start at 0. Accessing out-of-bounds indices (negative or >= size) causes undefined behavior and may crash your program.
Multidimensional Arrays
// 2D array (matrix)
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Accessing elements
cout << matrix[0][0] << endl; // 1 (first row, first column)
cout << matrix[1][2] << endl; // 6 (second row, third column)
// Nested loop to print matrix
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
// 3D array
int cube[2][3][4]; // 2 layers, 3 rows, 4 columns
Array Operations
// Finding max element
int arr[] = {45, 23, 67, 12, 89, 34};
int size = sizeof(arr) / sizeof(arr[0]); // Calculate array size
int maxVal = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
cout << "Maximum: " << maxVal << endl; // 89
// Sum of array elements
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
cout << "Sum: " << sum << endl;
// Average
double average = (double)sum / size;
cout << "Average: " << average << endl;
// Reversing an array
int start = 0, end = size - 1;
while (start < end) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
C-Style Strings
#include <cstring> // For C-string functions
// C-style strings (character arrays)
char name[20] = "Alice";
char greeting[] = "Hello";
// String functions
cout << "Length: " << strlen(name) << endl; // 5
char dest[20];
strcpy(dest, name); // Copy string
strcat(dest, " Smith"); // Concatenate
cout << dest << endl; // Alice Smith
int result = strcmp("abc", "abd"); // Compare strings
// Returns: <0 if first < second, 0 if equal, >0 if first > second
Security Warning: C-style strings are prone to buffer overflows. Always ensure the destination array is large enough when using strcpy and strcat.
C++ String Class
The C++ string class is safer and more convenient than C-style strings.
#include <string>
using namespace std;
// Creating strings
string name = "Alice";
string greeting = "Hello";
string empty;
// String operations
string fullName = name + " Smith"; // Concatenation
cout << fullName << endl;
// String methods
cout << name.length() << endl; // 5 (length)
cout << name.size() << endl; // 5 (same as length)
cout << name.empty() << endl; // false
// Accessing characters
char first = name[0]; // 'A'
char last = name[name.length()-1]; // 'e'
// Modifying strings
name[0] = 'a'; // "alice"
name += " Smith"; // Append
cout << name << endl; // "alice Smith"
// Substring
string text = "Hello, World!";
string sub = text.substr(0, 5); // "Hello"
string world = text.substr(7, 5); // "World"
// Finding substrings
size_t pos = text.find("World"); // Returns position (7)
if (pos != string::npos) {
cout << "Found at: " << pos << endl;
}
// Replace
text.replace(7, 5, "C++"); // "Hello, C++!"
// Insert and erase
text.insert(5, " there"); // Insert at position 5
text.erase(0, 6); // Erase 6 chars from position 0
String Input/Output
// Reading strings
string name;
// cin stops at whitespace
cin >> name; // Reads only first word
// getline reads entire line
getline(cin, name);
// Reading with delimiter
getline(cin, name, ','); // Read until comma
// Converting between types
string numStr = "123";
int num = stoi(numStr); // String to int
double d = stod("3.14"); // String to double
string str = to_string(456); // Int to string
cout << str << endl; // "456"
String Comparison
string s1 = "apple";
string s2 = "banana";
// Using operators
if (s1 == s2) {
cout << "Equal" << endl;
}
if (s1 < s2) {
cout << s1 << " comes before " << s2 << endl;
}
// Using compare method
int result = s1.compare(s2);
if (result == 0) {
cout << "Equal" << endl;
} else if (result < 0) {
cout << s1 << " is less than " << s2 << endl;
} else {
cout << s1 << " is greater than " << s2 << endl;
}
Best Practice: Prefer C++ string class over C-style strings. It's safer, easier to use, and provides many useful methods.
Character Functions
#include <cctype>
char ch = 'a';
// Character testing
isalpha(ch); // true (is alphabetic)
isdigit(ch); // false (is digit)
isalnum(ch); // true (is alphanumeric)
isspace(ch); // false (is whitespace)
isupper(ch); // false (is uppercase)
islower(ch); // true (is lowercase)
// Character conversion
char upper = toupper(ch); // 'A'
char lower = tolower('B'); // 'b'
// Example: Count vowels
string text = "Hello World";
int vowelCount = 0;
for (char c : text) {
c = tolower(c);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
vowelCount++;
}
}
cout << "Vowels: " << vowelCount << endl;
Test Your Knowledge - Lesson 3
1. Array indices in C++ start at:
2. Which function returns the length of a C++ string?
3. How do you read an entire line of text including spaces?
Lesson 4: Pointers and References
Introduction to Pointers
Pointers are variables that store memory addresses. They are one of the most powerful features of C++.
Key Concept: A pointer "points to" a memory location where data is stored. The & operator gets an address, and * dereferences (accesses the value at) an address.
Pointer Basics
// Declaring pointers
int x = 10;
int* ptr; // Pointer to int (uninitialized)
int* ptr2 = &x; // Pointer initialized with address of x
// Address-of operator (&)
cout << &x << endl; // Prints address of x (e.g., 0x7ffd...)
// Dereference operator (*)
cout << *ptr2 << endl; // Prints value at address (10)
// Pointer assignment
ptr = &x; // ptr now points to x
*ptr = 20; // Changes x to 20 through pointer
cout << x << endl; // 20
// Different pointer types
double d = 3.14;
double* dPtr = &d;
char c = 'A';
char* cPtr = &c;
// Null pointer
int* nullPtr = nullptr; // C++11 (better than NULL)
// int* nullPtr = NULL; // Old style
Critical Warning: Dereferencing an uninitialized or null pointer causes undefined behavior and often crashes. Always initialize pointers before use.
Pointers and Arrays
// Arrays and pointers are closely related
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr; // Array name is pointer to first element
// Access elements using pointer
cout << *ptr << endl; // 10 (first element)
cout << *(ptr + 1) << endl; // 20 (second element)
cout << *(ptr + 2) << endl; // 30 (third element)
// Pointer arithmetic
ptr++; // Move to next element
cout << *ptr << endl; // 20
// Array notation with pointers
ptr = arr;
cout << ptr[0] << endl; // 10
cout << ptr[2] << endl; // 30
// Traversing array with pointer
ptr = arr;
for (int i = 0; i < 5; i++) {
cout << *(ptr + i) << " ";
}
cout << endl;
Pointers to Pointers
int x = 100;
int* ptr = &x; // Pointer to int
int** ptr2 = &ptr; // Pointer to pointer to int
cout << x << endl; // 100 (direct value)
cout << *ptr << endl; // 100 (one dereference)
cout << **ptr2 << endl; // 100 (two dereferences)
// Modifying through pointer to pointer
**ptr2 = 200;
cout << x << endl; // 200
Dynamic Memory Allocation
// Allocating single variable
int* ptr = new int; // Allocate memory for one int
*ptr = 42;
cout << *ptr << endl; // 42
delete ptr; // Free memory (important!)
// Allocating with initialization
int* ptr2 = new int(100);
cout << *ptr2 << endl; // 100
delete ptr2;
// Allocating arrays
int size = 5;
int* arr = new int[size]; // Dynamic array
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
// Use the array
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
delete[] arr; // Free array memory (note the [])
// 2D dynamic array
int rows = 3, cols = 4;
int** matrix = new int*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols];
}
// Use matrix...
matrix[0][0] = 1;
// Free memory
for (int i = 0; i < rows; i++) {
delete[] matrix[i];
}
delete[] matrix;
Memory Leak Warning: Every new must have a corresponding delete. Use delete[] for arrays. Failing to free memory causes memory leaks.
References
References are aliases for existing variables. Unlike pointers, they cannot be null and must be initialized.
// Reference basics
int x = 10;
int& ref = x; // ref is a reference (alias) to x
cout << ref << endl; // 10
ref = 20; // Changes x to 20
cout << x << endl; // 20
// References must be initialized
// int& ref2; // Error! Must initialize
// References cannot be reassigned
int y = 30;
ref = y; // This assigns y's value to x, not reassigns ref
cout << x << endl; // 30
cout << y << endl; // 30
Pass by Value vs Pass by Reference
// Pass by value (copy)
void incrementByValue(int x) {
x++; // Only modifies the copy
}
// Pass by reference
void incrementByReference(int& x) {
x++; // Modifies the original
}
// Pass by pointer
void incrementByPointer(int* x) {
(*x)++; // Modifies the original
}
int main() {
int num = 10;
incrementByValue(num);
cout << num << endl; // 10 (unchanged)
incrementByReference(num);
cout << num << endl; // 11 (changed)
incrementByPointer(&num);
cout << num << endl; // 12 (changed)
return 0;
}
Const Pointers and References
int x = 10;
int y = 20;
// Pointer to constant (can't change value)
const int* ptr1 = &x;
// *ptr1 = 20; // Error! Can't modify value
ptr1 = &y; // OK - can change pointer
// Constant pointer (can't change address)
int* const ptr2 = &x;
*ptr2 = 30; // OK - can modify value
// ptr2 = &y; // Error! Can't change pointer
// Constant pointer to constant
const int* const ptr3 = &x;
// *ptr3 = 40; // Error! Can't modify value
// ptr3 = &y; // Error! Can't change pointer
// Const reference (common for function parameters)
void printValue(const int& x) {
cout << x << endl;
// x = 100; // Error! Can't modify
}
Best Practice: Use references for function parameters when you want to avoid copying large objects. Use const references when the function shouldn't modify the parameter.
Common Pointer Pitfalls
// Dangling pointer
int* ptr = new int(10);
delete ptr;
// *ptr = 20; // Error! Dangling pointer (accessing freed memory)
ptr = nullptr; // Good practice after delete
// Memory leak
void leakyFunction() {
int* ptr = new int(100);
// Forgot to delete!
} // Memory leaked when function returns
// Wild pointer
int* ptr; // Uninitialized
// *ptr = 5; // Error! Wild pointer (random address)
Return by Reference
// Return reference to allow chaining
class Counter {
int count;
public:
Counter() : count(0) {}
Counter& increment() {
count++;
return *this; // Return reference to self
}
int getValue() {
return count;
}
};
int main() {
Counter c;
c.increment().increment().increment(); // Chaining
cout << c.getValue() << endl; // 3
return 0;
}
Test Your Knowledge - Lesson 4
1. What operator is used to get the address of a variable?
2. Which is used to free dynamically allocated array memory?
3. What is the main difference between pointers and references?
Lesson 5: Object-Oriented Programming
Introduction to OOP
Object-Oriented Programming organizes code into classes and objects, encapsulating data and behavior together.
Four Pillars of OOP: Encapsulation, Abstraction, Inheritance, and Polymorphism.
Classes and Objects
// Class definition
class Dog {
public:
// Data members (attributes)
string name;
int age;
// Member functions (methods)
void bark() {
cout << name << " says: Woof!" << endl;
}
void displayInfo() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
int main() {
// Creating objects
Dog dog1;
dog1.name = "Buddy";
dog1.age = 3;
dog1.bark(); // Buddy says: Woof!
dog1.displayInfo(); // Name: Buddy, Age: 3
Dog dog2;
dog2.name = "Max";
dog2.age = 5;
dog2.bark(); // Max says: Woof!
return 0;
}
Encapsulation: Keep data members private and provide public getter/setter methods. This protects data from invalid modifications.
Inheritance
// Base class (parent)
class Animal {
protected:
string name;
int age;
public:
Animal(string n, int a) : name(n), age(a) {}
void eat() {
cout << name << " is eating" << endl;
}
void sleep() {
cout << name << " is sleeping" << endl;
}
virtual void makeSound() {
cout << "Some generic sound" << endl;
}
};
// Derived class (child)
class Dog : public Animal {
private:
string breed;
public:
Dog(string n, int a, string b) : Animal(n, a), breed(b) {}
void makeSound() override {
cout << name << " barks: Woof!" << endl;
}
void fetch() {
cout << name << " is fetching the ball" << endl;
}
};
class Cat : public Animal {
public:
Cat(string n, int a) : Animal(n, a) {}
void makeSound() override {
cout << name << " meows: Meow!" << endl;
}
};
int main() {
Dog dog("Buddy", 3, "Golden Retriever");
dog.eat(); // Inherited from Animal
dog.makeSound(); // Overridden in Dog
dog.fetch(); // Dog-specific method
Cat cat("Whiskers", 2);
cat.sleep(); // Inherited from Animal
cat.makeSound(); // Overridden in Cat
return 0;
}
Polymorphism
// Runtime polymorphism with virtual functions
class Shape {
public:
virtual double getArea() {
return 0;
}
virtual void display() {
cout << "This is a shape" << endl;
}
// Virtual destructor important for polymorphism
virtual ~Shape() {}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() override {
return 3.14159 * radius * radius;
}
void display() override {
cout << "Circle with radius " << radius << endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() override {
return width * height;
}
void display() override {
cout << "Rectangle " << width << "x" << height << endl;
}
};
int main() {
// Polymorphism: base class pointer to derived objects
Shape* shapes[3];
shapes[0] = new Circle(5.0);
shapes[1] = new Rectangle(4.0, 6.0);
shapes[2] = new Circle(3.0);
// Calls correct overridden method for each object
for (int i = 0; i < 3; i++) {
shapes[i]->display();
cout << "Area: " << shapes[i]->getArea() << endl;
cout << endl;
}
// Clean up
for (int i = 0; i < 3; i++) {
delete shapes[i];
}
return 0;
}
Virtual Functions: Use virtual keyword in base class to enable runtime polymorphism. The correct method is called based on the actual object type, not pointer type.
Abstract Classes and Pure Virtual Functions
// Abstract class (cannot be instantiated)
class Shape {
public:
// Pure virtual function
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
virtual ~Shape() {}
};
class Triangle : public Shape {
private:
double a, b, c;
public:
Triangle(double side1, double side2, double side3)
: a(side1), b(side2), c(side3) {}
// Must implement all pure virtual functions
double getArea() override {
double s = (a + b + c) / 2;
return sqrt(s * (s-a) * (s-b) * (s-c));
}
double getPerimeter() override {
return a + b + c;
}
};
int main() {
// Shape s; // Error! Cannot instantiate abstract class
Triangle t(3, 4, 5);
cout << "Area: " << t.getArea() << endl;
cout << "Perimeter: " << t.getPerimeter() << endl;
return 0;
}
Friend Functions and Classes
class Box {
private:
double width;
public:
Box(double w) : width(w) {}
// Friend function can access private members
friend void printWidth(Box b);
// Friend class
friend class BoxPrinter;
};
void printWidth(Box b) {
cout << "Width: " << b.width << endl;
}
class BoxPrinter {
public:
void print(Box b) {
cout << "Box width: " << b.width << endl;
}
};
int main() {
Box box(10.5);
printWidth(box);
BoxPrinter printer;
printer.print(box);
return 0;
}
Static Members
class Counter {
private:
static int count; // Static member variable
int id;
public:
Counter() {
id = ++count;
}
// Static member function
static int getCount() {
return count;
}
int getId() {
return id;
}
};
// Initialize static member outside class
int Counter::count = 0;
int main() {
cout << "Initial count: " << Counter::getCount() << endl;
Counter c1, c2, c3;
cout << "Count: " << Counter::getCount() << endl; // 3
cout << "c1 ID: " << c1.getId() << endl; // 1
cout << "c2 ID: " << c2.getId() << endl; // 2
cout << "c3 ID: " << c3.getId() << endl; // 3
return 0;
}
Remember: Static members are shared by all objects of the class. They exist even before any object is created.
Test Your Knowledge - Lesson 5
1. What access specifier should be used for data members to ensure encapsulation?
2. Which keyword enables runtime polymorphism in C++?
3. What makes a class abstract in C++?
Lesson 6: Advanced C++
Standard Template Library (STL)
The STL provides powerful, reusable components: containers, iterators, and algorithms.
Vectors
#include <vector>
using namespace std;
// Creating vectors
vector<int> numbers; // Empty vector
vector<int> scores(5); // 5 elements, default value 0
vector<int> values(5, 10); // 5 elements, all set to 10
vector<int> nums = {1, 2, 3, 4, 5}; // Initialize with values
// Adding elements
numbers.push_back(10); // Add to end
numbers.push_back(20);
numbers.push_back(30);
// Accessing elements
cout << numbers[0] << endl; // 10 (no bounds checking)
cout << numbers.at(1) << endl; // 20 (with bounds checking)
cout << numbers.front() << endl; // First element
cout << numbers.back() << endl; // Last element
// Size and capacity
cout << numbers.size() << endl; // Number of elements
cout << numbers.capacity() << endl; // Allocated space
cout << numbers.empty() << endl; // Check if empty
// Removing elements
numbers.pop_back(); // Remove last element
numbers.clear(); // Remove all elements
// Iterating
vector<int> v = {1, 2, 3, 4, 5};
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
// Range-based for loop
for (int num : v) {
cout << num << " ";
}
cout << endl;
// Iterator
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
#include <memory>
// unique_ptr - exclusive ownership
unique_ptr<int> ptr1(new int(10));
// Or better (C++14):
auto ptr2 = make_unique<int>(20);
cout << *ptr1 << endl; // 10
*ptr1 = 100;
// unique_ptr cannot be copied
// unique_ptr<int> ptr3 = ptr1; // Error!
// But can be moved
unique_ptr<int> ptr4 = move(ptr1); // ptr1 is now null
// shared_ptr - shared ownership
shared_ptr<int> sp1 = make_shared<int>(42);
shared_ptr<int> sp2 = sp1; // Both point to same object
cout << sp1.use_count() << endl; // 2 (reference count)
sp1.reset(); // sp1 releases ownership
cout << sp2.use_count() << endl; // 1
// weak_ptr - non-owning reference
shared_ptr<int> sp = make_shared<int>(100);
weak_ptr<int> wp = sp;
if (auto temp = wp.lock()) { // Convert to shared_ptr
cout << *temp << endl;
}
// Smart pointer with custom class
class Person {
public:
string name;
Person(string n) : name(n) {
cout << name << " created" << endl;
}
~Person() {
cout << name << " destroyed" << endl;
}
};
auto person = make_unique<Person>("Alice");
// Automatically deleted when person goes out of scope
Best Practice: Use smart pointers instead of raw pointers with new/delete. Prefer unique_ptr by default, use shared_ptr when sharing ownership is necessary.
Lambda Expressions (C++11)
// Basic lambda
auto greet = []() {
cout << "Hello!" << endl;
};
greet();
// Lambda with parameters
auto add = [](int a, int b) {
return a + b;
};
cout << add(5, 3) << endl; // 8
// Lambda with capture
int x = 10;
auto addX = [x](int y) {
return x + y;
};
cout << addX(5) << endl; // 15
// Capture by reference
int count = 0;
auto increment = [&count]() {
count++;
};
increment();
increment();
cout << count << endl; // 2
// Capture all by value/reference
int a = 1, b = 2;
auto lambda1 = [=]() { return a + b; }; // Capture all by value
auto lambda2 = [&]() { a++; b++; }; // Capture all by reference
// Using with STL algorithms
vector<int> nums = {1, 2, 3, 4, 5, 6};
// for_each
for_each(nums.begin(), nums.end(), [](int n) {
cout << n * 2 << " ";
});
cout << endl;
// sort with custom comparator
sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b; // Descending order
});
#include <fstream>
// Writing to file
ofstream outFile("output.txt");
if (outFile.is_open()) {
outFile << "Hello, World!" << endl;
outFile << "C++ File I/O" << endl;
outFile.close();
}
// Reading from file
ifstream inFile("input.txt");
if (inFile.is_open()) {
string line;
while (getline(inFile, line)) {
cout << line << endl;
}
inFile.close();
}
// Reading word by word
ifstream file("data.txt");
string word;
while (file >> word) {
cout << word << " ";
}
file.close();
// Append to file
ofstream appendFile("log.txt", ios::app);
appendFile << "New log entry" << endl;
appendFile.close();
// Binary file operations
ofstream binFile("data.bin", ios::binary);
int numbers[] = {1, 2, 3, 4, 5};
binFile.write((char*)numbers, sizeof(numbers));
binFile.close();
Modern C++ Features: C++11 and later introduced many features: auto keyword, range-based for loops, smart pointers, lambda expressions, and more. These make C++ code cleaner and safer.
Test Your Knowledge - Lesson 6
1. Which smart pointer should be used for exclusive ownership?
2. Which STL container maintains sorted unique elements?
3. What symbol is used to capture all variables by reference in a lambda?