ఎక్సెప్షన్ హ్యాండ్లింగ్, ఫైల్ I/O & గ్రాండ్ కాప్‌స్టోన్ ప్రాజెక్ట్ (Exceptions, Files & Capstone)

🌐 Modern C++ (C++17/C++20) 🎯 Chapter 20 of 20 🚀 Phase 05: అడ్వాన్స్‌డ్ C++, STL & ప్రాజెక్ట్ 📅 2026 Edition
📌 Covered in this chapter: try, catch, throw · Standard Exceptions (std::exception, std::runtime_error) · Stack Unwinding & RAII · Custom Exception Classes · File I/O (std::ifstream, std::ofstream) · Binary vs Text Modes · Serialization · గ్రాండ్ ఫినాలే: పూర్తి C++ రియల్-వరల్డ్ బ్యాంకింగ్ & ఆడిట్ లెడ్జర్ ప్రాజెక్ట్!
సాఫ్ట్‌వేర్ అభివృద్ధిలో కోడ్ రాయడం ఎంత ముఖ్యమో, ఊహించని ఎర్రర్స్ (Network failures, Invalid user input, File not found, Out of Memory) వచ్చినప్పుడు ప్రోగ్రామ్ క్రాష్ అవ్వకుండా సురక్షితంగా రికవర్ అవ్వడం కూడా అంతే ముఖ్యం. సాంప్రదాయ C లాంగ్వేజ్‌లో ఎర్రర్ కోడ్‌లు రిటర్న్ చేసే పద్ధతి కోడ్‌ను అస్తవ్యస్తం చేస్తుంది. కానీ Modern C++ లోని try-catch-throw ఆర్కిటెక్చర్, Stack Unwinding మరియు RAII మెకానిజమ్స్ మీ సిస్టమ్‌ను బుల్లెట్‌ప్రూఫ్‌గా మారుస్తాయి. దీనితో పాటు, మీ అప్లికేషన్ డేటాను డిస్క్‌లో శాశ్వతంగా భద్రపరచడానికి ఫైల్ స్ట్రీమ్స్ (Text & Binary File I/O) ఎలా ఉపయోగించాలో నేర్చుకుంటాం. చివరిగా, ఈ 20 అధ్యాయాల సమగ్ర C++ ప్రయాణానికి కిరీటంలా — ఆబ్జెక్ట్ ఓరియెంటెడ్ పాలిమార్ఫిజమ్, STL కంటైనర్లు, స్మార్ట్ పాయింటర్లు, కస్టమ్ ఎక్సెప్షన్స్ మరియు డిస్క్ పర్సిస్టెన్స్ కలగలిసిన పూర్తి స్థాయి ఎంటర్‌ప్రైజ్ బ్యాంకింగ్ & ట్రాన్సాక్షన్ ఆడిట్ లెడ్జర్ ప్రాజెక్ట్ ను మీ చేతులతో నిర్మిస్తారు!

1 ఎక్సెప్షన్ హ్యాండ్లింగ్ ఎందుకు? (Why Exceptions vs Traditional Error Codes)

ఒక సాఫ్ట్‌వేర్ రన్ అవుతున్నప్పుడు రెండు రకాల లోపాలు ఎదురవుతాయి:

సాంప్రదాయ C లాంగ్వేజ్‌లో ప్రతి ఫంక్షన్ ఒక ఇంటిజర్ ఎర్రర్ కోడ్ (ఉదాహరణకు 0 సక్సెస్, -1 ఎర్రర్) రిటర్న్ చేస్తుంది. దీనివల్ల డీప్ కాల్ స్టాక్ ఉన్నప్పుడు (Function A -> Function B -> Function C -> Function D), ప్రతి లెవెల్ వద్ద పదుల సంఖ్యలో if (err != 0) return err; చెక్స్ రాయాల్సి వస్తుంది. దీనినే "Spaghetti Error Handling" అంటారు.

[సాంప్రదాయ ఎర్రర్ కోడ్‌లు vs ఆధునిక C++ ఎక్సెప్షన్ ఆర్కిటెక్చర్] 1. పాత C-శైలి (Error Return Codes - బోరింగ్ & లోపభూయిష్టం): Caller() --> FuncA() --> FuncB() --> FuncC() [ఫెయిల్: returns -1] | | | | | (if check) | (if check) | (if check) | v v v v ప్రతి ఫంక్షన్ ఎర్రర్ కోడ్‌ను మ్యానువల్‌గా పైకి పాస్ చేయాలి! ఒక్క ఫంక్షన్ మర్చిపోయినా సిస్టమ్ సైలెంట్‌గా తప్పు సమాధానం ఇస్తుంది! 2. Modern C++ Exceptions (స్వచ్ఛమైన సెపరేషన్ & ఆటోమేటిక్ ప్రొపగేషన్): Caller() [try-catch] --> FuncA() --> FuncB() --> FuncC() [throw std::runtime_error] ▲ │ │══════════════════ [ఆటోమేటిక్ Stack Unwinding] ═══════════╝ • మధ్యలోని FuncA, FuncB లలో ఏ చెక్స్ అవసరం లేదు! • స్టాక్‌లోని లోకల్ ఆబ్జెక్టుల Destructors ఆటోమేటిక్‌గా రన్ అయ్యి రిసోర్సెస్ క్లీన్ అవుతాయి! • ఎర్రర్ నేరుగా Caller లోని catch బ్లాక్‌కు చేరుకుంటుంది!

2 try, catch, throw ఫండమెంటల్స్ & కంట్రోల్ ఫ్లో

C++ ఎక్సెప్షన్ హ్యాండ్లింగ్ మూడు కీలక కీవర్డ్‌లపై ఆధారపడి పనిచేస్తుంది:

