C++ Core Concepts and Language Features
Introduction to C++ and Classes
Classes represent a powerful addition to C++'s type system, enabling data encapsulation and information hiding.
Encapsulation refers to bundling related data and operations into a single unit, creating separation from external code. Typically, class data members are declared as private to enforce information hiding. The class mechanism supports core object-oriented principles including encapsulation, information hiding, inheritance, derivation, and polymorphism.
Declaration Placement: Declarations can appear inside functions (local scope limited to that function) or outside functions (global scope).
Compilation Process: The compiler translates source code (.cpp files) into binary object files (.obj), performing lexical and syntax checking. The compiler reports both warnings (non-critical issues) and errors (must-fix issues).
Execution Process: The compiled object code links with system libraries to produce an executable (.exe), which then loads into memory and runs.
Functions and Storage
Function Characteristics:
- Functions cannot be nested within other functions, but nested calls are permitted
- Parameters do not occupy memory storage until the function is invoked
- Default arguments must appear at the rightmost position of the parameter list
- Default values are specified in function declarations
// Declaration with defaults
int calculate(int width, int height = 10, int depth = 10);
// Valid call: 15, 10, 10
calculate(15);
Function Overloading: Multiple functions may share the same name when their parameter lists differ in count, order, or type.
Storage Classes:
| Declaration | Category | Description |
|---|---|---|
| auto | Automatic local | Local variables within functions (default) |
| static | Static local | Stored in static storage area |
| register | Register | Frequently-used variables stored in CPU registers |
| extern | External | Global variables; enables use before declaration |
Static Storage vs Dynamic Storage: Global variables reside in static storage throughout program execution. Dynamic storage contains:
- Function parameters (allocated during invocation)
- Local variables without static keyword
- Return addresses and call context
Pointers and Arrays
Array Parameters: The C++ compilation system treats array parameters as pointer variables.
Row and Column Pointers: Row pointers reference entire rows; column pointers reference specific elements within rows.
a[1][2] == *(*(a + 1) + 2)
// a+1 is a row pointer
// *(a+1) yields the column pointer a[1]
// &a[1] converts back to row pointer
Const Pointers:
| Type | Behavior | Syntax |
|---|---|---|
| Pointer to constant | Cannot modify pointed-to value; pointer can change | const int* ptr; |
| Constant pointer | Can modify pointed-to value; pointer cannot change | int* const ptr; |
| Const pointer to constant | Neither can change | const int* const ptr; |
References: A reference serves as a alias for a variable. Since references are aliases rather than actual variables, constants cannot use references. Internally, references contain variable addresses.
Structures and Enumerations
Structure Rules:
- Structures cannot contain instances of themselves but can contain pointers to themselves
- Member initialization within the structure definition is not allowed
- Structures support constructors but not destructors
- Structures cannot participate in inheritance
- Structure instances can be created without
new, unlike class objects - Passing a structure to a function as a parameter uses pass-by-value
Enumeration Types:
enum ColorScheme { red, green, blue }; // Values: 0, 1, 2
enum Priority { high = 3, medium, low }; // Values: 3, 4, 5
Enumerators are constants that cannot be reassigned but possess implicit integer values starting from zero.
Bitwise XOR: The ^ operator yields 1 when operands differ and 0 when they match.
Object-Oriented Principles
Four Pillars of OOP:
- Abstraction: Focusing on essential characteristics
- Encapsulation: Grouping data and operations; typically data members are private, interface functions are public
- Inheritance: Reusing existing classes to create new derived classes
- Polymorphism: Derived class instances respond differently to identical messages
Class Declaration Details:
- Class declarations end with a semicolon, similar to structs
- Members default to private if no access specifier is provided
- Multiple access sections (public/private) can exist within a class
- Classes lacking member functions behave like structs
Inline Member Functions:
Functions defined inside the class body are automatically treated as inline if they contain no control structures:
class Rectangle {
public:
inline int area() { return width * height; } // Optional inline keyword
};
External definitions require explicit inline declaration but separate the interface (.h) from implementation (.cpp).
Memory Layout: Individual objects only store their data members; member functions occupy shared space outside objects.
Constructors and Destructors
Constructor Behavior:
- Default constructors provide default parameter values
- Classes and structs both generate parameterless constructors automatically
- User-defined parameterized constructors replace the default constructor
class Container {
public:
Container() : width(0), height(0) {}
Container(int w = 0, int h = 0) : width(w), height(h) {}
};
Destructor Rules:
- Destructor names begin with ~ followed by the class name
- Destructors cannot be overloaded (no parameters allowed)
- Only one destructor per class exists
- If undefined, the system provides an empty destructor
class ResourceHolder {
public:
ResourceHolder(int val) { data = new int(val); }
~ResourceHolder() { delete data; }
private:
int* data;
};
Destruction Order: Objects constructed last are destroyed first (LIFO stack behavior).
Static local objects constructed within functions persist until program termination.
The this Pointer
The *this pointer references the object invoking the member function. When calling obj.function(), the this pointer automatically points to obj, enabling access to that object's members.
Constant Objects and Members
Constant Object Declaration:
| Type | Syntax |
|---|---|
| Constant object | class_name const obj or const class_name obj |
| Constant data member | const data_type member_name |
| Constant member function | return_type function_name(params) const |
Constant Object Rules:
- Constant objects can only invoke const member functions
- Const member functions can read both const and non-const data but cannot modify any data
- Non-const member functions cannot be called on constant objects
Constant Pointers:
| Type | Syntax |
|---|---|
| Pointer to constant object | const class_name* ptr; |
| Constant pointer to object | class_name* const ptr; |
When declaring constant pointers (regardless of pointed-to type), const appears within the declaration.
Object Copying and Assignment
Copy Constructor: Creates a new object identical to an existing one.
class Box {
public:
Box(const Box& original); // Copy constructor signature
};
Box newBox = existingBox; // Invokes copy constructor
Box anotherBox(existingBox); // Also invokes copy constructor
If undefined, the system provides a default copy constructor.
Assignment Operator: Assigns values to an existing object. Class data members should not include dynamically-allocated memory pointers, as this causes issues during deletion.
Static Members
Static Data Members:
- Declared with
statickeyword within the class - Initialized out side the class (mandatory)
- Cannot use initializer lists
- Default value is zero if uninitialized
- Public static members are accessible via class name or object name
- Private static members require member function access
- Static member scope is limited to the class
class BankAccount {
private:
static double interestRate; // Declaration
public:
static int instanceCount;
};
double BankAccount::interestRate = 0.05; // Definition
Static Member Functions:
- Lack the
thispointer - Can only access static members of their class
- Declared with
statickeyword
Friend Functions and Classes
Friend functions gain access to private members of the class that declares them as friends. Since friend functions lack the this pointer, object names must be specified when accessing members:
class Triangle {
friend void display(Triangle& t);
};
void display(Triangle& t) {
cout << t.sideLength; // Object reference required
}
Friend classes grant all member functions of one class access to another class's private members.
Operator Overloading
Overloading Rules:
- Overloaded functions must differ in parameter count, order, or type
- Overloaded operators cannot have default arguments
- At least one parameter must be a user-defined type
operator<<andoperator>>must be friend functions (not members), as their left operands are stream types
class Complex {
friend Complex operator+(const Complex& left, const Complex& right);
friend ostream& operator<<(ostream& out, const Complex& obj);
};
Conversion Constructors: Convert basic types or other class objects into the current class type. These constructors accept exactly one parameter.
Type Conversion Functions: Convert the current class object to another type.
class Temperature {
public:
operator double() { return celsiusValue; } // Converts to double
};
Caution: Having both conversion constructors and type conversion functions can introduce ambiguity.
Inheritance and Derivation
Class Relationships:
- If class B inherits from class A, then A is the base/parent/superclass and B is the derived/child subclass
class DerivedClass : [access_mode] BaseClass {
// New members specific to derived class
};
// access_mode: public, protected, private (default is private)
Inherited Members: All base class members are inherited except constructors and destructors.
Member Overriding: When derived class members share names with base class members, the derived version takes precedence (shadowing).
Access Control Table:
| Base Member | Public Derivation | Protected Derivation | Private Derivation |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | inaccessible | inaccessible | inaccessible |
Memory Layout: Derived class instances contain their own non-static data members plus inherited non-static members. Static members and member functions exist once per class hierarchy.
Derived Class Constructors:
Constructor execution order:
- Call base class constructor
- Call member object constructors
- Execute derived class constructor
Destructor execution follows reverse order.
class Person {
protected:
string name;
int age;
public:
Person(string n, int a) : name(n), age(a) {}
};
class Employee : public Person {
private:
int employeeId;
public:
Employee(string n, int a, int id) : Person(n, a), employeeId(id) {}
};
For simple derived classes without member objects, base class data members should be protected (not private) to allow access from derived constructors.
Multiple Inheritance:
class Derived : public Base1, public Base2 {
// Members from both base classes
};
Ambiguity Resolution: When base classes share members with identical names:
class Graduate : public Teacher, public Student {
// Both Teacher and Student have 'name' member
};
Graduate g;
g.Teacher::name; // Access Teacher's name
Virtual Base Classes: Prevent duplicate copies of base class members in multiple inheritance scenarios:
class Base {
protected:
int sharedValue;
public:
Base(int v) : sharedValue(v) {}
};
class BranchA : virtual public Base {
public:
BranchA(int v) : Base(v) {}
};
class BranchB : virtual public Base {
public:
BranchB(int v) : Base(v) {}
};
class Final : public BranchA, public BranchB {
public:
Final(int v) : Base(v), BranchA(v), BranchB(v) {}
};
With virtual base classes, only the most derived class (Final) initializes the shared base class member.
Assignment Compatibility: A derived class instance can be assigned to a base class variable, but this discards the derived-specific portions. Base class pointers can reference derived class objects to access base members.
Polymorphism and Virtual Functions
Polymorphism Types:
| Type | Resolution Time | Implementation | Purpose |
|---|---|---|---|
| Static | Compile-time | Function overloading | Same-level functions with different signatures |
| Dynamic | Runtime | Virtual functions | Same function across inheritance hierarchy |
Virtual Functions: Declared with the virtual keyword in the base class and redefined in derived classes.
class Shape {
public:
virtual double area() const { return 0; }
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
};
Virtual functions enable base class pointers and references to invoke appropriate derived class implementations dynamically.
Important Rules:
- Without
virtualkeyword, base class pointer calls base class members only - Derived class virtual functions become virtual automatically
- Virtual functions cannot be constructors but should be destructors
- Declaring base class destructors as virtual ensures proper cleanup when deleting derived objects through base pointers
Pure Virtual Functions: Declare virtual functions without implementations:
virtual double volume() const = 0;
Abstract Classes: Classes containing pure virtual functions cannot be instantiated. They serve as interface specifications for derived classes.
class Geometry {
public:
virtual double calculate() const = 0;
virtual ~Geometry() {} // Virtual destructor
};
Derived classes must implement all pure virtual functions to become instantiable. Abstract class pointers can reference derived class objects to achieve polymorphic behavior.
Input and Output Streams
I/O Categories:
- Standard I/O: Input/output to system devices
- File I/O: Input/output to storage media
- String I/O: Input/output to memory buffers
Stream Class Hierarchy:
| Class | Purpose | Header |
|---|---|---|
| ios | Abstract base class | - |
| istream | Input stream base | - |
| ostream | Output stream base | iostream |
| iostream | Combined input/output | - |
| ifstream | Input file stream | fstream |
| ofstream | Output file stream | fstream |
| fstream | Bidirectional file stream | fstream |
| istrstream | Input string stream | strstream |
| ostrstream | Output string stream | strstream |
Standard Output Objects:
- cout: Buffered output;
endlflushes the buffer and outputs a newline - cerr: Unbuffered error output to standard error device
- clog: Buffered error output; flushes when buffer fills or at program end
Standard Input Object:
- cin: Extractions (
>>) skip whitespace characters - Input requires pressing Enter before processing
- Returns zero in boolean context when encountering EOF or errors
Character Input Methods:
cin.get(); // Read single character including whitespace
cin.get(ch); // Read into character variable
cin.get(buffer, n); // Read n-1 characters
cin.getline(buffer, n); // Read line, skip delimiter in next read
cin.ignore(n, delim); // Skip n characters or until delimiter
cin.peek(); // View next character without consuming
cin.putback(ch); // Return character to input stream
File End Detection:
while (!cin.eof()) { // Continue until end of file
// Process input
}
Exception Handling
Exception Flow:
- Program execution proceeds normally through the
tryblock - When an exception occurs, the program searches for a matching
catchblock - If no catch exists in the current function, the search continues in calling functions
- After handling, execution continues after the catch blocks
- Uncaught exceptions terminate the program
try {
// Normal execution path
if (errorCondition) {
throw value;
}
} catch (int errorCode) {
// Handle integer exceptions
} catch (const char* message) {
// Handle string exceptions
}
Function Pointers
Obtaining Function Addresses: The function name itself represents its adress in memory.
Declaration Syntax: Function pointer declarations specify both the return type and parameter signature:
double calculate(int value);
double (*functionPtr)(int); // Pointer to function returning double
functionPtr = calculate; // Store function address
Function pointers enable passing behavior as arguments, allowing functions to call different operations based on runtime decisions.