02-Class CPP Tutorial
02-Class CPP Tutorial
3. C++ Encapsulation
Data Hiding, Getters/Setters, and Design Patterns
5. C++ Inheritance
Single, Multiple, Virtual, and Diamond Problem
6. C++ Polymorphism
Virtual Functions, Abstract Classes, and RTTI
8. Practical Exercises
Challenge Your Knowledge
9. Discussion Points
Advanced Topics for Senior Developers
inline Use for small, frequently called methods where function call overhead
Implementation matters. Modern compilers often make this decision automatically.
class SecurePaymentProcessor { Prefer non-member non-friend functions when possible to further reduce
private: coupling and exposure.
// Only accessible within this class
std::string m_encryptionKey;
double m_transactionFee;
Common Pitfalls
std::string encryptData(const std::string& data) {
// Proprietary encryption algorithm Over-exposing implementation details makes it harder to change internals
return encrypted_result; without breaking client code.
}
Making data members public for convenience prevents enforcing invariants and
protected: validations.
// Accessible by this class and derived classes
Over-using protected creates tight coupling between base and derived classes,
bool validateTransaction(const Transaction& tx) {
hindering independent evolution.
// Common validation logic
return isValid; Assuming privacy equals security — access modifiers are for maintainability, not
} security (compiled code still exposed).
Implementation patterns Consider fluent interfaces for operations that can be chained.
public: Overlooking thread safety in mutator methods when objects are shared.
// Constructor initializes with invariants
BankAccount(const std::string& accNum, double initialBalance = 0.0
: m_accountNumber(accNum), m_balance(initialBalance), m_frozen
Design Pattern Example
// Getter methods - read-only access to private data
double getBalance() const { // Immutable transaction record pattern
return m_balance; class Transaction {
} private:
const std::string m_type;
const double m_amount;
const std::string& getAccountNumber() const { const std::time_t m_timestamp;
return m_accountNumber;
} public:
Transaction(std::string type, double amount)
: m_type(std::move(type)),
bool isFrozen() const { m_amount(amount),
return m_frozen; m_timestamp(std::time(nullptr)) {}
}
// Read-only accessors, no setters at all
const std::string& type() const { return m_type; }
// Controlled mutation with validation double amount() const { return m_amount; }
bool deposit(double amount) { std::time_t timestamp() const { return m_timestamp; }
if (amount <= 0 || m_frozen) return false; };
This immutable object pattern ensures transaction records can't be altered after creation - perfect for audit
m_balance += amount; trails.
m_transactions.push_back(Transaction("deposit", amount));
return true;
}
Exercise
// Fluent interface pattern - method chaining Refactor this anti-pattern to proper encapsulation:
BankAccount& freeze() { class BadAccount {
m_frozen = true; public:
return *this; double balance; // Directly accessible!
} void setBalance(double b) { balance = b; }
};
BankAccount& unfreeze() {
m_frozen = false;
return *this;
}
};
C++ Friend Functions & Classes: When and Why?
// Forward declaration for friendship Forward declare friend classes to reduce compile-time dependencies.
class JsonSerializer;
class TradeOrder {
private: Common Pitfalls
int m_orderId;
double m_price; Overusing friendship — creates tight coupling and breaks encapsulation
std::string m_symbol; principles.
bool m_isBuyOrder;
Friendship is not inherited — derived classes don't inherit friendship
// Friend function - can access private members relationships.
friend std::ostream& operator<<(
Friendship is not transitive — if A is friend of B, and B is friend of C, A is not
std::ostream& os, const TradeOrder& order);
automatically friend of C.
// Friend class - entire class can access private members Friendship breaks unit testing isolation — harder to mock behavior when
friend class JsonSerializer; implementation is exposed.
public:
TradeOrder(int id, double price,
const std::string& symbol,
Discussion Points
bool isBuy)
: m_orderId(id), m_price(price), When is friend justifiable versus using public accessor methods?
m_symbol(symbol), m_isBuyOrder(isBuy) {}
}; How would you refactor to minimize friend declarations?
// Friend function implementation - direct access to private Demo: Implementing stream operators for complex financial types
std::ostream& operator<<(std::ostream& os,
const TradeOrder& order) { What alternatives exist when friendship causes maintenance problems?
return os << "Order #" << order.m_orderId
<< " [" << order.m_symbol << "]: "
<< (order.m_isBuyOrder ? "BUY" : "SELL")
<< " at $" << order.m_price;
}
// Pure virtual functions define the interface Object slicing — assigning derived objects to base objects by value loses derived
virtual bool canProcess(const std::string& dataType) const = 0; class data and behavior.
virtual void process(std::vector<double>& data) = 0;
Missing virtual destructors — causes memory leaks when deleting derived
objects through base pointers.
// Virtual function with default implementation
virtual std::string getVersion() const { Overusing RTTI (typeid, dynamic_cast) — often indicates design problems;
return "1.0"; prefer polymorphic interfaces.
}
}; Forgetting the override keyword — can lead to subtle bugs when base class
interfaces change.
// Concrete implementation of the interface
class ImageProcessor : public DataProcessor {
private:
std::string m_processorName; Discussion Points
double m_qualityFactor;
When should you use pure polymorphism versus RTTI with dynamic_cast?
public:
ImageProcessor(const std::string& name, double quality) Performance implications of virtual function calls in high-performance systems
: m_processorName(name), m_qualityFactor(quality) {}
Modern alternatives: type erasure, std::variant, std::visit vs. classic polymorphism
bool canProcess(const std::string& dataType) const override {
Interactive: Identify and fix polymorphism bugs in a plugin architecture
return dataType == "image" || dataType == "bitmap";
}
public:
void registerPlugin(std::unique_ptr<DataProcessor> plugin) {
m_plugins.push_back(std::move(plugin));
}
// Copy assignment
ResourceHandler& operator=(const ResourceHandler& other) { Find the Bug Challenge
if (this != &other) {
delete m_resource; class DataProcessor {
m_resource = new Resource(*other.m_resource); int* data;
} public:
DataProcessor(size_t size) {
return *this; data = new int[size];
} }
~DataProcessor() { delete data; }
// Move constructor DataProcessor(const DataProcessor& other) {
data = other.data;
ResourceHandler(ResourceHandler&& other) noexcept }
: m_resource(other.m_resource) { };
other.m_resource = nullptr;
} What memory-related issues does this code have? How would you fix them using modern C++
practices?
// Move assignment
ResourceHandler& operator=(ResourceHandler&& other) noexcept {
if (this != &other) {
delete m_resource;
m_resource = other.m_resource;
other.m_resource = nullptr;
}
return *this;
}
};
public:
// Constructor - RAII handled automatically
ModernResourceHandler()
: m_resource(std::make_unique<Resource>()) {}
Test your understanding of advanced C++ concepts with these real-world coding challenges. Try to solve them before looking at the solutions.
Refactor this poorly encapsulated class to use proper access control and data hiding: Fix the diamond inheritance problem in this class hierarchy:
Find and fix the memory leaks and polymorphism issues: Implement a proper stream insertion operator using friend function:
Facilitate critical thinking and advanced debate among senior C++ developers. Consider these topics for group discussion, peer review sessions, or personal reflection on coding
philosophy.
When are friend functions truly justified, and when do they signal poor design? What design decisions would you make when balancing extensibility and performance?
"Friend functions break encapsulation and create tight coupling between classes. Are there "Abstract interfaces and virtual functions enable extensibility but introduce runtime overhead.
legitimate use cases where this violation is acceptable?" How do you decide when this trade-off is worth it?"
"What alternative designs would you suggest to avoid using friend functions while maintaining "How would you design a system that needs to be both highly extensible and performance-
performance and readability?" critical? Which C++ features would you leverage?"
"How do you balance the practical need for efficient operator overloading with the principle of "When is compile-time polymorphism (templates, CRTP) preferable to runtime polymorphism
strict encapsulation?" (virtual functions)?"
Virtual Inheritance: Use or Avoid? Language Performance Debugging & Optimization Performance
Is virtual inheritance a necessary tool or a sign of overly complex design? Share experiences benchmarking and optimizing complex C++ code:
"Virtual inheritance solves the diamond problem but adds complexity and runtime overhead. "What hidden performance bottlenecks have you discovered in C++ code that appeared correct
Is the problem itself an indication of a flawed design?" but performed poorly?"
"What alternative design patterns could you use to avoid multiple inheritance scenarios that "How do you balance modern C++ features (smart pointers, std::function, lambdas) with
lead to the diamond problem?" performance requirements in critical code paths?"
"How would you refactor a legacy codebase that heavily relies on complex inheritance "What tools and techniques have you found most effective for profiling memory usage and
hierarchies with multiple inheritance?" identifying leaks in complex C++ applications?"
namespace cpp_advanced {
Summary & Key Takeaways
Mastering these advanced C++ concepts will enable you to build robust, efficient, and maintainable software systems:
Inheritance Polymorphism
Apply single, multiple, and virtual inheritance thoughtfully, and solve the diamond problem Design with virtual functions, abstract classes, and RTTI for flexible, extensible architectures
with virtual base classes. and runtime behavior.
Continue your C++ journey by exploring templates, move semantics, concurrency, and modern C++ features.