basic_try_catch_flow.cpp
#include <iostream>
#include <stdexcept> // standard exception classes

// సురక్షితమైన డివిజన్ ఫంక్షన్
double safeDivide(double numerator, double denominator) {
    if (denominator == 0.0) {
        // జీరోతో భాగించడం అసాధ్యం, కాబట్టి runtime_error ను త్రో చేస్తున్నాం
        throw std::runtime_error("గణిత లోపం: సున్నాతో భాగించడం సాధ్యం కాదు (Divide by Zero)!");
    }
    return numerator / denominator;
}

int main() {
    std::cout << "--- Modern C++ ఎక్సెప్షన్ హ్యాండ్లింగ్ డిమో ---\n";

    double a = 100.0;
    double b = 0.0;

    try {
        std::cout << "డివిజన్ ఆపరేషన్ ప్రారంభమవుతోంది...\n";
        double result = safeDivide(a, b);
        // ఎక్సెప్షన్ వస్తే క్రింది లైన్ ఎగ్జిక్యూట్ అవ్వదు!
        std::cout << "ఫలితం: " << result << "\n";
    }
    catch (const std::runtime_error& ex) {
        // సురక్షితంగా ఎక్సెప్షన్‌ను పట్టుకున్నాం
        std::cerr << "🚨 [ERROR CAUGHT]: " << ex.what() << "\n";
        std::cout << "ℹ️ రికవరీ లాజిక్: డిఫాల్ట్ వేల్యూ 0.0 కేటాయించబడింది.\n";
    }

    std::cout << "✅ ప్రోగ్రామ్ సురక్షితంగా తన ప్రయాణాన్ని కొనసాగిస్తోంది!\n";
    return 0;
}

3 C++ స్టాండర్డ్ ఎక్సెప్షన్ హైరార్కీ (Standard Exception Hierarchy)

C++ స్టాండర్డ్ లైబ్రరీ (<stdexcept>) ఒక క్రమబద్ధమైన క్లాస్ హైరార్కీని అందిస్తుంది. ఈ క్లాసులన్నీ మూల పేరెంట్ అయిన std::exception నుండి ఇన్హెరిట్ అవుతాయి. ప్రతి ఎక్సెప్షన్ క్లాస్‌లో virtual const char* what() const noexcept; అనే మెథడ్ ఉంటుంది, ఇది ఎర్రర్ వివరణాత్మక మెసేజ్‌ను రిటర్న్ చేస్తుంది.

Exception Class రకం / కేటగిరీ ఎప్పుడు సంభవిస్తుంది? (Real-world Scenario)
std::exception Root Base Class C++ లోని అన్ని స్టాండర్డ్ ఎక్సెప్షన్లకు మూల క్లాస్. పాలిమార్ఫిక్ క్యాచ్ కోసం వాడుతారు.
std::logic_error ప్రోగ్రామర్ లాజిక్ లోపం కోడ్ రాయడంలో తప్పుల వల్ల సంభవించేవి (ఉదాహరణకు ప్రీ-కండిషన్స్ ఫెయిల్ అవ్వడం).
std::invalid_argument logic_error డెరివేటివ్ ఫంక్షన్‌కు చెల్లని ఆర్గ్యుమెంట్ పంపినప్పుడు (ఉదా: నెగటివ్ ఏజ్, తప్పు ఇమెయిల్ ఫార్మాట్).
std::out_of_range logic_error డెరివేటివ్ కంటైనర్ పరిధి దాటి యాక్సెస్ చేసినప్పుడు (ఉదా: std::vector::at(99) బౌండ్స్ ఎర్రర్).
std::runtime_error సిస్టమ్ / రన్‌టైమ్ లోపం రన్‌టైమ్‌లో సిస్టమ్ లేదా ఎక్స్‌టర్నల్ ఈవెంట్ల వల్ల జరిగే లోపాలు (ఫైల్ దొరకకపోవడం, హార్డ్‌వేర్ ఫెయిల్యూర్).
std::bad_alloc మెమరీ లోపం (<new>) హీప్ మెమరీ పూర్తిగా అయిపోయి new ఆపరేటర్ ఫెయిల్ అయినప్పుడు.

4 Stack Unwinding & RAII గ్యారెంటీ (Destructor Cleanup)

C++ ఎక్సెప్షన్ సిస్టమ్ యొక్క అత్యున్నత సూపర్-పవర్ Stack Unwinding. ఒక ఫంక్షన్ లోపల throw జరిగినప్పుడు, దానికి సరిపడే catch బ్లాక్ దొరికే వరకు కాల్ స్టాక్‌లో వెనుకకు వెళ్తుంది. ఈ వెనుకకు వెళ్ళే క్రమంలో స్టాక్ ఫ్రేమ్‌లలో క్రియేట్ చేయబడిన ప్రతి లోకల్ ఆబ్జెక్ట్ యొక్క Destructor ఆటోమేటిక్‌గా ఎగ్జిక్యూట్ అవుతుంది!

[కాల్ స్టాక్ లో Stack Unwinding జరిగే క్రమం] [CALL STACK] ┌─────────────────────────────────────────────────────────┐ │ Frame 3: processTransaction() │ │ • ResourceGuard lock (RAII Object) │ │ • DatabaseConnection db (RAII Object) │ │ ==> THROW Occurs! │ <-- 1. ఇక్కడ ఎక్సెప్షన్ త్రో అయ్యింది └───────────────────────────┬─────────────────────────────┘ │ (Stack Unwinding స్టార్ట్: db.~DatabaseConnection() & lock.~ResourceGuard() కాల్ అవుతాయి!) ▼ ┌─────────────────────────────────────────────────────────┐ │ Frame 2: handleUserRequest() │ │ • FileLogger logger (RAII Object) │ <-- 2. logger.~FileLogger() క్లీన్ అవుతుంది! └───────────────────────────┬─────────────────────────────┘ │ (వెనక్కి అన్-వైండ్ అవుతోంది...) ▼ ┌─────────────────────────────────────────────────────────┐ │ Frame 1: main() [try { handleUserRequest(); } catch] │ <-- 3. సరిపడే catch దొరికింది! │ • catch (const std::exception& e) లోకి ఎంటర్ అవుతుంది!│ └─────────────────────────────────────────────────────────┘
stack_unwinding_raii_safety.cpp
#include <iostream>
#include <string>
#include <stdexcept>

