Chapter 1: C++ Foundations and Compile Model
C++ gives you high performance, deterministic control, and abstraction power at the same time. That power comes with more responsibility than many managed languages, so the first step is understanding the translation model, toolchain, and why the language is used in systems, engines, trading, robotics, and embedded products.
1.1 Step-by-Step Theory
- You write source files in .cpp and declarations in headers. Code structure often spans multiple translation units.
- The compiler transforms each source file separately. Preprocessing, compilation, assembly, and linking are distinct stages.
- The linker combines object files. This is where unresolved symbols and duplicate definitions surface.
- The executable runs directly on the operating system. There is no JVM-style managed runtime layer by default.
| Stage | Input | Output |
|---|---|---|
| Preprocessing | Source + includes + macros | Expanded translation unit |
| Compilation | Expanded source | Object code |
| Linking | Object files + libraries | Executable |
1.2 Build Example
g++ -std=c++20 main.cpp utils.cpp -o app
./app- Compile with warnings enabled from the beginning.
- Understand linking errors instead of treating them as random tool failures.
- Use a consistent language standard such as C++17 or C++20 across a project.
- C++ compiles to native code without a managed runtime requirement.
- The build pipeline matters because translation units and linking shape real project behavior.
- Toolchain understanding is part of professional C++ development, not optional overhead.
Interview Questions
- What is the difference between compilation and linking?
- Why do linker errors happen in C++ projects?
- Why is C++ often used where performance and control matter?
Chapter 2: Syntax, Types, and Operators
C++ inherits low-level power from C but adds richer abstraction tools. Before those abstractions matter, you need a precise understanding of value types, references, expressions, and how the type system guides both correctness and performance.
2.1 Code and Output
#include
using namespace std;
int main() {
int count = 4;
double price = 99.5;
bool available = true;
cout << count << " " << price << " " << available << endl;
} 2.2 Comparison
| Type | Purpose | Typical Use |
|---|---|---|
| int | Integer math | Counters, indexes, sizes |
| double | Floating-point values | Measurements, calculations |
| bool | True/false logic | Conditions and state flags |
| reference | Alias for an existing object | Efficient parameter passing |
C++ type decisions affect not only correctness but copy behavior, lifetime, and performance. This is why the language rewards precision more than convenience-driven guessing.
- C++ types influence memory layout, performance, and API design.
- References are not just syntax sugar; they change how data is accessed and passed.
- Operator behavior should always be interpreted through the types involved.
Interview Questions
- What is the difference between a reference and a pointer?
- Why does type precision matter more in C++ than in some managed languages?
- Why does bool often print as 0 or 1 with iostreams by default?
Chapter 3: Control Flow and Functions
Flow control in C++ is familiar syntactically, but the real learning goal is writing predictable logic while keeping functions small, expressive, and cheap to call. This chapter is where algorithmic thinking starts meeting design discipline.
3.1 Example
#include
#include
using namespace std;
int passedCount(const vector& scores) {
int passed = 0;
for (int score : scores) {
if (score >= 40) {
++passed;
}
}
return passed;
} 3.2 Function Design Comparison
| Approach | Benefit | Risk |
|---|---|---|
| Small pure function | Easy to test and reason about | May require composition of several helpers |
| Large stateful function | Fast to write once | Hard to debug and reuse |
| const reference parameter | Avoids copying large objects | Needs lifetime awareness |
- Pass large read-only objects by const reference.
- Prefer descriptive names over condensed logic.
- Let one function solve one coherent problem.
Interview Questions
- Why is const reference commonly used in C++ APIs?
- What makes a function hard to maintain?
- How do off-by-one errors show up in loop logic?
Chapter 4: Arrays, Strings, and Core Containers
C++ offers both low-level arrays and high-level standard library containers. Choosing between them is a design decision about safety, flexibility, and performance. Modern C++ strongly encourages standard containers for most application logic.
4.1 Container Example
#include
#include
using namespace std;
vector values = {10, 20, 30};
values.push_back(40);
cout << values.size() << endl; 4.2 Comparison
| Container | Strength | Use It For | Typical Declaration |
|---|---|---|---|
| raw array | Very low-level control | Interfacing with fixed-size memory or legacy APIs | int scores[3] = {90, 82, 76}; |
| std::array | Fixed size with value semantics | Compile-time fixed collections | std::array |
| std::vector | Dynamic contiguous storage | General-purpose sequence data | std::vector |
| std::string | Managed text storage | Safe string handling | std::string name = "Spectrum"; |
- Modern C++ favors standard containers over manual array management.
- std::vector is the default choice for dynamic sequential data.
- Choosing safer abstractions usually improves both correctness and maintainability.
Interview Questions
- Why is std::vector preferred over raw arrays in many cases?
- What is the difference between std::array and std::vector?
- Why is std::string usually better than char buffers for application code?
Chapter 5: Classes and Object-Oriented Design
Classes in C++ are not only about bundling data and functions. They also control visibility, lifetime, invariants, and ownership patterns. Good class design is one of the most important differences between toy code and maintainable C++ software.
5.1 Class Example
class Account {
private:
double balance;
public:
explicit Account(double balance) : balance(balance) {}
void deposit(double amount) { balance += amount; }
double getBalance() const { return balance; }
};5.2 Design Insight
| Concept | Why It Matters | Typical Risk |
|---|---|---|
| encapsulation | Protects invariants and design boundaries | Making everything public for convenience |
| constructor | Ensures correct initialization | Allowing invalid initial state |
| const member function | Communicates read-only behavior | Omitting const and weakening API clarity |
- Initialize objects into valid state immediately.
- Mark read-only methods const whenever appropriate.
- Expose behavior-driven APIs instead of leaking internal representation.
Interview Questions
- Why is encapsulation important in C++ class design?
- What does const after a member function mean?
- Why should constructors establish valid object state?
Chapter 6: Inheritance, Polymorphism, and Virtual Dispatch
C++ supports rich object hierarchies, but that power is best used carefully. Virtual functions enable runtime polymorphism, yet inheritance can also create tight coupling if relationships are forced instead of modeled honestly.
6.1 Polymorphism Example
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double radius;
public:
explicit Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
};6.2 Comparison
| Technique | Strength | Risk |
|---|---|---|
| inheritance | Models shared interface and specialization | Fragile hierarchies if relationships are artificial |
| virtual function | Runtime polymorphism | Extra indirection and design complexity |
| composition | Flexible behavior assembly | Requires more deliberate composition design |
- Virtual dispatch enables runtime flexibility.
- Composition is often safer than inheritance for reuse.
- Base classes should represent stable abstractions, not convenience-only groupings.
Interview Questions
- Why should polymorphic base classes usually have virtual destructors?
- When is composition better than inheritance in C++?
- What problem do pure virtual functions solve?
Chapter 7: Templates and Generic Programming
Templates are one of C++'s most powerful features because they let you write algorithms and types that are resolved at compile time for many data types. They also introduce complexity, so the real skill is using them for generality without sacrificing readability.
7.1 Template Example
template
T maxValue(T a, T b) {
return (a > b) ? a : b;
}
cout << maxValue(10, 20) << endl;
cout << maxValue(3.5, 2.1) << endl; 7.2 Template Thinking
| Benefit | Why It Matters | Tradeoff |
|---|---|---|
| Type-generic reuse | One algorithm can support many types | Error messages can become harder to parse |
| Compile-time optimization | No runtime dispatch required for many patterns | Compilation complexity may grow |
| STL compatibility | Enables reusable container and algorithm design | Requires stronger type constraints understanding |
- Use templates when reuse is real, not hypothetical.
- Prefer clear type constraints and readable names.
- Do not hide weak design behind over-generalized templates.
Interview Questions
- What problem do templates solve in C++?
- Why can template error messages become difficult?
- How does generic programming support STL design?
Chapter 8: Memory Management and RAII
This is the chapter that most clearly separates C++ from many higher-level languages. Lifetime, ownership, stack vs heap allocation, and RAII define how robust C++ software is built. Memory bugs are rarely caused by syntax alone; they come from weak ownership models.
8.1 Smart Pointer Example
#include
using namespace std;
auto value = make_unique(42);
cout << *value << endl; 8.2 Ownership Comparison
| Technique | Strength | Risk |
|---|---|---|
| raw new/delete | Low-level control | Leaks, double deletes, unclear ownership |
| std::unique_ptr | Single-owner safety | Ownership transfer must be explicit |
| std::shared_ptr | Shared ownership | Overuse can obscure lifetime and add overhead |
RAII means resource acquisition is initialization: object lifetime controls resource lifetime. This principle applies not only to memory but also files, locks, sockets, and other managed resources.
Interview Questions
- What is RAII and why is it central in C++?
- Why are smart pointers generally safer than raw new/delete?
- What is the difference between unique_ptr and shared_ptr?
Chapter 9: STL Algorithms and Iterators
The STL is one of the language's biggest productivity multipliers. Standard containers become much more powerful when combined with algorithms and iterators, because you stop writing many loops manually and start expressing intent more directly.
9.1 Algorithm Example
#include
#include
vector values = {5, 2, 9, 1};
sort(values.begin(), values.end());
for (int value : values) {
cout << value << " ";
} 9.2 STL Perspective
| Tool | Use It For | Why It Helps |
|---|---|---|
| iterator | Abstract traversal | Lets algorithms work across many containers |
| sort | Ordering ranges | Well-tested and efficient implementation |
| find / count_if | Search and predicates | Clear intent over hand-written loop boilerplate |
- Use standard algorithms before writing custom loops by reflex.
- Choose containers and algorithms together, not in isolation.
- Prefer clarity and tested library behavior over duplicated logic.
Interview Questions
- Why are iterators important to STL design?
- What advantage do standard algorithms give over repeated custom loops?
- How do containers and algorithms complement each other in modern C++?
Chapter 10: Modern C++ Features
Modern C++ added features that improve safety and expressiveness, but only if you understand their design intent. auto, range-based loops, move semantics, structured bindings, and constexpr are all useful tools when they reduce noise without hiding meaning.
10.1 Modern Syntax Example
vector> users = {{"Asha", 82}, {"Ravi", 91}};
for (const auto& [name, score] : users) {
cout << name << ": " << score << endl;
} 10.2 Comparison
| Feature | Value | Use Carefully Because |
|---|---|---|
| auto | Reduces repetitive type noise | Can hide important type information if overused |
| move semantics | Improves efficiency for transferable resources | Requires strong ownership understanding |
| structured bindings | Readable unpacking of tuple-like values | Still needs meaningful variable naming |
- Modern C++ aims to express intent more clearly and safely.
- Convenience features work best when they preserve type understanding.
- Performance-oriented features still require ownership awareness.
Interview Questions
- When is auto helpful and when can it reduce clarity?
- What problem do move semantics solve?
- Why do modern C++ features still require discipline rather than blind adoption?
Chapter 11: File I/O, Errors, and Defensive Coding
Production C++ must protect itself against bad inputs, missing files, failed operations, and invalid state transitions. This chapter connects I/O, error handling, and defensive checks so programs stay trustworthy outside ideal classroom inputs.
11.1 File Example
#include
#include
using namespace std;
ofstream out("report.txt");
out << "C++ course completed" << endl;
out.close();
ifstream in("report.txt");
string line;
getline(in, line);
cout << line << endl; 11.2 Defensive Thinking
| Practice | Benefit | Risk If Ignored |
|---|---|---|
| Check stream state | Detect file open or read failures early | Silent bad data flow |
| Validate assumptions | Prevents undefined or misleading behavior | Edge-case crashes and corruption |
| Prefer clear invariants | Supports maintainability and debugging | Hard-to-trace state bugs |
- Check whether resources opened successfully before processing them.
- Guard assumptions close to the point where they matter.
- Treat undefined behavior as a design failure, not a harmless corner case.
Interview Questions
- Why should stream state be checked in C++ file handling?
- What is undefined behavior and why is it especially important in C++?
- How does defensive coding reduce debugging time?
Chapter 12: Capstone C++ Project
A strong C++ capstone should demonstrate disciplined ownership, reasonable abstractions, clear data structures, and good library use. The point is not to write the most complex code possible. It is to write performant, safe, understandable software with control over behavior and resources.
12.1 Project Scope
- Design a multi-file native application with clear separation of responsibilities.
- Use STL containers, algorithms, and classes rather than purely manual structures.
- Apply RAII and safe ownership patterns.
- Include file I/O, validation, and defensive handling of errors or edge cases.
- Good C++ code balances power with safety and clarity.
- Ownership design is as important as algorithm correctness.
- The best capstones show controlled complexity, not unnecessary complexity.
Interview Questions
- How would you structure a medium-sized C++ project for maintainability?
- What ownership decisions matter most in modern C++ design?
- How do you balance performance, clarity, and safety in C++?