C++ Fundamentals
A Complete 9-Chapter Programming Course
Table of Contents
- Getting Started
- Variables, References & Basic Types
- Functions & Overloading
- Classes & Objects
- Constructors, Destructors & RAII
- Operator Overloading
- Inheritance & Polymorphism
- References vs. Pointers, In Depth
- Namespaces & the Standard Library Tour
Getting Started
c3-6 named this course directly: the next step once OOP is layered on top of everything the C track already covers. Most of what you already know still applies. This chapter is about the specific places it doesn't.
g++ and clang++
The same underlying toolchains as c1-1's gcc/clang, extended for C++. The situation around build tooling hasn't changed either โ no official package manager or build system, exactly as before; make/CMake still fill that gap, and c2-5's own Makefile material transfers directly, unchanged.
What's Source-Compatible With C
A large majority of valid C code compiles as valid C++, unchanged: variables, control flow, functions, arrays, pointers, and plain structs all carry over. Everything the C track covered about manual memory, bounds checking, and pointer discipline still applies at the same level as before โ none of that gets replaced, only added to.
What's Not Compatible
A concrete, common example: in C, malloc's void * return implicitly converts to any pointer type. In C++, it does not โ an explicit cast is required.
This reflects a broader theme worth noting immediately: C++'s type checking is stricter than C's in several places, not looser.
iostream vs. stdio
cout/cin/cerr are C++'s own I/O streams โ using << and >>, which are genuinely overloaded operators (Chapter 6 covers writing your own), not format-string parsing the way printf/scanf work. Both remain fully usable in C++ โ <cstdio> still works โ but iostream is the idiomatic choice.
The Compile-Then-Link Model, Unchanged
c1-1's compile-then-link model transfers completely unchanged โ same two conceptual steps, same idea of object files and linking. The only visible difference is convention: C++ source files typically use a .cpp extension, and g++ replaces gcc.
The Smallest C++ Program
Compare directly against c1-1's own hello world โ the shape is identical; only the I/O mechanism changed.
| Concept | C | C++ |
|---|---|---|
| Print a value | printf("%d\n", x) | std::cout << x << std::endl; |
| void* implicit conversion | allowed | requires an explicit cast |
| File extension convention | .c | .cpp |
| Compiler | gcc / clang | g++ / clang++ |
void *-to-typed-pointer implicit conversion difference is a genuine, common trap when porting C code to C++ or mixing the two โ code that compiled cleanly as C can fail to compile once treated as C++, specifically because C++'s type checking is stricter here.
Coding Challenges
Write a hello-world program using iostream that prints two separate lines with two separate std::cout statements, compile it with g++, and run it.
๐ View solutionTake a small C program using malloc without an explicit cast on its return value, and compile it first as C (gcc), then attempt to compile the identical file as C++ (g++). Report what happens in each case.
๐ View solutionExplain why the malloc void* conversion difference is evidence that C++ is, in at least this respect, MORE strictly typed than C โ not simply "different" โ and why that's a genuinely counter-intuitive fact given C++ is usually introduced as C "with more features."
๐ View solutionChapter 1 Quick Reference
g++/clang++โ same underlying toolchains as C's own compilers, extended- Most valid C compiles as valid C++ unchanged โ the manual-control discipline carries forward
void *requires an explicit cast to a typed pointer in C++ โ not implicit, unlike Ciostream'scout/cinuse overloaded<</>>, not format strings โstdiostill works too- The compile-then-link model and
c2-5's Makefile material transfer completely unchanged - Next chapter: variables, references, and a genuine bool primitive from day one
Variables, References & Basic Types
c1-3 spent real time on C's historical lack of a true boolean. This chapter opens by closing that gap for good โ then introduces something the C track never had at all.
A Genuine bool From Day One
C had no boolean type until C99, and even then, bool is a macro over _Bool โ genuinely just an integer in disguise. C++ has had a real, distinct bool primitive since its very first standard. true and false are genuine keyword literals of their own type โ not macros standing in for 1 and 0.
References โ A New Concept Beyond Pointers
A reference is an alias for an existing variable โ not a separate object with its own address the way a pointer is.
Two real, permanent constraints: a reference must be initialized the moment it's declared โ there's no such thing as an uninitialized or null reference the way there's an uninitialized or null pointer โ and once bound, it can never be rebound to refer to something else.
References vs. Pointers
| Concept | Reference | Pointer |
|---|---|---|
| Syntax at use site | used exactly like the variable itself | requires * to dereference |
| Can it be null? | no | yes โ NULL is valid |
| Can it be reassigned to something else? | no โ bound permanently at initialization | yes โ freely |
| Must be initialized immediately? | yes | no โ can be declared, then assigned later |
cpp1-8 covers exactly when to reach for each in real code โ this is just the first introduction.
auto Type Inference
auto lets the compiler deduce a variable's type from its initializer. C has no equivalent at all โ every variable's type is always written explicitly. This is genuinely a point where C++ and Rust agree (Rust's let does the same thing), unlike most of the C track's own C-vs-Rust contrasts.
const in C++
Used far more pervasively and idiomatically in C++ than in C. A const variable's value cannot change after initialization โ a full dedicated chapter on const-correctness comes later (cpp2-8); for now, just the basic qualifier.
| Concept | C | C++ |
|---|---|---|
| Boolean type | _Bool/bool since C99 โ really an int | bool โ a real, distinct primitive since the first standard |
| Alias for a variable | not available | references (&) โ new in this course |
| Type inference | none โ always explicit | auto โ genuinely matches Rust's own let |
auto is genuinely useful for verbose or obvious types (iterator types especially, covered later) โ but overusing it to the point where a reader can't tell a variable's type at a glance is a real, debated style tradeoff even within the C++ community.
int &ref; with no initializer simply doesn't compile.
Coding Challenges
Declare an int variable, create a reference to it, modify the value through the reference, and print the original variable directly to confirm it changed โ using cout, not printf.
๐ View solutionDeclare three variables using auto โ an int literal, a double literal, and a string literal โ and print each one's value along with, using typeid or a comment, what type auto actually deduced.
๐ View solutionExplain why a reference cannot be null while a pointer can, tying your answer back to what a reference actually IS (an alias) rather than a separate object with its own address.
๐ View solutionChapter 2 Quick Reference
bool/true/falseโ a genuine primitive and real literals, since C++'s first standardint &ref = x;โ an alias for an existing variable, not a separate object- References: no null state, no rebinding, must be initialized immediately โ all unlike pointers
autoโ type inference from the initializer, genuinely matching Rust's ownletconstโ used pervasively in idiomatic C++; full correctness rules come incpp2-8- Next chapter: functions and overloading โ something C structurally cannot do at all
Functions & Overloading
c1-5's functions had one name each, always. This chapter introduces the first genuinely new C++ capability that has no C equivalent at all โ not a missing feature, a structural impossibility in C's own linking model.
Function Overloading
Multiple functions can share the same name, distinguished by their parameter types or count โ the compiler picks the right one based on the actual arguments at each call site.
C cannot do this at all. C compiles every function name directly to one plain, unmangled linker symbol โ two functions named print in the same C program is simply a redefinition error, per c2-5's own linking material.
Name Mangling, Briefly
C++ solves this by encoding parameter types into the actual linker symbol name. print(int) and print(double) become genuinely different symbols under the hood โ something like _Z5printi and _Z5printd.
This directly extends c2-5's own linker material: overloading isn't possible because the linker got smarter โ it's possible because C++ generates different names for what looks, in source, like the same function name.
extern "C"
A real, practical need: calling a C library from C++, or being called from C code, requires plain, unmangled names. extern "C" tells the compiler to use C's own naming rules for specific declarations โ this is exactly why standard C headers are internally wrapped in extern "C" when included from C++.
Default Arguments
A genuinely new feature: parameters can have default values, letting a function be called with fewer arguments than it declares.
C has no equivalent at all โ the closest workaround is a variadic function, or several separately-named functions.
Overload Resolution Ambiguity
If a call could plausibly match more than one overload after implicit conversions, the compiler reports an ambiguous call error rather than guessing.
| Concept | C | C++ |
|---|---|---|
| Two functions, same name | redefinition error | valid โ overloading, resolved by parameter types |
| Linker symbol names | plain function name | mangled โ encodes parameter types |
| Fewer arguments than declared | not possible โ variadic functions or multiple names only | default arguments |
extern "C" is needed specifically because name mangling would otherwise make a C++-compiled function unreachable from C's own, unmangled linker expectations.
Coding Challenges
Write two overloaded describe functions, one taking an int and one taking a std::string, each printing a different message. Call both from main and confirm the correct overload is selected each time.
๐ View solutionWrite a function power(int base, int exponent = 2) with a default argument, and call it once with both arguments and once with only the base, printing both results.
๐ View solutionExplain precisely why C cannot support function overloading at all โ tying your answer to how C's linker resolves symbol names โ and why extern "C" is necessary when C++ code needs to interoperate with C.
๐ View solutionChapter 3 Quick Reference
- Overloading โ same function name, different parameter types/count, resolved at the call site
- Name mangling โ the compiler encodes parameter types into the real linker symbol, making overloading possible
extern "C"โ forces plain, unmangled C-style linkage for interop with C libraries/callers- Default arguments โ trailing parameters only; can't skip a middle one and provide a later one
- An ambiguous overload call is a compile error, never a silent guess
- Next chapter: classes and objects โ the leap beyond plain structs
Classes & Objects
c2-1 named this chapter's whole subject directly: "one way C approximates method-like dispatch without a language feature for it at all." This chapter is that language feature.
From struct to class
c2-1's C struct was pure data โ no methods, ever; any function operating on it had to be written separately, taking a pointer as its first argument. A C++ class bundles data and methods together directly, inside the same definition, called with object.method() syntax instead of function(&object).
A First Class
Compare directly against c2-7's own hand-rolled Circle struct with a manually-wired function pointer. Same idea โ the language now does the wiring for you.
public and private Access Control
A genuinely new concept: members can be hidden from outside code entirely. A class's members are private by default; a struct's are public by default โ the only real difference between the two keywords in C++, worth naming explicitly since it's a common point of confusion.
This is enforced by the compiler โ unlike C, where a struct's members are always fully exposed, with zero protection of any kind.
Constructors
A special member function that runs automatically when an object is created โ replacing the "manually call an init function" pattern C never even had a formal concept for. Multiple constructors are possible, reusing cpp1-3's own overloading rules directly.
Destructors
A special member function that runs automatically when an object is destroyed โ a genuine preview of RAII, this course's own central chapter, coming next.
this โ A Hidden Parameter
Inside a member function, this is a pointer to the object the method was called on โ exactly the explicit self/void * parameter c2-7 required passing by hand in plain C. The compiler now supplies it invisibly, every call.
| Concept | C (c2-1/c2-7) | C++ class |
|---|---|---|
| Data + behavior | separate โ struct, plus functions taking a pointer | bundled together directly |
| Hidden self/object parameter | passed explicitly, by hand | this โ supplied automatically |
| Access control | none โ every member always fully exposed | public/private, compiler-enforced |
| Initialization | a separate, manually-called init function, by convention | a constructor, run automatically |
struct for pure-data types specifically as a signal to readers, purely by convention.
account.balance from outside Account's own methods genuinely fails to compile โ a real, enforced boundary, not a convention the compiler merely suggests.
Coding Challenges
Write a Rectangle class with private width and height members, a constructor taking both values, and a public area() method. Create an instance and print its area.
๐ View solutionAdd a destructor to the Rectangle class from Challenge 1 that prints a message when an instance is destroyed. Create an instance inside a nested scope (an inner {}) and observe when the destructor's message actually prints.
๐ View solutionExplain precisely what the this pointer is standing in for, tying your answer directly back to c2-7's own hand-rolled vtable-style dispatch pattern and what parameter that pattern required the programmer to pass explicitly.
๐ View solutionChapter 4 Quick Reference
- A class bundles data and methods together โ resolving what
c2-1explicitly foreshadowed classโ private by default;structโ public by default; otherwise identical- A constructor runs automatically on creation; a destructor runs automatically on destruction โ RAII's own preview
thisโ the hidden object pointer, replacingc2-7's explicit self/void* parameter- Private-member access from outside the class is a genuine compile error, compiler-enforced
- Next chapter: Constructors, Destructors & RAII โ C++'s own answer to C's manual malloc/free discipline
Constructors, Destructors & RAII
c2-2 was the C track's own central chapter: manual malloc/free, with every allocation carrying real risk of a forgotten free. This chapter is C++'s answer โ and, genuinely, it's the same answer Rust would later give.
RAII โ Resource Acquisition Is Initialization
The core idea: tie a resource's lifetime directly to an object's lifetime. Acquire the resource in the constructor; release it in the destructor. When the object goes out of scope โ through any exit path, including an early return or an exception โ the destructor runs automatically, releasing the resource with zero remaining programmer effort at that point.
A Worked RAII Example โ A Simple Owning Wrapper
Compare this directly against c2-2's own discipline: "every allocation needs a matching free, and the programmer must remember every single time." Here, allocate once in the constructor, and never have to remember to free again โ the destructor handles it unconditionally, even during an exception unwind.
The Direct Rust Parallel
This is, genuinely, exactly Rust's Drop trait โ invented roughly fifteen years earlier, via manual class-writing instead of a language-level trait system. Rust's own ownership model (rust1-3) and smart pointers (rust2-2) were historically compared to, and influenced by, this exact C++ idiom. A real, rare moment of genuine convergent evolution between the two languages, unlike most of the C track's own contrasts.
new and delete
C++'s own heap-allocation operators โ a real, concrete improvement over C's malloc/free. new allocates and calls the constructor automatically; delete calls the destructor and frees the memory. Raw malloc/free know nothing about object lifecycles at all โ just bytes.
Mismatching them โ new with delete[], or vice versa โ is undefined behavior, the exact same category c3-4 covered in full.
The Rule of Three, Previewed
A class managing a resource manually generally needs a destructor, a copy constructor, and a copy assignment operator together โ or copies of the object risk a double-free exactly like c2-3's own bug catalog. Full depth arrives in cpp2-8; this is just the forward pointer.
Exception Safety, Briefly
RAII's other major payoff, previewed ahead of cpp2-6's own Exception Handling chapter: because destructors run on any scope exit โ including an exception unwinding through the call stack โ RAII-managed resources are cleaned up automatically even when an error is thrown mid-function. C's manual cleanup pattern has no equivalent mechanism at all; C has no exceptions and no automatic stack unwinding.
| Concept | C (c2-2) | C++ RAII | Rust |
|---|---|---|---|
| Resource release | manual free() โ programmer must remember | automatic โ destructor on scope exit | automatic โ Drop on scope exit |
| Cleanup on an error mid-function | no mechanism โ manual only | automatic โ destructors run during unwinding | automatic โ Drop runs even on panic unwind |
| Allocation + initialization | two separate manual steps (malloc, then init) | one step โ new calls the constructor | one step โ the constructor pattern is the norm |
IntArray is a teaching device. Course 2's smart pointers (unique_ptr/shared_ptr) are the standard, battle-tested RAII wrappers real code should reach for โ reinventing them by hand is rarely the right call outside a learning exercise.
cpp2-6 introduces exceptions in full.
Coding Challenges
Write a class that acquires a resource (e.g. allocates an int array with new[]) in its constructor and releases it with delete[] in its destructor. Create an instance inside a nested scope and print a message from both the constructor and destructor to observe the order they run in.
๐ View solutionAllocate an object with new, then intentionally never call delete on it. Explain, referencing c2-2's own material, what class of bug this is and why RAII alone doesn't protect against this specific mistake.
๐ View solutionExplain precisely why RAII is considered the same underlying idea as Rust's Drop trait, and name the one concrete mechanical difference between how each language actually triggers the cleanup code.
๐ View solutionChapter 5 Quick Reference
- RAII โ acquire in the constructor, release in the destructor, tied to object lifetime
new/deleteโ allocate+construct / destruct+free in one step, unlike raw malloc/freenew[]must be matched withdelete[]โ mismatching is undefined behavior- Destructors run on any scope exit, including exception unwinding โ C has no equivalent mechanism at all
- This is genuinely the same idea as Rust's
Drop, roughly fifteen years earlier - Next chapter: operator overloading โ giving custom types +, ==, and <<
Operator Overloading
Every std::cout << ... line since cpp1-1 has quietly relied on a mechanism this chapter finally names directly.
Why Operator Overloading
A Vector2D class: adding two vectors should read naturally as v1 + v2, not add(v1, v2). C has no equivalent mechanism at all โ every "operation" on a struct must be a plainly-named function call, always.
Overloading +
Written as a member function, this supplies the left operand implicitly; other is the right one.
Overloading ==
Returns a genuine bool โ cpp1-2's own real boolean primitive, put to direct use.
Overloading << for Output
A real exception to the pattern so far: operator<< must be a free function, not a member โ the left operand is std::ostream&, not the class itself, so it can't be written as a member of Vector2D. A friend declaration lets the free function reach the class's private members.
Which Operators Can/Can't Be Overloaded
Most can โ arithmetic, comparison, stream, subscript [], function-call (), and more. A small, fixed set genuinely cannot: ::, ., .*, ?:, sizeof โ a real exclusion list, not an arbitrary restriction.
The Real Payoff โ This Is What cout << Already Uses
Here's the reveal: std::cout << 5 is itself a call to operator<<(ostream&, int) โ an overload of exactly this same operator, just for a built-in type instead of a custom one. Every cout statement since Chapter 1 has been using this mechanism the entire time.
| Concept | C | C++ |
|---|---|---|
| Custom "addition" for a type | a plainly-named function โ add(a, b) | operator+ โ a + b reads naturally |
| Left operand isn't the class | n/a โ no operator concept at all | requires a free function, not a member |
+ for something genuinely addition-like โ never for an unrelated operation just because the syntax happens to be convenient. A real style principle, sometimes called the principle of least astonishment.
operator<< as a member function of the class itself is a genuine, common early confusion โ the left-hand operand is ostream&, which isn't the class being extended, so member-function syntax simply doesn't fit.
Coding Challenges
Write a Vector2D class with x and y members, overload operator+ as a member function, and print the sum of two vectors' x and y components after adding them.
๐ View solutionAdd operator== to the Vector2D class from Challenge 1, and test it against two vectors with identical components and two with different ones, printing the boolean result of each comparison.
๐ View solutionExplain precisely why operator<< for a custom class cannot be written as a member function, tying your answer to which operand is on the left-hand side of the << expression.
๐ View solutionChapter 6 Quick Reference
operator+/operator==โ usually member functions,thisas the implicit left operandoperator<<โ must be a free function, since the left operand isostream&, not the classfriendโ lets a free function access a class's private members- A fixed, small set of operators genuinely cannot be overloaded โ
::,.,.*,?:,sizeof std::cout << 5already uses this exact mechanism โ anoperator<<overload forint- Next chapter: inheritance and polymorphism โ making real the vtable mechanism
c2-7built by hand
Inheritance & Polymorphism
c2-7 ended by naming this chapter directly: what a struct of function pointers, wired up by hand, actually becomes once C++ does it for you. This is that reveal, in full.
Base and Derived Classes
Without virtual โ The Problem
A genuine, surprising gotcha: calling area() through a Shape * pointing at a Circle calls Shape's own area(), not Circle's. This is static binding โ resolved at compile time, based on the pointer's declared type, not the actual object it points to.
virtual โ Making Dispatch Real
Adding virtual changes everything: dispatch now resolves at runtime, based on the actual object's real type. This is exactly c2-7's own hand-built mechanism, made real by the language.
The vtable, Named Directly
Under the hood, a class with virtual functions gets a hidden vtable โ a table of function pointers, exactly c2-7's own hand-rolled Circle struct with its own function-pointer member. Every object of such a class carries a hidden pointer to its class's vtable. Calling a virtual function through a base pointer looks up the real function through this table, at runtime โ literally the same shape->area(shape) code c2-7 wrote by hand, now generated automatically.
override and Pure Virtual Functions
override (C++11+) is a safety check confirming a derived function genuinely overrides a base virtual โ catching typos or signature mismatches as compile errors instead of silently creating an unrelated function.
= 0 marks a pure virtual function โ the class becomes abstract, unable to be instantiated directly, requiring every derived class to provide its own implementation.
| Concept | C (c2-7) | C++ |
|---|---|---|
| Dispatch table | hand-written struct of function pointers | vtable โ generated automatically |
| Wiring a function pointer correctly | the programmer's own responsibility, unchecked | the compiler builds it, guaranteed correct |
| Forcing an implementation to exist | no mechanism โ a NULL entry is UB when called | = 0 (pure virtual) โ a compile-time requirement |
virtual, calls the wrong function, and gives no warning by default. C, by contrast, never pretends to have automatic dispatch at all โ c2-7's own struct-of-function-pointers approach fails loudly (a NULL call) rather than silently calling the wrong thing.
Coding Challenges
Write a Shape base class with a non-virtual area() returning 0, and a Circle derived class overriding it. Call area() through a Shape* pointing at a Circle and report what actually gets called.
๐ View solutionAdd virtual to Shape::area() from Challenge 1 (and override to Circle::area()), then run the identical Shape* call again. Report how the result changed and why.
๐ View solutionExplain precisely what the vtable is, and why calling a virtual function through a base-class pointer is described as "the exact code c2-7 built by hand, generated automatically" โ name the specific piece c2-7 wrote manually that the vtable now replaces.
๐ View solutionChapter 7 Quick Reference
class Derived : public Base { }โ inherits members and methods- Without
virtualโ static binding, resolved by the pointer's declared type at compile time - With
virtualโ dynamic binding, resolved by the actual object's type at runtime - The vtable โ a hidden, compiler-generated table of function pointers, exactly
c2-7's own hand-rolled mechanism overrideโ compile-time safety check;= 0โ pure virtual, makes a class abstract- Always give a base class with virtual functions a virtual destructor
- Next chapter: references vs. pointers, in depth โ when to reach for each
References vs. Pointers, In Depth
cpp1-2 introduced references structurally. This chapter is the practical guidance โ when to reach for each โ and the honest comparison against what Rust's own references actually guarantee.
Recap: The Structural Differences
| Property | Reference | Pointer |
|---|---|---|
| Can be null | no | yes |
| Can be reassigned | no โ bound permanently | yes |
| Must be initialized immediately | yes | no |
When to Use a Reference
Primarily function parameters: passing a large object by reference avoids an expensive copy while still letting the function read or modify the caller's actual object. Return types too โ but never return a reference to a local variable; that's the exact reference-shaped version of c1-7's own dangling-pointer warning.
const Reference โ Pass-By-Reference Without Copying, Safely
const T& is the real, idiomatic C++ default for passing anything nontrivial into a function โ it avoids the copy plain pass-by-value would make, while const guarantees the function can't accidentally modify the caller's object. Genuinely the most common parameter-passing pattern in real C++ code.
When to Use a Pointer
- "No object" (null) is a genuinely valid state to represent
- The thing being pointed to needs to be reassigned to point elsewhere later
- Pointer arithmetic โ iterating a raw array, exactly
c1-6/c1-7's own territory - Ownership semantics via smart pointers โ previewed for Course 2
Contrasted With Rust's Borrow Rules
Rust's &/&mut references are checked at compile time by the borrow checker for aliasing rules โ you cannot have a mutable reference and any other reference to the same data alive simultaneously. C++ references have none of these guarantees enforced at all: a const T& and a plain T& to the same object can coexist freely, and nothing stops two non-const references from both mutating the same data unsafely โ including from two different threads, tying directly to cpp3-4's own concurrency chapter. This is a real, genuine gap this course's own framing has been building toward: C++ references solve C's raw-pointer ergonomics problem โ no dereference syntax, no null, no rebinding โ but not its safety problem.
| Concept | C++ Reference | C++ Pointer | Rust Reference |
|---|---|---|---|
| Aliasing rules enforced? | no | no | yes โ compile-time borrow checking |
| Mutable + shared reference coexisting | allowed, unchecked | allowed, unchecked | compile error |
| Ergonomics vs. a raw pointer | better โ no null, no dereference syntax | n/a | better, and safety-checked |
const T& is the idiomatic starting point in real C++ โ deviate only with a specific reason (needing to modify the caller's object, or genuinely wanting a cheap copy for a small type).
c1-7's own dangling-pointer warning, now in reference form.
Coding Challenges
Write a function that takes a std::string by const reference and prints its length. Call it with a string literal and confirm no copy-related side effects are needed to make it work.
๐ View solutionWrite a function that deliberately returns a reference to one of its own local variables. Explain what happens when the caller tries to use the returned reference, tying your answer to c1-7's own dangling pointer material.
๐ View solutionExplain precisely what Rust's borrow checker would reject that C++ freely allows, using a concrete scenario involving a mutable reference and a second reference to the same data โ and why this specific gap is described as C++ solving pointer "ergonomics" but not "safety."
๐ View solutionChapter 8 Quick Reference โ Almost Course 1 Complete
- Use a reference for function parameters/return values tied to an object that already exists elsewhere
const T&โ the idiomatic default: avoids a copy, guarantees no mutation- Use a pointer when null is valid, reassignment is needed, or for raw pointer arithmetic
- Never return a reference to a local variable โ the exact dangling-pointer bug, in reference form
- C++ references have no compile-time aliasing guarantees โ a real, genuine gap vs. Rust's borrow checker
- Next chapter: namespaces and the standard library tour โ closing Course 1
Namespaces & the Standard Library Tour
Every single example since cpp1-1 has written std::cout, never just cout. This closing chapter finally explains why โ and previews everything Course 2 is about to build on top of it.
The Global Namespace Problem
C has exactly one flat namespace for every function and variable name across an entire program. Two libraries both defining a function called process genuinely collide โ a real, historical C problem with no built-in solution.
std:: โ The Standard Library's Own Namespace
This is why every example so far has written std::cout: C++'s entire standard library lives inside a namespace called std, specifically so it never collides with anything the programmer names themselves.
Declaring Your Own Namespace
using namespace โ Convenient But Risky
using namespace std; brings every std:: name into scope unqualified โ genuinely convenient for small examples, but it reintroduces exactly the collision risk namespaces exist to prevent.
using namespace directive affects every single file that includes it, transitively โ a real, well-known C++ style rule, since it silently reintroduces global-namespace collision risk across an entire project, not just the one file that wrote it.
A Standard Library Tour, Previewing Course 2
A quick map of what's coming, not the full depth yet:
- std::string โ a real, growable string type, unlike C's own raw
chararrays - std::vector โ the direct answer to
c3-2's own "C has no standard containers" gap - std::map โ a built-in key-value structure, replacing
c3-2's own hand-rolled hash table
Course 2's own dedicated chapters cover each in full โ this is just naming what's ahead.
Closing Course 1
From cpp1-1's compile model through cpp1-8's honest safety-gap admission, one throughline: C++ adds real capability on top of C โ overloading, classes, RAII, polymorphism, safer references โ without adding Rust's own compile-time safety guarantees. That tension is exactly what Course 3's concurrency and UB chapters make fully concrete. Course 2, starting next, is about the practical, everyday tools โ templates, the STL, smart pointers, move semantics โ built directly on this foundation.
| Concept | C | C++ |
|---|---|---|
| Name collisions across libraries | a real, unsolved problem โ one flat namespace | namespaces โ names grouped, no collision |
| The standard library's own names | global โ printf, malloc, etc. | inside std:: โ cout, string, vector |
std:: explicitly, rather than reaching for using namespace std; even in a source file, is the safer default in any codebase beyond a small standalone example โ it costs a few extra characters and avoids the collision risk entirely.
Coding Challenges
Declare a namespace called geometry containing a function area(double radius) computing a circle's area. Call it fully qualified as geometry::area(...) and print the result.
๐ View solutionDeclare two separate namespaces, each containing a function named distance with a different implementation. Show that both can be called unambiguously when fully qualified, and explain what would happen if both were brought into scope with using namespace and called as plain distance(...).
๐ View solutionExplain why "using namespace X;" in a header file is considered worse practice than the identical directive in a single source (.cpp) file, tying your answer to how #include actually works per c2-4's own material.
๐ View solutionChapter 9 Quick Reference โ Course 1 Complete
- C has one flat namespace for every name; C++ groups names into namespaces to avoid collisions
std::โ the entire standard library's own namespace, why every example writes it explicitlyusing namespaceโ convenient, but reintroduces collision risk; never in a header file- Course 2 preview:
std::string,std::vector(resolvingc3-2's container gap),std::map - Course 1 complete. Course 2 builds templates, the STL, smart pointers, and move semantics directly on this foundation