// RAII రిసోర్స్ క్లాస్ - డిస్ట్రక్టర్ ఎగ్జిక్యూషన్ ట్రాక్ చేయడానికి
class NetworkSocket {
    std::string endpoint;
public:
    NetworkSocket(const std::string& ep) : endpoint(ep) {
        std::cout << "  🔌 [OPEN] సాకెట్ కనెక్షన్ తెరవబడింది: " << endpoint << "\n";
    }
    ~NetworkSocket() {
        std::cout << "  🧹 [CLEANUP] Stack Unwinding ద్వారా సాకెట్ క్లోజ్ చేయబడింది: " << endpoint << "\n";
    }
};

void deepFunction() {
    NetworkSocket sock("192.168.1.100:8080");
    std::cout << "  ⚡ deepFunction లోపల కీలక డేటా ప్రాసెస్ అవుతోంది...\n";
    
    // ఏదో తప్పు జరిగి ఎక్సెప్షన్ త్రో అయింది
    throw std::runtime_error("నెట్‌వర్క్ ప్యాకెట్ డ్రాప్ అయ్యింది (Corrupted Payload)!");
    
    std::cout << "  ఈ లైన్ ఎప్పటికీ ప్రింట్ అవ్వదు!\n";
}

int main() {
    std::cout << "=== STACK UNWINDING & RAII సేఫ్టీ డెమో ===\n";
    try {
        deepFunction();
    }
    catch (const std::exception& e) {
        std::cout << "🚨 [CATCH BLOCK]: ప్రధాన హ్యాండ్లర్ లో ఎర్రర్ దొరికింది: " << e.what() << "\n";
    }
    std::cout << "✅ ఏ మెమరీ లీక్ లేకుండా సిస్టమ్ రికవర్ అయ్యింది!\n";
    return 0;
}

5 Catch by Reference & Object Slicing నివారణ

ఎక్సెప్షన్ హ్యాండ్లింగ్‌లో బిగినర్స్ చేసే అతిపెద్ద తప్పు: ఎక్సెప్షన్‌ను పాస్-బై-వేల్యూ (catch (std::exception e)) తో క్యాచ్ చేయడం. ఎల్లప్పుడూ కాన్స్ట్ రిఫరెన్స్ (catch (const std::exception& e)) ద్వారానే క్యాచ్ చేయాలి. ఎందుకు?

6 కస్టమ్ ఎక్సెప్షన్ క్లాసెస్ నిర్మాణం (Custom Exception Classes)

రియల్ వరల్డ్ ప్రాజెక్ట్‌లలో జనరిక్ ఎర్రర్ల బదులు మీ బిజినెస్ డొమైన్‌కు సరిపడే కస్టమ్ ఎక్సెప్షన్ క్లాసులు రాయడం ఎంతో ప్రొఫెషనల్ పద్ధతి. దీనికోసం std::runtime_error లేదా std::exception నుండి ఇన్హెరిట్ చేసుకుంటాం.

custom_banking_exceptions.cpp
#include <iostream>
#include <string>
#include <stdexcept>

// బ్యాంకింగ్ డొమైన్ కోసం రూట్ బేస్ ఎక్సెప్షన్
class BankingException : public std::runtime_error {
public:
    explicit BankingException(const std::string& msg) : std::runtime_error(msg) {}
};

// అకౌంట్‌లో తగినంత నిధులు లేనప్పుడు వచ్చే కస్టమ్ ఎక్సెప్షన్
class InsufficientFundsException : public BankingException {
    double currentBalance;
    double requestedAmount;
public:
    InsufficientFundsException(double balance, double requested)
        : BankingException("ఖాతాలో సరిపడా నిధులు లేవు! బ్యాలెన్స్: " + std::to_string(balance) + 
                           ", ఉపసంహరించదలచిన మొత్తం: " + std::to_string(requested)),
          currentBalance(balance), requestedAmount(requested) {}

    double getDeficit() const noexcept { return requestedAmount - currentBalance; }
};

class BankAccount {
    std::string accNumber;
    double balance;
public:
    BankAccount(const std::string& acc, double initial) : accNumber(acc), balance(initial) {}

    void withdraw(double amount) {
        if (amount > balance) {
            throw InsufficientFundsException(balance, amount);
        }
        balance -= amount;
        std::cout << "✅ ఉపసంహరణ విజయవంతం! విత్‌డ్రా: ₹ " << amount << ", ప్రస్తుత నిల్వ: ₹ " << balance << "\n";
    }
};

int main() {
    BankAccount myAcc("SB-987654321", 5000.0);

    try {
        std::cout << "లావాదేవీ 1: ₹ 2000 విత్‌డ్రా చేయడానికి ప్రయత్నిస్తున్నాం...\n";
        myAcc.withdraw(2000.0);

        std::cout << "\nలావాదేవీ 2: ₹ 7000 విత్‌డ్రా చేయడానికి ప్రయత్నిస్తున్నాం...\n";
        myAcc.withdraw(7000.0);
    }
    catch (const InsufficientFundsException& ex) {
        std::cerr << "❌ [BANKING ERROR]: " << ex.what() << "\n";
        std::cerr << "   లక్ష్యం నెరవేరడానికి ఇంకా లోటు మొత్తం: ₹ " << ex.getDeficit() << "\n";
    }
    catch (const BankingException& ex) {
        std::cerr << "❌ [GENERAL BANK ERROR]: " << ex.what() << "\n";
    }
    return 0;
}

