Ch17: Classes¶
TL;DR: Encapsulation: Classes bundle variables and behaviors. Access specifiers (
public,private) restrict visibility. Members default toprivatein classes andpublicin structs. | C++ compiles each.cppfile independently into an object file; headers (.h) share declarations between files, and the linker stitches everything together at the end — mixing this up causes either "undefined reference" or "multiple definition" errors.
⚡ Quick Reference¶
#include <iostream>
#include <string>
class MemberClass {
public:
MemberClass() = default;
};
// Friend class forward declaration
class FriendClass;
class ClassName {
private:
// Private default variables encapsulated
int m_val {0};
mutable int m_counter {0}; // mutable: bypasses const constraints
public:
// ClassName() = default; - compiler-generated default constructor
ClassName() = default;
// ClassName() = delete; - deletes a constructor to block use
ClassName(double) = delete;
// Parameterized constructor using an initializer list
ClassName(int val) : m_val(val) {}
// Constructor delegation
ClassName(int val, double) : ClassName(val) {}
// Copy constructor representing deep copy
ClassName(const ClassName& other) : m_val(other.m_val) {}
// Destructor called automatically upon cleanup
~ClassName() {}
// static - shared variable across all instances
inline static int s_shared_val = 42; // inline static (C++17)
// const method: promises not to modify class state
int get_value() const {
m_counter++; // allowed because of mutable
return m_val;
}
// return *this; - returns self reference to enable method chaining
ClassName& set_value(int val) {
m_val = val;
return *this;
}
// Friend function granted private access
friend void print_private(const ClassName& obj);
};
// Friend function definition
void print_private(const ClassName& obj) {
std::cout << obj.m_val << std::endl;
}
// struct - default access specifier is public
struct Point {
int x;
int y;
};
int main() {
ClassName obj(10);
obj.set_value(20).get_value(); // Dot notation & method chaining
ClassName* p_obj = &obj;
p_obj->get_value(); // Arrow notation
const ClassName const_obj(5); // const object
Point p{1, 2};
auto [x, y] = p; // Structured bindings (C++20)
return 0;
}
Build Model & Separate Compilation¶
// ---- math_utils.h (header: declarations only) ----
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int square(int x); // declaration only, no body
#endif // include guard prevents double-inclusion
// ---- math_utils.cpp (translation unit: definition) ----
#include "math_utils.h"
int square(int x) { // actual implementation lives here
return x * x;
}
// ---- main.cpp (another translation unit) ----
#include "math_utils.h"
#include <iostream>
int main() {
std::cout << square(5) << '\n'; // linker resolves this call to math_utils.cpp
return 0;
}
// Build steps (conceptually):
// g++ -c math_utils.cpp -o math_utils.o <- compile
// g++ -c main.cpp -o main.o <- compile
// g++ math_utils.o main.o -o app <- link
🧠 Core Concepts¶
- Encapsulation: Classes bundle variables and behaviors. Access specifiers (
public,private) restrict visibility. Members default toprivatein classes andpublicin structs. - Object Lifecycle: Constructors (parameterized or default) initialize member states, while destructors (
~ClassName) perform cleanup and release heap allocations. - Const Correctness & Shared Members: Const objects call only const methods. Static members are shared across all class instances, representing class-level state.
- Translation Unit: Each
.cppfile, after the preprocessor expands its#includes, becomes one translation unit compiled independently into an object file (.o). - Declaration vs Definition: A declaration (in
.h) tells the compiler a function/variable exists; a definition (in.cpp) provides the actual implementation. Headers should contain declarations so multiple.cppfiles can share them without conflict. - The Linker: After all translation units are compiled separately, the linker combines the object files and resolves calls between them into a single executable — this is where "undefined reference" and "multiple definition" errors originate, not compile-time.
- Include Guards:
#ifndef/#define/#endif(or#pragma once) prevent a header from being processed twice within the same translation unit, which would otherwise cause duplicate declaration errors.
⚠️ Pitfalls (Quick Scan)¶
| Mistake | Fix |
|---|---|
| Forgetting the semicolon after a class definition | Always end class declarations with a semicolon (;) |
| Accessing private class members directly from outside code | Use public: specifier or getters/setters to expose members |
| Attempting parameterless instantiation after defining parameterized constructors | Explicitly add ClassName() = default; to restore default instantiation |
| Making constructors private in normal classes | Keep constructors public unless implementing specific patterns like Singleton |
| Using default memberwise copy constructor with raw pointer members | Write custom copy constructors to allocate new memory and perform deep copies |
| Calling non-const methods on const objects | Mark read-only member functions with the const suffix |
| Returning pointers or references to local variables from const methods | Return values by value or return references to class member variables |
| Returning mutable references from getter methods | Overload getters to return const references for const access |
| Allowing single-argument constructors to perform implicit conversions | Declare single-argument constructors as explicit |
| Assigning values in constructor body instead of using initializer lists | Always initialize members using constructor initializer list syntax |
Using static linkage keyword (static) for internal functions |
Wrap helper functions inside anonymous namespaces to achieve internal linkage |
| Defining a non-inline symbol in multiple header files without inline modifiers | Place declarations in headers and definitions in single source files, or use the inline keyword |
| Defining a function body directly in a header | Only declare in .h; define in the matching .cpp (unless inline) |
Missing include guard / #pragma once |
Add one to every header to prevent double-inclusion errors |
| Forgetting to link an object file | Ensure every .cpp producing needed symbols is compiled and linked |
| Declaring a function but never defining it anywhere | Compiles fine, fails at link with "undefined reference" |
Defining the same non-inline function in two .cpp files |
Causes "multiple definition" at link time — keep definitions in exactly one .cpp |
📖 Full Details¶
Cause → Effect → Fix with timestamp (click to expand)
* **Forgetting the semicolon after a class definition** -> **Compile-time syntax errors** -> **Always end class declarations with a semicolon (`;`) (50:00)** * **Accessing private class members directly from outside code** -> **Compile-time error stating that members are inaccessible** -> **Use `public:` specifier or getters/setters to expose members (51:00)** * **Attempting parameterless instantiation after defining parameterized constructors** -> **Compile-time error because compiler stops generating default constructor** -> **Explicitly add `ClassName() = default;` to restore default instantiation (52:00)** * **Making constructors private in normal classes** -> **Compile-time error because client code cannot instantiate objects** -> **Keep constructors public unless implementing specific patterns like Singleton (53:00)** * **Using default memberwise copy constructor with raw pointer members** -> **Multiple objects share the same pointer address, causing double-free crashes on destruction** -> **Write custom copy constructors to allocate new memory and perform deep copies (54:00)** * **Calling non-const methods on const objects** -> **Compile-time error because const objects cannot invoke state-modifying actions** -> **Mark read-only member functions with the `const` suffix (56:00)** * **Returning pointers or references to local variables from const methods** -> **Undefined behavior because local variables are destroyed upon return** -> **Return values by value or return references to class member variables (57:00, 58:00)** * **Returning mutable references from getter methods** -> **Allows callers to bypass private encapsulation and modify internal state** -> **Overload getters to return const references for const access (59:00)** * **Allowing single-argument constructors to perform implicit conversions** -> **Silent creation of temporary objects from matching primitive types, causing logical errors** -> **Declare single-argument constructors as `explicit` (60:00)** * **Assigning values in constructor body instead of using initializer lists** -> **Members are default-initialized first then assigned, causing performance overhead and compile errors for const/ref members** -> **Always initialize members using constructor initializer list syntax (62:00)** * **Using static linkage keyword (`static`) for internal functions** -> **Works but `static` is deprecated for non-member internal functions in modern standards** -> **Wrap helper functions inside anonymous namespaces to achieve internal linkage (64:00)** * **Defining a non-inline symbol in multiple header files without inline modifiers** -> **Linker error stating multiple definitions of the same symbol** -> **Place declarations in headers and definitions in single source files, or use the `inline` keyword (65:00)** * **Defining a function body directly in a header without `inline`** -> **Each `.cpp` that includes the header gets its own copy, causing "multiple definition" linker errors** -> **Keep only declarations in headers; put the body in one `.cpp`, or mark it `inline` if it truly must live in the header** * **Missing include guard / `#pragma once`** -> **If the header gets included twice in one translation unit (common with nested includes), the compiler sees duplicate declarations** -> **Always wrap headers in `#ifndef/#define/#endif` or use `#pragma once`** * **Forgetting to link an object file that defines a needed symbol** -> **Compilation succeeds per-file, but the final link step fails with "undefined reference to ..."** -> **Make sure every `.o` file containing a called function is included in the final link command** * **Declaring a function in a header but never defining it in any `.cpp`** -> **Every file that includes the header compiles fine individually, but linking fails since no implementation exists** -> **Write the corresponding definition in exactly one `.cpp` file** * **Defining the same non-`inline` function identically in two `.cpp` files** -> **Linker finds two symbols with the same name and refuses to pick one — "multiple definition" error** -> **Ensure exactly one translation unit defines any given non-inline function or global variable**📎 Repo Files¶
26.Classes/26.2YourFirstClass/main.cpp26.Classes/26.3Constructors/main.cpp26.Classes/26.4DefaultedConstructors/main.cpp26.Classes/26.5SettersAndGetters/main.cpp26.Classes/26.6ClassAcrossMultipleFiles/26.Classes/26.8ManagingClassObjectsThroughPointers/main.cpp26.Classes/26.9Destructors/main.cpp26.Classes/26.10OrderOfConstructorDestructorCalls/main.cpp26.Classes/26.11ThisPointer/main.cpp26.Classes/26.12Struct/main.cpp26.Classes/26.13.SizeOfClassObjects/main.cpp27.ZoomingInOnClassObjects/27.2ConstObjects/27.ZoomingInOnClassObjects/27.3ConstObjectsAsFunctionParameters/27.ZoomingInOnClassObjects/27.4ConstMemberFunctions/27.ZoomingInOnClassObjects/27.5GettersThatDoubleAsSetters/27.ZoomingInOnClassObjects/27.6DanglingPointersAndReferences/27.ZoomingInOnClassObjects/27.8MutableObjects/27.ZoomingInOnClassObjects/27.9StructuredBindings/28.DivingDeepIntoConstructorsAndInitialization/28.2DefaultParametersForConstructors/28.DivingDeepIntoConstructorsAndInitialization/28.3InitializerListsForConstructors/28.DivingDeepIntoConstructorsAndInitialization/28.5ExplicitConstructors/28.DivingDeepIntoConstructorsAndInitialization/28.6ConstructorDelegation/28.DivingDeepIntoConstructorsAndInitialization/28.7CopyConstructors/28.DivingDeepIntoConstructorsAndInitialization/28.8ObjectsStoredInArraysAreCopies/28.DivingDeepIntoConstructorsAndInitialization/28.10MoveConstructors/28.DivingDeepIntoConstructorsAndInitialization/28.11DeletedConstructors/29.Friends/29.2FriendFunctions/29.Friends/29.3FriendClasses/30.ConstAndStaticMembers/30.3StaticMemberVariables/30.ConstAndStaticMembers/30.4InlineStaticMemberVariables/30.ConstAndStaticMembers/30.9StaticMemberFunctions/30.ConstAndStaticMembers/30.11InClassMemberVariableInitialization/31.Namespaces/31.2CreatingNamespaces/31.Namespaces/31.6UsingDeclarations/31.Namespaces/31.7AnonymousNamespaces/32.ProgramsWithMultipleFiles/32.2CompilingAndLinking_Model/32.ProgramsWithMultipleFiles/32.4DeclarationsAndDefinitions/32.ProgramsWithMultipleFiles/32.5OneDefinitionRule/32.ProgramsWithMultipleFiles/32.6Linkage/32.ProgramsWithMultipleFiles/32.9InlineVariablesAndFunctions/32.ProgramsWithMultipleFiles/main.cpp