3. Programming Language and Its Applications (ACtE03)
3.1 Introduction to C Programming
C is a powerful, general-purpose programming language developed by Dennis Ritchie at Bell Labs. It is known for its efficiency, low-level memory access, and portability, making it ideal for system programming and embedded systems.
C Tokens
C tokens are the smallest individual units in a C program. They are categorized into six types:
- Keywords: Reserved words with predefined meanings (e.g.,
int,if,while,for,return). There are 32 keywords in ANSI C. - Identifiers: User-defined names given to variables, functions, arrays, etc. They must start with an alphabet or underscore, followed by alphabets, digits, or underscores. Case-sensitive (e.g.,
myVariable,_count,addNumbers). - Constants: Fixed values that do not change during program execution (e.g.,
10(integer),3.14(float),'A'(character),"Hello"(string)). - Strings: A sequence of characters enclosed in double quotes (e.g.,
"C Programming"). Strings are terminated by a null character (\0). - Operators: Symbols that perform operations on operands (e.g.,
+,-,*,/,=,==). - Special Symbols: Characters with specific meanings (e.g.,
{}(block),()(function/expression),[](array),;(statement terminator),#(preprocessor directive)).
Operators with Precedence Table
Operators are symbols that tell the compiler to perform specific mathematical or logical manipulations. C supports a rich set of operators:
- Arithmetic Operators:
+(addition),-(subtraction),*(multiplication),/(division),%(modulo).int a = 10, b = 3; int sum = a + b; // 13 int diff = a - b; // 7 int prod = a * b; // 30 int quot = a / b; // 3 int rem = a % b; // 1 - Relational Operators: Used for comparison; return 1 (true) or 0 (false).
==(equal to),!=(not equal to),>(greater than),<(less than),>=(greater than or equal to),<=(less than or equal to).int x = 5, y = 10; int isEqual = (x == y); // 0 (false) int isGreater = (y > x); // 1 (true) - Logical Operators: Combine or negate relational expressions.
&&(logical AND),||(logical OR),!(logical NOT).int age = 20, hasLicense = 1; int canDrive = (age >= 18 && hasLicense); // 1 (true) int canVote = (age >= 18 || hasLicense); // 1 (true) int notTrue = !(age < 18); // 1 (true) - Bitwise Operators: Operate on individual bits.
&(bitwise AND),|(bitwise OR),^(bitwise XOR),~(bitwise NOT),<<(left shift),>>(right shift).int p = 5; // 0101 int q = 3; // 0011 int and_res = p & q; // 0001 (1) int or_res = p | q; // 0111 (7) int xor_res = p ^ q; // 0110 (6) int not_res = ~p; // 1010 (-6, for 8-bit signed) int left_shift = p << 1; // 1010 (10) - Assignment Operators: Assign values to variables.
=(simple assignment),+=,-=,*=,/=,%=(compound assignment).int num = 10; num += 5; // num is now 15 (num = num + 5) - Ternary Operator (Conditional Operator):
condition ? expression1 : expression2;If condition is true, expression1 is evaluated; otherwise, expression2.int marks = 75; char grade = (marks >= 60) ? 'A' : 'B'; // grade is 'A'
Operator Precedence (Simplified)
| Category | Operators | Associativity |
|---|---|---|
| Postfix | () [] . -> ++ -- |
Left-to-right |
| Unary | + - ! ~ ++ -- (type)* & sizeof |
Right-to-left |
| Multiplicative | * / % |
Left-to-right |
| Additive | + - |
Left-to-right |
| Shift | << >> |
Left-to-right |
| Relational | < <= > >= |
Left-to-right |
| Equality | == != |
Left-to-right |
| Bitwise AND | & |
Left-to-right |
| Bitwise XOR | ^ |
Left-to-right |
| Bitwise OR | | |
Left-to-right |
| Logical AND | && |
Left-to-right |
| Logical OR | || |
Left-to-right |
| Conditional | ? : |
Right-to-left |
| Assignment | = += -= *= /= %= &= ^= |= <<= >>= |
Right-to-left |
| Comma | , |
Left-to-right |
Formatted Input/Output (printf, scanf)
Formatted I/O functions allow reading and writing data in a specific format using format specifiers.
printf(): Used to print formatted output to the console.#include <stdio.h> int main() { int age = 25; float height = 1.75; char initial = 'J'; printf("Age: %d, Height: %.2f, Initial: %c\n", age, height, initial); return 0; } // Output: Age: 25, Height: 1.75, Initial: JCommon format specifiers:
%d(integer),%f(float),%lf(double),%c(character),%s(string),%x(hexadecimal),%o(octal).scanf(): Used to read formatted input from the console.#include <stdio.h> int main() { int num; float price; printf("Enter an integer and a float: "); scanf("%d %f", &num, &price); printf("You entered: %d and %.2f\n", num, price); return 0; }Note the
&(address-of) operator before variable names inscanf().
Unformatted Input/Output (getchar, putchar, gets, puts)
These functions handle single characters or strings without any formatting options.
getchar(): Reads a single character from the standard input.#include <stdio.h> int main() { char ch; printf("Enter a character: "); ch = getchar(); printf("You entered: "); putchar(ch); printf("\n"); return 0; }putchar(): Writes a single character to the standard output. (See example above).gets(): Reads a string from the standard input until a newline character is encountered. (Potentially unsafe, preferfgets())#include <stdio.h> int main() { char name[50]; printf("Enter your name: "); gets(name); // Unsafe: no buffer overflow check printf("Hello, "); puts(name); return 0; }puts(): Writes a string to the standard output, automatically adding a newline character at the end. (See example above).
Control Statements
Control statements dictate the flow of execution in a program.
if: Executes a block of code if a condition is true.if (condition) { // code to execute if condition is true }if-else: Executes one block if true, another if false.if (condition) { // code if true } else { // code if false }nested if-else: Anif-elsestatement inside anotheriforelseblock.if (condition1) { if (condition2) { // code if condition1 and condition2 are true } else { // code if condition1 is true, but condition2 is false } } else { // code if condition1 is false }switch-case: Used for multi-way branching based on the value of an expression.switch (expression) { case value1: // code for value1 break; case value2: // code for value2 break; default: // code if no match }
Looping
Loops allow repeated execution of a block of code.
forloop: Used when the number of iterations is known.
Example:for (initialization; condition; update) { // code to repeat }for (int i = 0; i < 5; i++) { printf("%d ", i); // Output: 0 1 2 3 4 }whileloop: Executes as long as a condition is true.
Example:while (condition) { // code to repeat }int i = 0; while (i < 5) { printf("%d ", i); i++; } // Output: 0 1 2 3 4do-whileloop: Executes the block at least once, then checks the condition.
Example:do { // code to repeat } while (condition);int i = 0; do { printf("%d ", i); i++; } while (i < 5); // Output: 0 1 2 3 4nested loops: A loop inside another loop.for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { printf("(%d,%d) ", i, j); } } // Output: (0,0) (0,1) (0,2) (1,0) (1,1) (1,2)break: Terminates the innermost loop orswitchstatement.for (int i = 0; i < 10; i++) { if (i == 5) break; printf("%d ", i); // Output: 0 1 2 3 4 }continue: Skips the rest of the current iteration and proceeds to the next iteration.for (int i = 0; i < 5; i++) { if (i == 2) continue; printf("%d ", i); // Output: 0 1 3 4 }goto: Transfers control to a labeled statement. Generally discouraged due to making code hard to read and maintain.// Example of goto (generally avoid) int i = 0; loop_start: if (i < 5) { printf("%d ", i); i++; goto loop_start; } // Output: 0 1 2 3 4
User-defined Functions
Functions are blocks of code that perform a specific task, promoting modularity and reusability.
- Declaration (Prototype): Tells the compiler about the function's name, return type, and parameters.
Example:return_type function_name(parameter_list);int add(int a, int b); - Definition: Contains the actual code of the function.
int add(int a, int b) { return a + b; } - Call: Invokes the function to execute its code.
int result = add(10, 20); - Parameters: Values passed to the function during a call.
- Return Type: The type of value the function sends back to the caller.
voidif no value is returned.
Recursive Functions
A function that calls itself is called a recursive function. It must have a base case to stop the recursion.
- Factorial Example:
long long factorial(int n) { if (n == 0 || n == 1) { return 1; // Base case } else { return n * factorial(n - 1); // Recursive call } } - Fibonacci Example:
int fibonacci(int n) { if (n <= 1) { return n; // Base case } else { return fibonacci(n - 1) + fibonacci(n - 2); // Recursive call } }
Arrays
An array is a collection of elements of the same data type stored at contiguous memory locations.
- 1-D Array:
// Declaration int numbers[5]; // Initialization int scores[] = {10, 20, 30, 40, 50}; // Traversal for (int i = 0; i < 5; i++) { printf("%d ", scores[i]); } - 2-D Array (Matrix):
// Declaration and Initialization int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}}; // Traversal for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } - Multi-dimensional Array: Arrays with more than two dimensions (e.g.,
int cube[2][2][2];).
String Manipulations
C strings are character arrays terminated by a null character \0. The <string.h> header provides functions for string manipulation.
strlen(str): Returns the length of the string (excluding\0).char s1[] = "Hello"; int len = strlen(s1); // len is 5strcpy(dest, src): Copies the content ofsrcstring todeststring.char s2[20]; strcpy(s2, s1); // s2 is now "Hello"strcat(dest, src): Concatenates (joins)srcstring to the end ofdeststring.char s3[30] = "World"; strcat(s1, s3); // s1 is now "HelloWorld"strcmp(str1, str2): Compares two strings lexicographically. Returns 0 if equal, <0 ifstr1is less thanstr2, >0 ifstr1is greater thanstr2.int cmp = strcmp("apple", "banana"); // cmp is <0strupr(str): Converts all lowercase letters in a string to uppercase. (Non-standard, often available in compilers like GCC for Windows).char s4[] = "hello"; strupr(s4); // s4 is now "HELLO"strlwr(str): Converts all uppercase letters in a string to lowercase. (Non-standard).char s5[] = "WORLD"; strlwr(s5); // s5 is now "world"
3.2 Pointers, Structure and Data Files in C
Pointer Basics
A pointer is a variable that stores the memory address of another variable.
- Declaration:
data_type *pointer_name;int *ptr; // ptr is a pointer to an integer - Initialization: Assigning the address of a variable to a pointer using the address-of operator (
&).int var = 10; ptr = &var; // ptr now holds the address of var - Dereferencing: Accessing the value stored at the memory address pointed to by the pointer using the dereference operator (
*).printf("Value of var: %d\n", *ptr); // Output: 10 *ptr = 20; // var is now 20
Pointer Arithmetic
Pointers can be incremented, decremented, added to, or subtracted from, but only with integers. The arithmetic operations are scaled by the size of the data type the pointer points to.
- Increment/Decrement: Moves the pointer to the next/previous memory location of its data type.
int arr[] = {10, 20, 30}; int *p = arr; // p points to arr[0] (value 10) p++; // p now points to arr[1] (value 20) printf("%d\n", *p); // Output: 20 - Addition/Subtraction: Adding an integer
nto a pointer moves itn * sizeof(data_type)bytes forward.p = p + 1; // Same as p++ - Subtraction of two pointers: Returns the number of elements between them (only valid for pointers to elements of the same array).
int *p1 = &arr[0]; int *p2 = &arr[2]; int diff = p2 - p1; // diff is 2
Pointer and Array
Array names are essentially constant pointers to their first element.
- Pointer Arithmetic with Arrays:
int arr[] = {10, 20, 30}; int *p = arr; // p points to arr[0] printf("%d\n", *(p + 1)); // Accesses arr[1] (value 20) printf("%d\n", arr[2]); // Accesses arr[2] (value 30) printf("%d\n", *(arr + 2)); // Same as arr[2] - Array of Pointers: An array where each element is a pointer.
int a = 10, b = 20, c = 30; int *ptr_array[3]; // Array of 3 integer pointers ptr_array[0] = &a; ptr_array[1] = &b; ptr_array[2] = &c; printf("%d\n", *ptr_array[1]); // Output: 20
Passing Pointer to Function (Call by Reference)
Instead of passing values, we pass addresses, allowing the function to modify the original variables.
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 5, b = 10;
swap(&a, &b); // Pass addresses
printf("a = %d, b = %d\n", a, b); // Output: a = 10, b = 5
return 0;
}
Structure vs Union
Both are user-defined data types to group different types of data, but they differ in memory allocation.
- Structure:
- Syntax:
struct StructureName { data_type member1; ... }; - Memory Allocation: Each member gets its own separate memory space. Total size is the sum of sizes of all members (plus padding).
- Access: All members can be accessed simultaneously.
- Example:
struct Student { char name[50]; int roll; float marks; }; struct Student s1; // Allocates memory for name, roll, and marks
- Syntax:
- Union:
- Syntax:
union UnionName { data_type member1; ... }; - Memory Allocation: All members share the same memory location. The size of a union is equal to the size of its largest member.
- Access: Only one member can be accessed at a time; changing one member's value overwrites others.
- Example:
union Data { int i; float f; char str[20]; }; union Data d1; // Allocates memory equal to sizeof(str)
- Syntax:
Array of Structures
An array where each element is a structure.
struct Point {
int x, y;
};
int main() {
struct Point points[3]; // Array of 3 Point structures
points[0].x = 10;
points[0].y = 20;
// ... initialize others
printf("Point 0: (%d, %d)\n", points[0].x, points[0].y);
return 0;
}
Passing Structure to Function
Structures can be passed by value or by address (pointer).
- By Value: Creates a copy; original structure remains unchanged.
void displayPoint(struct Point p) { printf("X: %d, Y: %d\n", p.x, p.y); } // Call: displayPoint(myPoint); - By Reference (Pointer): Passes the address; allows modification of the original.
void modifyPoint(struct Point *p) { p->x = 100; // Use -> operator for pointer to structure } // Call: modifyPoint(&myPoint);
Structure and Pointer
A pointer can point to a structure. Members are accessed using the arrow (->) operator.
struct Person {
char name[50];
int age;
};
int main() {
struct Person p1 = {"Alice", 30};
struct Person *ptr_p1;
ptr_p1 = &p1;
printf("Name: %s, Age: %d\n", ptr_p1->name, ptr_p1->age);
// Equivalent to (*ptr_p1).name, (*ptr_p1).age
return 0;
}
Input/Output Operations on Files
C provides functions to perform file operations using the <stdio.h> header.
FILE *fopen(const char *filename, const char *mode): Opens a file. Returns aFILEpointer orNULLon failure.Modes:
"r"(read),"w"(write, creates/truncates),"a"(append),"rb","wb","ab"(binary modes),"r+"(read/write), etc.int fclose(FILE *stream): Closes an opened file. Returns 0 on success.int fprintf(FILE *stream, const char *format, ...): Writes formatted output to a file.int fscanf(FILE *stream, const char *format, ...): Reads formatted input from a file.char *fgets(char *str, int n, FILE *stream): Reads a line from a file (up ton-1characters or until newline). Safer thangets().int fputs(const char *str, FILE *stream): Writes a string to a file.
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("example.txt", "w"); // Open for writing
if (fp == NULL) {
printf("Error opening file!\n");
return 1;
}
fprintf(fp, "Hello, File!\n");
fputs("This is another line.\n", fp);
fclose(fp);
fp = fopen("example.txt", "r"); // Open for reading
if (fp == NULL) {
printf("Error opening file!\n");
return 1;
}
char buffer[100];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
return 0;
}
Sequential and Random Access to File
- Sequential Access: Reading/writing data from the beginning of the file to the end. (Done by default with
fprintf,fscanf,fgets,fputs). - Random Access: Moving the file pointer to a specific location to read/write data.
int fseek(FILE *stream, long offset, int origin): Sets the file position indicator.offset: Number of bytes to move.origin:SEEK_SET(from beginning),SEEK_CUR(from current position),SEEK_END(from end).
long ftell(FILE *stream): Returns the current position of the file pointer.void rewind(FILE *stream): Sets the file pointer to the beginning of the file (equivalent tofseek(stream, 0, SEEK_SET)).
#include <stdio.h> int main() { FILE *fp = fopen("random.txt", "w+"); // Read and write fputs("ABCDEFGHIJ", fp); // Write 10 characters fseek(fp, 3, SEEK_SET); // Move to 4th character ('D') fputc('X', fp); // Overwrite 'D' with 'X' fseek(fp, 0, SEEK_END); // Move to end long size = ftell(fp); // Get file size printf("File size: %ld\n", size); // Output: 10 rewind(fp); // Go to beginning char ch = fgetc(fp); // Read 'A' printf("First char: %c\n", ch); fclose(fp); return 0; } // File content after execution: ABCXEFGHIJ
3.3 C++ Language Constructs with Objects and Classes
C++ is an extension of C, adding object-oriented programming (OOP) features. It supports both procedural and object-oriented paradigms.
Namespace
Namespaces are used to organize code into logical groups and prevent name collisions, especially in large projects or when using multiple libraries.
- Declaration:
namespace MySpace { int value = 10; void func() { /* ... */ } } usingdirective: Brings all names from a namespace into the current scope.using namespace MySpace; // Now you can directly use value and func() int x = value;usingdeclaration: Brings a specific name from a namespace into the current scope.using MySpace::value; // Now you can use 'value' directly, but func() still needs MySpace::func() int y = value;
Function Overloading
Allows multiple functions with the same name but different parameter lists (number, type, or order of parameters) within the same scope. The compiler decides which function to call based on the arguments provided.
- Examples:
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() { add(5, 10); // Calls int add(int, int) add(5.5, 10.5); // Calls double add(double, double) add(1, 2, 3); // Calls int add(int, int, int) return 0; } - Rules:
- Only parameter list (number, type, order) can differentiate overloaded functions.
- Return type alone is not sufficient for overloading.
- Default arguments can sometimes lead to ambiguity.
Inline Functions
A hint to the compiler to replace the function call with the function's body at compile time, potentially improving performance by avoiding function call overhead, especially for small, frequently called functions.
inline int multiply(int a, int b) {
return a * b;
}
int main() {
int result = multiply(5, 3); // Compiler might replace this with '5 * 3'
return 0;
}
Default Argument
Allows a function parameter to have a default value if no argument is provided for that parameter during a function call.
void display(int a, int b = 0, int c = 0) {
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
}
int main() {
display(10); // a=10, b=0, c=0
display(10, 20); // a=10, b=20, c=0
display(10, 20, 30); // a=10, b=20, c=30
return 0;
}
Default arguments must be specified from right to left.
Pass/Return by Reference
- Pass by Reference: Allows a function to modify the original variable passed as an argument by passing its reference.
void increment(int &val) { // val is a reference to an int val++; } int main() { int num = 10; increment(num); // num becomes 11 return 0; } - Return by Reference: A function can return a reference to a variable, allowing the caller to modify the original variable. Care must be taken not to return a reference to a local variable that goes out of scope.
int global_var = 5; int &getGlobalVar() { return global_var; } int main() { getGlobalVar() = 10; // Modifies global_var std::cout << global_var << std::endl; // Output: 10 return 0; }
Introduction to Class and Object
C++ is an object-oriented language, and classes and objects are its core concepts.
- Class: A blueprint or a template for creating objects. It encapsulates data (member variables) and functions (member functions) that operate on that data.
class Car { public: // Member variables (attributes) std::string brand; int year; // Member function (behavior) void displayInfo() { std::cout << "Brand: " << brand << ", Year: " << year << std::endl; } }; - Object: An instance of a class. When a class is defined, no memory is allocated; memory is allocated only when an object is created.
int main() { Car myCar; // myCar is an object of the Car class myCar.brand = "Toyota"; myCar.year = 2020; myCar.displayInfo(); return 0; }
Access Specifiers
Keywords that control the visibility and accessibility of class members.
public:Members are accessible from anywhere, inside or outside the class.private:Members are accessible only from within the same class. (Default for classes).protected:Members are accessible within the same class and by derived classes (inheritance).
Objects and Member Access
- Dot Operator (
.): Used to access members of an object directly.Car myCar; myCar.brand = "Honda"; myCar.displayInfo(); - Arrow Operator (
->): Used to access members of an object through a pointer to that object.Car *ptrCar = &myCar; ptrCar->brand = "Nissan"; ptrCar->displayInfo(); // Equivalent to (*ptrCar).displayInfo()
Defining Member Function
- Inside Class: Functions defined inside the class body are implicitly
inline.class Dog { public: void bark() { std::cout << "Woof!" << std::endl; } }; - Outside Class with Scope Resolution (
::): Preferred for larger functions to keep class definition clean.class Cat { public: void meow(); }; void Cat::meow() { // Definition outside class using scope resolution std::cout << "Meow!" << std::endl; }
Constructor and its types
Special member functions automatically called when an object is created. They initialize the object's members. They have the same name as the class and no return type.
- Default Constructor: A constructor that takes no arguments. If not provided, C++ generates a default one.
class Box { int length, width; public: Box() { // Default constructor length = 0; width = 0; std::cout << "Default constructor called." << std::endl; } }; Box b1; // Calls default constructor - Parameterized Constructor: Takes arguments to initialize object members with specific values.
class Box { int length, width; public: Box(int l, int w) { // Parameterized constructor length = l; width = w; std::cout << "Parameterized constructor called." << std::endl; } }; Box b2(10, 5); // Calls parameterized constructor - Copy Constructor: Initializes an object by copying an existing object of the same class.
class Box { int length, width; public: Box(const Box &other) { // Copy constructor length = other.length; width = other.width; std::cout << "Copy constructor called." << std::endl; } }; Box b3 = b2; // Calls copy constructor (initialization) Box b4(b2); // Also calls copy constructor
Destructor
A special member function automatically called when an object is destroyed (goes out of scope, or delete is called for dynamically allocated objects). Used to release resources (e.g., dynamically allocated memory). Has the same name as the class prefixed with a tilde (~) and takes no arguments.
class MyClass {
public:
MyClass() { std::cout << "Constructor called." << std::endl; }
~MyClass() { std::cout << "Destructor called." << std::endl; }
};
int main() {
MyClass obj; // Constructor called
// obj goes out of scope, destructor called
return 0;
}
Dynamic Memory Allocation for Objects and Object Array (new, delete)
C++ uses new and delete operators for dynamic memory management, which are type-safe and automatically call constructors/destructors.
- Single Object:
MyClass *ptr = new MyClass(); // Allocates MyClass object, calls constructor // ... use ptr ... delete ptr; // Deallocates memory, calls destructor - Object Array:
MyClass *arr = new MyClass[5]; // Allocates array of 5 MyClass objects, calls default constructor for each // ... use arr ... delete[] arr; // Deallocates array memory, calls destructor for each element
this Pointer
A special pointer that points to the current object on which a member function is called. It is implicitly passed to all non-static member functions.
class Point {
int x, y;
public:
void setCoords(int x, int y) {
this->x = x; // 'this->x' refers to the member variable
this->y = y; // 'x' and 'y' refer to function parameters
}
Point &compare(Point &other) { // Returns reference to the object with larger x
return (this->x > other.x) ? *this : other;
}
};
Static Data Member and Static Function
- Static Data Member: A member variable declared with the
statickeyword. It belongs to the class, not to any specific object. All objects of the class share a single copy of the static data member. It must be defined outside the class.class Counter { public: static int count; // Declaration Counter() { count++; } }; int Counter::count = 0; // Definition and initialization outside class int main() { Counter c1, c2, c3; std::cout << Counter::count << std::endl; // Output: 3 return 0; } - Static Function: A member function declared with
static. It can only access static data members and static member functions of the class. It can be called using the class name without creating an object.class MyMath { public: static int add(int a, int b) { return a + b; } }; // Call without an object int sum = MyMath::add(5, 7); // sum is 12
Constant Member Functions and Constant Objects
- Constant Object: An object declared with
const. Its data members cannot be modified after initialization. Onlyconstmember functions can be called on aconstobject.const MyClass obj_const; - Constant Member Function: A member function declared with
constafter its parameter list. It guarantees that the function will not modify any data members of the object.class Immutable { int value; public: Immutable(int v) : value(v) {} int getValue() const { // Constant member function return value; } // void setValue(int v) const { value = v; } // ERROR: cannot modify in const function }; int main() { const Immutable i1(10); std::cout << i1.getValue() << std::endl; // OK // i1.setValue(20); // ERROR return 0; }
Friend Function and Friend Classes
- Friend Function: A non-member function that is granted special permission to access the
privateandprotectedmembers of a class. Declared inside the class with thefriendkeyword.class MyData { int private_val; public: MyData(int v) : private_val(v) {} friend void showPrivate(MyData obj); // Friend function declaration }; void showPrivate(MyData obj) { std::cout << "Private value: " << obj.private_val << std::endl; } - Friend Class: If a class is declared as a friend of another class, all member functions of the friend class can access the
privateandprotectedmembers of the class that granted friendship.class A { int x; friend class B; // B is a friend of A public: A(int val) : x(val) {} }; class B { public: void displayA(A obj) { std::cout << "A's private x: " << obj.x << std::endl; } };
3.4 Features of Object-Oriented Programming
OOP is a programming paradigm based on the concept of "objects", which can contain data and code. Key features include Encapsulation, Abstraction, Inheritance, and Polymorphism.
Operator Overloading
Allows operators to be redefined or overloaded for user-defined types (classes). This enables operators to work with objects in a natural way.
- Unary Operators (
++,--):class Point { int x, y; public: Point(int _x = 0, int _y = 0) : x(_x), y(_y) {} void display() { std::cout << "(" << x << ", " << y << ")" << std::endl; } Point operator++() { // Prefix ++ ++x; ++y; return *this; } Point operator++(int) { // Postfix ++ (int is a dummy parameter) Point temp = *this; x++; y++; return temp; } }; - Binary Operators (
+,-,==,!=,<<,>>):class Vector { int i, j; public: Vector(int _i = 0, int _j = 0) : i(_i), j(_j) {} Vector operator+(const Vector &other) const { // Binary + return Vector(i + other.i, j + other.j); } bool operator==(const Vector &other) const { // Binary == return (i == other.i && j == other.j); } // Friend function for << (output stream) friend std::ostream &operator<<(std::ostream &out, const Vector &v) { out << "<" << v.i << ", " << v.j << ">"; return out; } // Friend function for >> (input stream) friend std::istream &operator>>(std::istream &in, Vector &v) { std::cout << "Enter i and j: "; in >> v.i >> v.j; return in; } };
Data Conversion (Type Conversion between User-defined Types)
Allows conversion between user-defined types (classes) or between user-defined types and built-in types.
- Basic Type to User-Defined Type (using constructor):
class Kilograms { double kg; public: Kilograms(double val) : kg(val) {} // Constructor converts double to Kilograms // ... }; Kilograms k = 5.5; // Implicit conversion from double to Kilograms - User-Defined Type to Basic Type (using conversion operator):
class Pounds { double lbs; public: Pounds(double val) : lbs(val) {} operator double() { // Conversion operator to double return lbs * 0.453592; // Convert pounds to kg } }; Pounds p(10); double kg_val = p; // Implicit conversion from Pounds to double - User-Defined Type to User-Defined Type: Can be achieved via a conversion constructor in the target class or a conversion operator in the source class.
Inheritance
A mechanism where a new class (derived/child class) inherits properties and behaviors from an existing class (base/parent class), promoting code reusability.
- Single Inheritance: One derived class from one base class.
class Animal { /* ... */ }; class Dog : public Animal { /* ... */ }; - Multiple Inheritance: One derived class from multiple base classes.
class Father { /* ... */ }; class Mother { /* ... */ }; class Child : public Father, public Mother { /* ... */ }; - Multilevel Inheritance: A derived class becomes a base class for another derived class.
class Grandparent { /* ... */ }; class Parent : public Grandparent { /* ... */ }; class Child : public Parent { /* ... */ }; - Hybrid Inheritance: A combination of two or more types of inheritance.
- Multipath Inheritance (Diamond Problem): Occurs when a class inherits from two classes that themselves inherit from a common base class, leading to ambiguity in accessing members of the common base. Resolved using virtual inheritance.
class A { /* ... */ }; class B : virtual public A { /* ... */ }; class C : virtual public A { /* ... */ }; class D : public B, public C { /* ... */ }; // D has only one A sub-object
Constructor/Destructor in Single/Multilevel Inheritance (Order of Execution)
- Constructors: Executed in the order of inheritance, from the most base class to the most derived class.
class Base { public: Base() { std::cout << "Base Constructor\n"; } ~Base() { std::cout << "Base Destructor\n"; } }; class Derived : public Base { public: Derived() { std::cout << "Derived Constructor\n"; } ~Derived() { std::cout << "Derived Destructor\n"; } }; // When Derived obj; is created: // Output: Base Constructor, Derived Constructor - Destructors: Executed in the reverse order of constructor calls, from the most derived class to the most base class.
// When Derived obj; goes out of scope: // Output: Derived Destructor, Base Destructor
3.5 Pure Virtual Function and File Handling
Virtual Function and Dynamic Binding
Virtual functions enable polymorphism in C++. When a base class pointer points to a derived class object, calling a virtual function through that pointer will execute the derived class's version of the function (dynamic/runtime polymorphism).
- Virtual Function: Declared with the
virtualkeyword in the base class.class Base { public: virtual void show() { // Virtual function std::cout << "Base class show()\n"; } }; class Derived : public Base { public: void show() override { // Overriding the virtual function std::cout << "Derived class show()\n"; } }; int main() { Base *bptr; Derived d_obj; bptr = &d_obj; bptr->show(); // Calls Derived::show() due to virtual function return 0; } - Pure Virtual Function: A virtual function declared by assigning
= 0in the base class. It makes the base class an abstract class (cannot be instantiated). Derived classes must provide an implementation for pure virtual functions.class Shape { // Abstract class public: virtual void draw() = 0; // Pure virtual function virtual ~Shape() {} }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing Circle\n"; } }; // Shape s; // ERROR: cannot instantiate abstract class Shape *sptr = new Circle(); sptr->draw(); // Calls Circle::draw() delete sptr; - Dynamic Binding (Late Binding): The decision of which function to call is made at runtime, based on the type of the object pointed to by the base class pointer. This is facilitated by a Virtual Table (vtable) and a Virtual Pointer (vptr). Each class with virtual functions has a vtable (a table of function pointers). Each object of such a class has a vptr, which points to its class's vtable.
Defining, Opening and Closing a File (fstream, ifstream, ofstream)
C++ uses stream classes for file I/O, defined in the <fstream> header.
ofstream: For writing to files.#include <fstream> // ... ofstream outFile("output.txt"); // Opens file for writing (truncates if exists) if (outFile.is_open()) { outFile << "Hello C++ File!"; outFile.close(); }ifstream: For reading from files.#include <fstream> // ... ifstream inFile("output.txt"); // Opens file for reading if (inFile.is_open()) { std::string line; while (getline(inFile, line)) { // Read line by line std::cout << line << std::endl; } inFile.close(); }fstream: For both reading and writing.#include <fstream> // ... fstream myFile("data.txt", std::ios::out | std::ios::in | std::ios::trunc); // Opens for writing, reading, and truncates if exists. // Can specify modes like std::ios::app (append), std::ios::binary, etc. if (myFile.is_open()) { myFile << "Initial data\n"; myFile.seekg(0); // Move read pointer to beginning std::string line; getline(myFile, line); std::cout << line << std::endl; myFile.close(); }
Input/Output Operations on Files
Once a file stream object is created and opened, you can use stream insertion (<<) and extraction (>>) operators, similar to console I/O.
#include <fstream>
#include <string>
int main() {
ofstream outFile("numbers.txt");
outFile << 10 << " " << 20.5 << std::endl;
outFile.close();
ifstream inFile("numbers.txt");
int i;
double d;
inFile >> i >> d; // Reads 10 and 20.5
std::cout << "Read: " << i << ", " << d << std::endl;
inFile.close();
return 0;
}
Error Handling During I/O Operations
Stream objects have member functions to check for I/O errors:
is_open(): Checks if the file is successfully opened.good(): Returns true if no errors have occurred.fail(): Returns true if an I/O operation failed.bad(): Returns true if a non-recoverable error occurred.eof(): Returns true if the end-of-file has been reached.clear(): Clears the error flags, allowing further I/O operations.
ifstream inFile("nonexistent.txt");
if (inFile.fail()) {
std::cerr << "Failed to open file.\n";
inFile.clear(); // Clear error flags if needed
}
Stream Class Hierarchy for Console I/O
C++ I/O is built on a hierarchy of classes in <iostream> and <fstream>:
ios_base: Base class for all stream classes, handles flags and formatting.ios: Inherits fromios_base, adds error state information.istream: For input operations (e.g.,std::cin).ostream: For output operations (e.g.,std::cout,std::cerr,std::clog).iostream: Inherits from bothistreamandostream.ifstream,ofstream,fstream: Derived fromistream,ostream, andiostreamrespectively for file operations.
Unformatted I/O and Formatted I/O with ios member functions and flags
- Unformatted I/O:
get(): Reads a single character or a block of characters.put(): Writes a single character.read(): Reads a block of binary data.write(): Writes a block of binary data.
char ch; std::cin.get(ch); // Reads one char std::cout.put(ch); // Writes one char - Formatted I/O with
iosmember functions and flags:You can control formatting using member functions like
setf(),unsetf(), andflags(), often with constants defined inios_base.std::cout.setf(std::ios::hex, std::ios::basefield); // Set hexadecimal output std::cout << 255 << std::endl; // Output: ff std::cout.unsetf(std::ios::hex); // Unset hexadecimal output std::cout.setf(std::ios::showpos); // Show '+' for positive numbers std::cout << 10 << std::endl; // Output: +10 std::cout.unsetf(std::ios::showpos);
Formatting with Manipulators
Manipulators (from <iomanip>) provide a more convenient way to control formatting.
setw(width): Sets the field width for the next output.std::cout << std::setw(10