7 noexcept స్పెసిఫైయర్ & మూవ్ సెమాంటిక్స్ పెర్ఫార్మెన్స్

Modern C++ (C++11 నుండి) లో ఒక ఫంక్షన్ ఎప్పటికీ ఎక్సెప్షన్‌ను త్రో చేయదు అని కంపైలర్‌కు హామీ ఇవ్వడానికి noexcept కీవర్డ్ వాడతాం.

8 ఫైల్ I/O పరిచయం: <fstream> స్ట్రీమ్ ఆర్కిటెక్చర్

రామ్ (RAM) లో ఉండే డేటా ప్రోగ్రామ్ ముగియగానే లేదా కరెంట్ పోగానే అంతరించిపోతుంది (Volatile). కానీ శాశ్వత స్టోరేజ్ (SSD/HDD) పై డేటాను సేవ్ చేయడానికి C++ లో <fstream> హెడర్ లైబ్రరీని ఉపయోగిస్తాం.

Stream Class దిశ (Direction) పనితీరు & ప్రయోజనం
std::ofstream Output File Stream (రాయడం) ఫైల్ లోకి డేటాను వ్రాయడానికి (Writing). ఫైల్ లేకపోతే కొత్త ఫైల్ సృష్టిస్తుంది.
std::ifstream Input File Stream (చదవడం) డిస్క్‌లోని ఫైల్ నుండి డేటాను చదవడానికి (Reading). ఫైల్ లేకపోతే ఓపెన్ అవ్వదు.
std::fstream Bi-directional Stream ఒకే ఫైల్ పై చదవడం (Read) మరియు రాయడం (Write) రెండూ చేయడానికి.

ముఖ్యమైన ఫైల్ ఓపెన్ మోడ్స్ (Open Modes):

9 టెక్స్ట్ ఫైల్స్ రాయడం & చదవడం (Reading & Writing Text Files)

టెక్స్ట్ ఫైల్స్‌తో పనిచేసేటప్పుడు << ఆపరేటర్‌తో రాయడం, మరియు std::getline() ఫంక్షన్‌తో లైన్ల వారీగా చదవడం అత్యంత సాధారణ పద్ధతి. ఫైల్ ఆబ్జెక్ట్ స్కోప్ దాటగానే RAII సూత్రం ప్రకారం ఆటోమేటిక్‌గా ఫైల్ క్లోజ్ అవుతుంది.

text_file_read_write.cpp
#include <iostream>
#include <fstream>
#include <string>

void writeAuditLog(const std::string& filename) {
    // ofstream ఆబ్జెక్ట్ సృష్టించి ఫైల్ ఓపెన్ చేస్తున్నాం
    std::ofstream outFile(filename, std::ios::out | std::ios::app);
    
    if (!outFile.is_open()) {
        std::cerr << "ఫైల్ తెరవడంలో లోపం ఏర్పడింది!\n";
        return;
    }

    outFile << "TIMESTAMP: 2026-11-01 10:30:00 | USER: Admin | EVENT: Login Successful\n";
    outFile << "TIMESTAMP: 2026-11-01 10:32:15 | USER: User_42 | EVENT: Fund Transfer ₹ 5000\n";
    outFile << "TIMESTAMP: 2026-11-01 10:35:40 | USER: Admin | EVENT: Password Updated\n";

    std::cout << "✅ ఆడిట్ లాగ్ డేటా విజయవంతంగా ఫైల్ లోకి వ్రాయబడింది!\n";
    // outFile ఇక్కడ స్కోప్ దాటగానే ఆటోమేటిక్‌గా క్లోజ్ అవుతుంది (RAII)
}

void readAuditLog(const std::string& filename) {
    std::ifstream inFile(filename);

    if (!inFile.is_open()) {
        std::cerr << "ఫైల్ చదవడానికి అందుబాటులో లేదు: " << filename << "\n";
        return;
    }

    std::cout << "\n--- [" << filename << "] నుండి లైన్ల వారీగా డేటా రీడింగ్ ---\n";
    std::string line;
    int lineNum = 1;
    while (std::getline(inFile, line)) {
        std::cout << "లైన్ " << lineNum++ << ": " << line << "\n";
    }
}

int main() {
    std::string path = "system_audit.log";
    writeAuditLog(path);
    readAuditLog(path);
    return 0;
}

10 బైనరీ ఫైల్ I/O & డేటా స్ట్రక్చర్ సీరియలైజేషన్ (Binary Serialization)

టెక్స్ట్ ఫైల్స్ మనుషులు చదవడానికి బాగుంటాయి కానీ ఎక్కువ స్పేస్ తీసుకుంటాయి మరియు నంబర్లను స్ట్రింగ్స్‌గా మార్చడానికి CPU సైకిల్స్ వృధా అవుతాయి. గేమ్ సేవ్ ఫైల్స్, డేటాబేస్ ఇండెక్స్ ఫైల్స్, హై-స్పీడ్ బ్యాంకింగ్ సిస్టమ్స్‌లో డేటాను యథాతథంగా మెమరీ బైట్స్ రూపంలో సేవ్ చేయడానికి Binary File I/O వాడతాం.

binary_serialization_struct.cpp
#include <iostream>
#include <fstream>
#include <cstring>

// ఫిక్స్‌డ్ సైజ్ ఉన్న POD (Plain Old Data) స్ట్రక్చర్
struct AccountRecord {
    int accountId;
    char customerName[32];
    double balance;
    bool isActive;
};

void saveBinaryRecord(const std::string& filename, const AccountRecord& record) {
    std::ofstream out(filename, std::ios::binary);
    if (!out) {
        std::cerr << "బైనరీ ఫైల్ తెరవడం విఫలమైంది!\n";
        return;
    }
    // మెమరీలోని బైట్లను నేరుగా డిస్క్‌పై వ్రాయడం
    out.write(reinterpret_cast<const char*>(&record), sizeof(AccountRecord));
    std::cout << "💾 బైనరీ రికార్డ్ సేవ్ చేయబడింది (" << sizeof(AccountRecord) << " బైట్స్).\n";
}

void loadBinaryRecord(const std::string& filename) {
    std::ifstream in(filename, std::ios::binary);
    if (!in) {
        std::cerr << "బైనరీ ఫైల్ చదవడం విఫలమైంది!\n";
        return;
    }
    AccountRecord loadedRecord{};
    in.read(reinterpret_cast<char*>(&loadedRecord), sizeof(AccountRecord));

    std::cout << "\n🔍 [డిస్క్ నుండి లోడ్ చేయబడిన రికార్డ్]:\n";
    std::cout << "  • అకౌంట్ ID: " << loadedRecord.accountId << "\n";
    std::cout << "  • ఖాతాదారుని పేరు: " << loadedRecord.customerName << "\n";
    std::cout << "  • బ్యాలెన్స్: ₹ " << loadedRecord.balance << "\n";
    std::cout << "  • యాక్టివ్ స్టేటస్: " << (loadedRecord.isActive ? "ACTIVE" : "INACTIVE") << "\n";
}

int main() {
    AccountRecord user1{101, "రాజేష్ శర్మ", 85400.50, true};
    std::string binFile = "account_101.dat";

    saveBinaryRecord(binFile, user1);
    loadBinaryRecord(binFile);
    return 0;
}

11 ఫైల్ పాయింటర్లు & ర్యాండమ్ యాక్సెస్ (File Pointers & Random Access)

ఫైల్‌ను క్రమపద్ధతిలో మొదటి నుండి చివరి వరకు చదవడమే కాకుండా, కావలసిన నిర్దిష్ట లొకేషన్‌కు నేరుగా దూకడానికి ఫైల్ పాయింటర్లు (File Pointers) ఉపయోగపడతాయి:

12 కామన్ మిస్టేక్స్ & ప్రొడక్షన్ బెస్ట్ ప్రాక్టీసెస్ (Gotchas & Best Practices)

ఎక్సెప్షన్స్ మరియు ఫైల్ హ్యాండ్లింగ్‌లో ప్రొడక్షన్ సిస్టమ్స్ బ్రేక్ అవ్వకుండా పాటించాల్సిన గోల్డెన్ రూల్స్:

13 గ్రాండ్ ఫినాలే రియల్-వరల్డ్ కాప్‌స్టోన్ ప్రాజెక్ట్: Enterprise Bank & Transaction Audit Ledger System

ఈ 20-అధ్యాయాల C++ మాస్టర్‌క్లాస్ యొక్క అత్యున్నత శిఖరం ఈ కాప్‌స్టోన్ ప్రాజెక్ట్. ఇది ఒక సాధారణ డెమో కాదు — మొత్తం C++ కోర్సులో మీరు నేర్చుకున్న అన్ని అధునాతన కాన్సెప్టులను కలగలిపి రూపొందించిన ఒక కంప్లీట్ ప్రొడక్షన్-గ్రేడ్ బ్యాంకింగ్ & ఆడిట్ లెడ్జర్ ఇంజిన్!

🏗️ ఈ కాప్‌స్టోన్ ప్రాజెక్ట్‌లో కలగలిసిన 10 కీలక C++ టెక్నాలజీలు:
  1. ఆబ్జెక్ట్ ఓరియెంటెడ్ పాలిమార్ఫిజమ్: Abstract Base Class Account, వర్చువల్ మెథడ్స్, SavingsAccount & CheckingAccount.
  2. కస్టమ్ డొమైన్ ఎక్సెప్షన్స్: BankingException, InsufficientFundsException, InvalidTransactionException.
  3. రెస్పాన్సిబుల్ RAII & స్మార్ట్ పాయింటర్లు: మెమరీ లీక్స్ లేకుండా std::unique_ptr మరియు std::shared_ptr మేనేజ్‌మెంట్.
  4. STL Containers & Hash Maps: ఖాతాలను వేగంగా శోధించడానికి std::unordered_map, లెడ్జర్ కోసం std::vector.
  5. ఫైల్ పర్సిస్టెన్స్ & ఆడిట్ లాగింగ్: ప్రతి లావాదేవీని టైమ్‌స్టాంప్‌తో డిస్క్‌లోని bank_audit_ledger.txt లోకి రాయడం.
  6. Modern C++20 ఫీచర్స్: noexcept మూవ్ ఆపరేషన్లు, constexpr లిమిట్స్, స్ట్రక్చర్డ్ బైండింగ్స్.
enterprise_bank_capstone.cpp
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <ctime>
#include <stdexcept>

// ==========================================
// 1. కస్టమ్ ఎక్సెప్షన్ హైరార్కీ (Domain Exceptions)
// ==========================================
class BankingException : public std::runtime_error {
public:
    explicit BankingException(const std::string& msg) : std::runtime_error(msg) {}
};

class InsufficientFundsException : public BankingException {
public:
    InsufficientFundsException(const std::string& acc, double balance, double req)
        : BankingException("ఖాతా [" + acc + "] లో తగినంత నిధులు లేవు! అందుబాటులో ఉన్నది: ₹ " + 
                           std::to_string(balance) + ", కోరినది: ₹ " + std::to_string(req)) {}
};

class InvalidAccountException : public BankingException {
public:
    explicit InvalidAccountException(const std::string& acc)
        : BankingException("ఖాతా సంఖ్య చెల్లదు లేదా బ్యాంక్ రికార్డులలో లేదు: " + acc) {}
};

// ==========================================
// 2. లావాదేవీ రికార్డ్ & డిస్క్ ఆడిటర్ (Audit Logger)
// ==========================================
struct Transaction {
    std::string timestamp;
    std::string accountId;
    std::string type;
    double amount;
    double postBalance;
};

class AuditLogger {
    std::string logFilePath;
public:
    AuditLogger(const std::string& path) : logFilePath(path) {}

    void log(const Transaction& tx) {
        std::ofstream file(logFilePath, std::ios::app);
        if (file.is_open()) {
            file << tx.timestamp << " | ACC: " << std::setw(12) << tx.accountId 
                 << " | TYPE: " << std::setw(10) << tx.type 
                 << " | AMOUNT: ₹ " << std::fixed << std::setprecision(2) << std::setw(10) << tx.amount 
                 << " | BAL: ₹ " << std::setw(10) << tx.postBalance << "\n";
        }
    }
};

// ==========================================
// 3. అబ్‌స్ట్రాక్ట్ బేస్ క్లాస్: Account (OOP)
// ==========================================
class Account {
protected:
    std::string accountId;
    std::string holderName;
    double balance;
public:
    Account(std::string id, std::string name, double initialBal)
        : accountId(std::move(id)), holderName(std::move(name)), balance(initialBal) {}
    
    virtual ~Account() = default;

    virtual void deposit(double amount) {
        if (amount <= 0) throw BankingException("డిపాజిట్ మొత్తం ₹ 0 కంటే ఎక్కువగా ఉండాలి!");
        balance += amount;
    }

    virtual void withdraw(double amount) = 0; // Pure Virtual Function

    const std::string& getId() const noexcept { return accountId; }
    const std::string& getName() const noexcept { return holderName; }
    double getBalance() const noexcept { return balance; }
    virtual std::string getAccountType() const = 0;
};

// ==========================================
// 4. డెరైవ్డ్ క్లాస్ 1: SavingsAccount (వడ్డీ సౌకర్యం)
// ==========================================
class SavingsAccount : public Account {
    double interestRate;
public:
    SavingsAccount(std::string id, std::string name, double bal, double rate = 4.5)
        : Account(std::move(id), std::move(name), bal), interestRate(rate) {}

    void withdraw(double amount) override {
        if (amount <= 0) throw BankingException("విత్‌డ్రా మొత్తం ధనాత్మకంగా ఉండాలి!");
        if (amount > balance) {
            throw InsufficientFundsException(accountId, balance, amount);
        }
        balance -= amount;
    }

    std::string getAccountType() const override { return "SAVINGS (సేవింగ్స్)"; }
};

// ==========================================
// 5. డెరైవ్డ్ క్లాస్ 2: CheckingAccount (ఓవర్‌డ్రాఫ్ట్ సౌకర్యం)
// ==========================================
class CheckingAccount : public Account {
    double overdraftLimit;
public:
    CheckingAccount(std::string id, std::string name, double bal, double limit = 15000.0)
        : Account(std::move(id), std::move(name), bal), overdraftLimit(limit) {}

    void withdraw(double amount) override {
        if (amount <= 0) throw BankingException("విత్‌డ్రా మొత్తం ధనాత్మకంగా ఉండాలి!");
        if (amount > (balance + overdraftLimit)) {
            throw InsufficientFundsException(accountId, balance + overdraftLimit, amount);
        }
        balance -= amount;
    }

    std::string getAccountType() const override { return "CURRENT/CHECKING (ఓవర్‌డ్రాఫ్ట్)"; }
};

// ==========================================
// 6. సెంట్రల్ బ్యాంక్ సిస్టమ్ మేనేజర్ (STL + Exceptions + File I/O)
// ==========================================
class EnterpriseBankSystem {
    std::unordered_map<std::string, std::shared_ptr<Account>> accounts;
    AuditLogger auditor;

    std::string getCurrentTimestamp() {
        auto now = std::chrono::system_clock::now();
        std::time_t t = std::chrono::system_clock::to_time_t(now);
        char buf[32];
        std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
        return std::string(buf);
    }

public:
    EnterpriseBankSystem(const std::string& logFile) : auditor(logFile) {}

    void openAccount(std::shared_ptr<Account> acc) {
        std::string id = acc->getId();
        accounts[id] = acc;
        auditor.log({getCurrentTimestamp(), id, "OPEN_ACC", acc->getBalance(), acc->getBalance()});
        std::cout << "  🎉 ఖాతా తెరవబడింది: [" << id << "] " << acc->getName() 
                  << " | టైప్: " << acc->getAccountType() << " | ప్రారంభ బ్యాలెన్స్: ₹ " << acc->getBalance() << "\n";
    }

    void processDeposit(const std::string& id, double amount) {
        auto it = accounts.find(id);
        if (it == accounts.end()) throw InvalidAccountException(id);

        it->second->deposit(amount);
        auditor.log({getCurrentTimestamp(), id, "DEPOSIT", amount, it->second->getBalance()});
        std::cout << "  💰 డిపాజిట్ విజయవంతం: ACC [" << id << "] +₹ " << amount 
                  << " => కొత్త బ్యాలెన్స్: ₹ " << it->second->getBalance() << "\n";
    }

    void processWithdrawal(const std::string& id, double amount) {
        auto it = accounts.find(id);
        if (it == accounts.end()) throw InvalidAccountException(id);

        it->second->withdraw(amount);
        auditor.log({getCurrentTimestamp(), id, "WITHDRAW", amount, it->second->getBalance()});
        std::cout << "  🏧 విత్‌డ్రా విజయవంతం: ACC [" << id << "] -₹ " << amount 
                  << " => కొత్త బ్యాలెన్స్: ₹ " << it->second->getBalance() << "\n";
    }

    void transferFunds(const std::string& fromId, const std::string& toId, double amount) {
        std::cout << "\n  🔄 అకౌంట్ ట్రాన్స్‌ఫర్ మొదలైంది: [" << fromId << "] నుండి [" << toId << "] కి ₹ " << amount << "...\n";
        auto fromIt = accounts.find(fromId);
        auto toIt = accounts.find(toId);

        if (fromIt == accounts.end()) throw InvalidAccountException(fromId);
        if (toIt == accounts.end()) throw InvalidAccountException(toId);

        // Transactional Safety: డెబిట్ సక్సెస్ అయితేనే క్రెడిట్ చేస్తాం!
        fromIt->second->withdraw(amount);
        try {
            toIt->second->deposit(amount);
        } catch (...) {
            // రోల్‌బ్యాక్ మెకానిజమ్
            fromIt->second->deposit(amount);
            throw BankingException("ట్రాన్స్‌ఫర్ ఫెయిల్ అయ్యింది! నిధులు రోల్‌బ్యాక్ చేయబడ్డాయి.");
        }

        auditor.log({getCurrentTimestamp(), fromId, "XFER_OUT", amount, fromIt->second->getBalance()});
        auditor.log({getCurrentTimestamp(), toId, "XFER_IN", amount, toIt->second->getBalance()});
        std::cout << "  ✅ నిధుల బదిలీ పూర్తయింది! ఆడిట్ లాగ్ రికార్డ్ అయ్యింది.\n";
    }

    void displayAllAccounts() const {
        std::cout << "\n=========================================================================================\n";
        std::cout << "                      🏦 ఎంటర్‌ప్రైజ్ బ్యాంక్ - ఖాతాదారుల రియల్-టైమ్ సమ్మరీ\n";
        std::cout << "=========================================================================================\n";
        for (const auto& [id, acc] : accounts) {
            std::cout << "  ID: " << std::setw(12) << acc->getId() 
                      << " | పేరు: " << std::setw(18) << acc->getName() 
                      << " | టైప్: " << std::setw(22) << acc->getAccountType() 
                      << " | బ్యాలెన్స్: ₹ " << std::setw(10) << acc->getBalance() << "\n";
        }
        std::cout << "=========================================================================================\n";
    }
};

int main() {
    std::cout << "=========================================================================================\n";
    std::cout << "      🚀 MODERN C++ గ్రాండ్ కాప్‌స్టోన్ ప్రాజెక్ట్: ENTERPRISE BANK AUDIT SYSTEM\n";
    std::cout << "=========================================================================================\n\n";

    EnterpriseBankSystem bank("enterprise_bank_audit.txt");

    // 1. ఖాతాలు తెరవడం
    bank.openAccount(std::make_shared<SavingsAccount>("SB-1001", "రాహుల్ వర్మ", 45000.0));
    bank.openAccount(std::make_shared<CheckingAccount>("CA-2001", "టెక్-సొల్యూషన్స్ లిమిటెడ్", 120000.0, 50000.0));
    bank.openAccount(std::make_shared<SavingsAccount>("SB-1002", "అనన్య రెడ్డి", 15000.0));

    bank.displayAllAccounts();

    // 2. సాధారణ విజయవంతమైన లావాదేవీలు
    std::cout << "\n[లావాదేవీల ఎగ్జిక్యూషన్]:\n";
    try {
        bank.processDeposit("SB-1001", 10000.0);
        bank.processWithdrawal("CA-2001", 25000.0);
        bank.transferFunds("SB-1001", "SB-1002", 15000.0);
    }
    catch (const BankingException& e) {
        std::cerr << "🚨 [ERROR]: " << e.what() << "\n";
    }

    // 3. ఎక్సెప్షన్ హ్యాండ్లింగ్ టెస్టింగ్ (నిధుల కొరత)
    std::cout << "\n[ఎక్సెప్షన్ టెస్ట్ 1: ఖాతాలో ఉన్నదానికంటే ఎక్కువ విత్‌డ్రా చేసినప్పుడు]:\n";
    try {
        bank.processWithdrawal("SB-1002", 90000.0);
    }
    catch (const InsufficientFundsException& e) {
        std::cerr << "  🚨 ఆపరేషన్ తిరస్కరించబడింది: " << e.what() << "\n";
    }

    // 4. ఎక్సెప్షన్ టెస్ట్ 2: లేని ఖాతాకు బదిలీ ప్రయత్నించినప్పుడు
    std::cout << "\n[ఎక్సెప్షన్ టెస్ట్ 2: లేని అకౌంట్ నంబర్ ఇచ్చినప్పుడు]:\n";
    try {
        bank.transferFunds("SB-1001", "SB-9999_UNKNOWN", 500.0);
    }
    catch (const InvalidAccountException& e) {
        std::cerr << "  🚨 ఆపరేషన్ తిరస్కరించబడింది: " << e.what() << "\n";
    }

    bank.displayAllAccounts();

    std::cout << "\n🎯 కాప్‌స్టోన్ ప్రాజెక్ట్ సక్సెస్: అన్ని లావాదేవీలు 'enterprise_bank_audit.txt' లో ఆడిట్ అయ్యాయి!\n";
    std::cout << "🏆 అభినందనలు! మీరు Modern C++ Masterclass కోర్సును విజయవంతంగా పూర్తి చేసారు! 🚀\n";
    return 0;
}

★ ఈ అధ్యాయంపై ప్రాక్టీస్ క్విజ్ (Knowledge Check)

1. C++ లో Stack Unwinding సమయంలో లోకల్ ఆబ్జెక్టుల మెమరీ మరియు రిసోర్సెస్‌కి ఏమి జరుగుతుంది?
సమాధానం: Stack Unwinding సమయంలో, త్రో అయిన స్థానం నుండి క్యాచ్ బ్లాక్ వరకు మధ్యలోని ప్రతి ఫంక్షన్ స్టాక్ ఫ్రేమ్‌లోని లోకల్ ఆబ్జెక్టుల Destructors ఆటోమేటిక్‌గా రన్ అవుతాయి. RAII (Resource Acquisition Is Initialization) సూత్రం ప్రకారం తెరవబడిన ఫైల్స్, మెమరీ లేదా నెట్‌వర్క్ సాకెట్లు ఎటువంటి లీక్స్ లేకుండా క్లీన్ చేయబడతాయి.
2. ఎక్సెప్షన్‌ను catch (const std::exception& e) కాకుండా catch (std::exception e) ద్వారా పట్టుకుంటే ఏమి ప్రమాదం?
సమాధానం: పాస్-బై-వేల్యూ ద్వారా క్యాచ్ చేస్తే ఆబ్జెక్ట్ స్లైసింగ్ (Object Slicing) జరుగుతుంది. మీరు త్రో చేసిన డెరైవ్డ్ క్లాస్ ఆబ్జెక్ట్ బేస్ క్లాస్ లోకి కుదించబడి, దానిలోని వర్చువల్ what() మరియు కస్టమ్ డెరివేటివ్ డేటా పోతుంది. కాన్స్ట్ రిఫరెన్స్ (&) వాడితే పాలిమార్ఫిజమ్ భద్రంగా ఉంటుంది మరియు కాపీ ఓవర్‌హెడ్ ఉండదు.
3. Destructor లోపల throw చేయడం వల్ల Modern C++ లో ఎందుకు తీవ్రమైన ప్రమాదం జరుగుతుంది?
సమాధానం: C++11 నుండి అన్ని డిస్ట్రక్టర్లు బై-డిఫాల్ట్ noexcept(true) గా పరిగణించబడతాయి. పైగా ఇప్పటికే ఒక ఎక్సెప్షన్ వల్ల Stack Unwinding జరుగుతుండగా, డిస్ట్రక్టర్ లోపల నుండి మరొక ఎక్సెప్షన్ త్రో అయితే రెండు అన్‌హ్యాండిల్డ్ ఎక్సెప్షన్స్ ఒకేసారి యాక్టివ్ అవుతాయి. అప్పుడు C++ రన్‌టైమ్ వెంటనే std::terminate() ను పిలిచి ప్రోగ్రామ్‌ను ఆపేస్తుంది. అందుకే డిస్ట్రక్టర్ల నుండి ఎక్సెప్షన్స్ ఎప్పుడూ బయటకు రాకూడదు.
4. std::vector కోసం Move Constructor కి noexcept స్పెసిఫైయర్ ఎందుకు చాలా కీలకం?
సమాధానం: std::vector కెపాసిటీ నిండి కొత్త మెమరీ లోకి ఎలిమెంట్స్‌ను తరలించేటప్పుడు బలమైన ఎక్సెప్షన్ సేఫ్టీ (Strong Exception Guarantee) ని కాపాడాలి. ఒకవేళ మూవ్ కన్‌స్ట్రక్టర్ noexcept కాకపోతే, మధ్యలో ఎర్రర్ వస్తే పాత ఎలిమెంట్స్ చెదిరిపోతాయనే భయంతో వెక్టర్ మూవ్ చేయకుండా ప్రతి ఎలిమెంట్‌ను నెమ్మదిగా కాపీ (Copy Constructor) చేస్తుంది. noexcept ఇవ్వడం వల్లనే నిజమైన మూవ్ సెమాంటిక్స్ వేగవంతమైన స్పీడ్ అందుతుంది.
5. బైనరీ ఫైల్ I/O లో డైనమిక్ మెమరీ ఉన్న క్లాస్‌లను (ఉదా: std::string, std::vector) reinterpret_cast తో నేరుగా ఎందుకు రాయకూడదు?
సమాధానం: std::string లేదా డైనమిక్ ఆబ్జెక్టుల లోపల హీప్ మెమరీని పాయింట్ చేసే పాయింటర్లు ఉంటాయి. reinterpret_cast కేవలం ఆబ్జెక్ట్ యొక్క స్టాక్ బైట్స్‌ను మాత్రమే రాస్తుంది (అంటే వర్చువల్ మెమరీ అడ్రస్‌లను). తర్వాతి సెషన్‌లో లేదా వేరే కంప్యూటర్‌లో ఆ ఫైల్‌ను చదివినప్పుడు ఆ పాయింటర్ అడ్రస్ చెల్లదు (Dangling pointer / Segfault). అందువల్ల కేవలం POD (Plain Old Data / Fixed Char Arrays) లేదా ప్రాపర్ సీరియలైజేషన్ ఫార్మాట్‌లను మాత్రమే బైనరీలో రాయాలి.
← 19. STL కంటైనర్లు & అల్గారిథమ్స్ 🏆 C++ Course HOME (ఇండెక్స్) →

ఈ కాప్‌స్టోన్ ప్రాజెక్ట్‌ను లైవ్‌గా మన కంపైలర్‌లో రన్ చేయండి!

ఎటువంటి లోకల్ సెటప్ లేకుండా, బ్రౌజర్‌లోనే నేరుగా Modern C++ (GCC 13 / C++20) కంపైలర్‌లో ఈ పూర్తి స్థాయి ఎంటర్‌ప్రైజ్ బ్యాంకింగ్ సిస్టమ్ కోడ్‌ను ఎగ్జిక్యూట్ చేసి రన్‌టైమ్ అవుట్‌పుట్‌ను తనిఖీ చేయండి.