🔒 Încapsularea în C++ - Ghid Complet cu Exemple

Încapsularea este unul dintre cei patru piloni fundamentali ai programării orientate pe obiecte (POO), alături de abstractizare, moștenire și polimorfism. Reprezintă mecanismul prin care datele (variabilele) și metodele care operează asupra acestor date sunt grupate împreună într-o singură unitate numită clasă, restricționând accesul direct la unele componente ale obiectului.

Principiul fundamental: "Ascunde implementarea, expune interfața."

🎯 De ce este importantă încapsularea?

🛡️ Protecție

Protejează datele interne ale obiectului de modificări neautorizate

🔧 Mentenanță

Facilitează modificarea implementării fără a afecta codul client

✅ Validare

Permite validarea datelor înainte de modificare

🎨 Abstractizare

Ascunde complexitatea implementării

🔄 Flexibilitate

Permite schimbarea implementării interne fără a afecta utilizatorii

🐛 Debugging

Facilitează identificarea și rezolvarea erorilor

📊 Niveluri de Acces în C++

Specificator În Clasă În Clasă Derivată În Afara Clasei Utilizare Tipică
private ✅ Da ❌ Nu ❌ Nu Date interne, metode auxiliare
protected ✅ Da ✅ Da ❌ Nu Date pentru moștenire
public ✅ Da ✅ Da ✅ Da Interfața publică

📁 Separarea în Fișiere .h și .cpp

1. Exemplu Complet - Clasa Student

📄 Student.h
// Student.h - Fișier header (declarații)
#ifndef STUDENT_H
#define STUDENT_H

#include <string>
#include <vector>
#include <memory>

class Student {
private:
    // Date membre private - ascunse de utilizator
    std::string nume;
    std::string prenume;
    int varsta;
    std::string matricol;
    std::vector<double> note;
    static int numarTotalStudenti;
    
    // Metode private pentru uz intern
    bool valideazaMatricol(const std::string& mat) const;
    void actualizeazaMedie();
    
    // Date pentru implementare internă
    struct Impl;
    std::unique_ptr<Impl> pImpl;

protected:
    // Date accesibile claselor derivate
    double medie;
    bool esteActiv;

public:
    // Constructori
    Student();
    Student(const std::string& nume, const std::string& prenume, int varsta);
    Student(const Student& other);  // Copy constructor
    Student(Student&& other) noexcept;  // Move constructor
    
    // Destructor
    ~Student();
    
    // Operatori
    Student& operator=(const Student& other);
    Student& operator=(Student&& other) noexcept;
    
    // Getters (metode const)
    std::string getNume() const;
    std::string getPrenume() const;
    std::string getNumeComplet() const;
    int getVarsta() const;
    double getMedie() const;
    std::string getMatricol() const;
    
    // Setters cu validare
    void setNume(const std::string& nume);
    void setPrenume(const std::string& prenume);
    bool setVarsta(int varsta);
    bool setMatricol(const std::string& matricol);
    
    // Metode pentru gestionare note
    bool adaugaNota(double nota);
    bool stergeUltimaNota();
    std::vector<double> getNote() const;
    
    // Metode utilitare
    void afiseaza() const;
    bool estePromovat() const;
    std::string getStatus() const;
    
    // Metode statice
    static int getNumarTotalStudenti();
    static void resetCounter();
    
    // Friend functions
    friend std::ostream& operator<<(std::ostream& os, const Student& s);
    friend bool operator<(const Student& s1, const Student& s2);
};

#endif // STUDENT_H
📄 Student.cpp
// Student.cpp - Fișier sursă (implementări)
#include "Student.h"
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <numeric>
#include <regex>

// Implementare structură internă (ascunsă în .cpp)
struct Student::Impl {
    std::string adresa;
    std::string telefon;
    std::string email;
    std::chrono::system_clock::time_point dataInregistrare;
    
    Impl() : dataInregistrare(std::chrono::system_clock::now()) {}
};

// Inițializare membru static
int Student::numarTotalStudenti = 0;

// Constructor implicit
Student::Student() 
    : nume("Necunoscut"), prenume("Necunoscut"), varsta(18), 
      medie(0.0), esteActiv(true), pImpl(std::make_unique<Impl>()) {
    numarTotalStudenti++;
    matricol = "STD" + std::to_string(numarTotalStudenti);
    std::cout << "Constructor implicit apelat pentru " << matricol << std::endl;
}

// Constructor parametrizat
Student::Student(const std::string& n, const std::string& p, int v)
    : nume(n), prenume(p), varsta(v), medie(0.0), esteActiv(true),
      pImpl(std::make_unique<Impl>()) {
    if (varsta < 16 || varsta > 100) {
        throw std::invalid_argument("Vârsta trebuie să fie între 16 și 100 ani");
    }
    numarTotalStudenti++;
    matricol = "STD" + std::to_string(numarTotalStudenti);
    std::cout << "Constructor parametrizat pentru " << getNumeComplet() << std::endl;
}

// Copy constructor
Student::Student(const Student& other)
    : nume(other.nume), prenume(other.prenume), varsta(other.varsta),
      matricol(other.matricol + "_COPY"), note(other.note),
      medie(other.medie), esteActiv(other.esteActiv),
      pImpl(std::make_unique<Impl>(*other.pImpl)) {
    numarTotalStudenti++;
    std::cout << "Copy constructor pentru " << getNumeComplet() << std::endl;
}

// Move constructor
Student::Student(Student&& other) noexcept
    : nume(std::move(other.nume)), prenume(std::move(other.prenume)),
      varsta(other.varsta), matricol(std::move(other.matricol)),
      note(std::move(other.note)), medie(other.medie),
      esteActiv(other.esteActiv), pImpl(std::move(other.pImpl)) {
    std::cout << "Move constructor pentru " << getNumeComplet() << std::endl;
    other.varsta = 0;
    other.medie = 0.0;
    other.esteActiv = false;
}

// Destructor
Student::~Student() {
    numarTotalStudenti--;
    std::cout << "Destructor pentru " << getNumeComplet() 
              << " (Rămân " << numarTotalStudenti << " studenți)" << std::endl;
}

// Copy assignment operator
Student& Student::operator=(const Student& other) {
    if (this != &other) {
        nume = other.nume;
        prenume = other.prenume;
        varsta = other.varsta;
        matricol = other.matricol + "_ASSIGN";
        note = other.note;
        medie = other.medie;
        esteActiv = other.esteActiv;
        pImpl = std::make_unique<Impl>(*other.pImpl);
    }
    return *this;
}

// Move assignment operator
Student& Student::operator=(Student&& other) noexcept {
    if (this != &other) {
        nume = std::move(other.nume);
        prenume = std::move(other.prenume);
        varsta = other.varsta;
        matricol = std::move(other.matricol);
        note = std::move(other.note);
        medie = other.medie;
        esteActiv = other.esteActiv;
        pImpl = std::move(other.pImpl);
        
        other.varsta = 0;
        other.medie = 0.0;
        other.esteActiv = false;
    }
    return *this;
}

// Implementare metode private
bool Student::valideazaMatricol(const std::string& mat) const {
    std::regex pattern("^STD[0-9]+(_COPY|_ASSIGN)?$");
    return std::regex_match(mat, pattern);
}

void Student::actualizeazaMedie() {
    if (note.empty()) {
        medie = 0.0;
    } else {
        double suma = std::accumulate(note.begin(), note.end(), 0.0);
        medie = suma / note.size();
    }
}

// Implementare getters
std::string Student::getNume() const { return nume; }
std::string Student::getPrenume() const { return prenume; }
std::string Student::getNumeComplet() const { 
    return nume + " " + prenume; 
}
int Student::getVarsta() const { return varsta; }
double Student::getMedie() const { return medie; }
std::string Student::getMatricol() const { return matricol; }

// Implementare setters cu validare
void Student::setNume(const std::string& n) {
    if (n.empty()) {
        throw std::invalid_argument("Numele nu poate fi gol");
    }
    nume = n;
}

void Student::setPrenume(const std::string& p) {
    if (p.empty()) {
        throw std::invalid_argument("Prenumele nu poate fi gol");
    }
    prenume = p;
}

bool Student::setVarsta(int v) {
    if (v < 16 || v > 100) {
        return false;
    }
    varsta = v;
    return true;
}

bool Student::setMatricol(const std::string& m) {
    if (!valideazaMatricol(m)) {
        return false;
    }
    matricol = m;
    return true;
}

// Implementare metode pentru note
bool Student::adaugaNota(double nota) {
    if (nota < 1.0 || nota > 10.0) {
        return false;
    }
    note.push_back(nota);
    actualizeazaMedie();
    return true;
}

bool Student::stergeUltimaNota() {
    if (note.empty()) {
        return false;
    }
    note.pop_back();
    actualizeazaMedie();
    return true;
}

std::vector<double> Student::getNote() const {
    return note;  // Returnează copie, nu referință
}

// Metode utilitare
void Student::afiseaza() const {
    std::cout << "\n=== Date Student ===" << std::endl;
    std::cout << "Matricol: " << matricol << std::endl;
    std::cout << "Nume: " << getNumeComplet() << std::endl;
    std::cout << "Vârsta: " << varsta << " ani" << std::endl;
    std::cout << "Media: " << std::fixed << std::setprecision(2) << medie << std::endl;
    std::cout << "Status: " << getStatus() << std::endl;
}

bool Student::estePromovat() const {
    return medie >= 5.0 && esteActiv;
}

std::string Student::getStatus() const {
    if (!esteActiv) return "Inactiv";
    if (note.empty()) return "Fără note";
    if (medie >= 9.0) return "Excelent";
    if (medie >= 7.0) return "Foarte bine";
    if (medie >= 5.0) return "Promovat";
    return "Restanțier";
}

// Metode statice
int Student::getNumarTotalStudenti() {
    return numarTotalStudenti;
}

void Student::resetCounter() {
    numarTotalStudenti = 0;
}

// Friend functions
std::ostream& operator<<(std::ostream& os, const Student& s) {
    os << "[" << s.matricol << "] " << s.getNumeComplet() 
       << " - Media: " << std::fixed << std::setprecision(2) << s.medie;
    return os;
}

bool operator<(const Student& s1, const Student& s2) {
    return s1.medie < s2.medie;
}
📄 main.cpp
// main.cpp - Program principal
#include "Student.h"
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    try {
        // Test constructor implicit
        Student s1;
        s1.setNume("Popescu");
        s1.setPrenume("Ion");
        s1.adaugaNota(8.5);
        s1.adaugaNota(9.0);
        s1.adaugaNota(7.5);
        
        // Test constructor parametrizat
        Student s2("Ionescu", "Maria", 20);
        s2.adaugaNota(10.0);
        s2.adaugaNota(9.5);
        
        // Test copy constructor
        Student s3 = s2;
        
        // Test move constructor
        Student s4 = std::move(Student("Georgescu", "Ana", 19));
        
        // Afișare date
        std::cout << "\n=== Lista Studenți ===" << std::endl;
        std::cout << s1 << std::endl;
        std::cout << s2 << std::endl;
        std::cout << s3 << std::endl;
        std::cout << s4 << std::endl;
        
        // Vector de studenți
        std::vector<Student> catalog;
        catalog.push_back(s1);
        catalog.push_back(s2);
        catalog.push_back(s3);
        
        // Sortare după medie
        std::sort(catalog.begin(), catalog.end());
        
        std::cout << "\n=== Catalog Sortat după Medie ===" << std::endl;
        for (const auto& student : catalog) {
            std::cout << student << std::endl;
        }
        
        std::cout << "\nTotal studenți creați: " 
                  << Student::getNumarTotalStudenti() << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "Eroare: " << e.what() << std::endl;
    }
    
    std::cout << "\n=== Destructori apelați automat ===" << std::endl;
    return 0;
}

💥 Destructori și Gestiunea Resurselor

2. Exemplu Complex cu Destructori - Gestiune Fișiere

📄 Logger.h
#ifndef LOGGER_H
#define LOGGER_H

#include <string>
#include <fstream>
#include <memory>
#include <mutex>
#include <chrono>

class Logger {
private:
    std::ofstream logFile;
    std::string filename;
    std::mutex logMutex;
    size_t linesWritten;
    std::chrono::steady_clock::time_point startTime;
    bool isOpen;
    
    // Singleton pattern - instanță unică
    static std::unique_ptr<Logger> instance;
    static std::mutex instanceMutex;
    
    // Constructor privat pentru Singleton
    explicit Logger(const std::string& file);
    
    // Metodă privată pentru formatare timestamp
    std::string getCurrentTimestamp() const;

public:
    // Destructor - FOARTE IMPORTANT pentru cleanup
    ~Logger();
    
    // Ștergem constructorii de copiere (Singleton)
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
    
    // Metode statice pentru acces
    static Logger& getInstance(const std::string& filename = "app.log");
    static void destroyInstance();
    
    // Metode publice pentru logging
    void log(const std::string& message);
    void logError(const std::string& error);
    void logWarning(const std::string& warning);
    void flush();
    
    // Getters
    size_t getLinesWritten() const;
    std::string getFilename() const;
    double getUptime() const;
};

#endif
📄 Logger.cpp
#include "Logger.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <ctime>

// Inițializare membri statici
std::unique_ptr<Logger> Logger::instance = nullptr;
std::mutex Logger::instanceMutex;

// Constructor privat
Logger::Logger(const std::string& file) 
    : filename(file), linesWritten(0), isOpen(false) {
    
    logFile.open(filename, std::ios::app);
    if (logFile.is_open()) {
        isOpen = true;
        startTime = std::chrono::steady_clock::now();
        
        // Scrie header la deschidere
        logFile << "\n===== Logger Started =====" << std::endl;
        logFile << "Time: " << getCurrentTimestamp() << std::endl;
        logFile << "=========================" << std::endl;
        
        std::cout << "[Logger] Fișier deschis: " << filename << std::endl;
    } else {
        std::cerr << "[Logger] Eroare la deschiderea fișierului: " << filename << std::endl;
    }
}

// DESTRUCTOR - Foarte important pentru cleanup
Logger::~Logger() {
    std::cout << "[Logger] Destructor apelat" << std::endl;
    
    if (isOpen && logFile.is_open()) {
        // Calculează timpul total de funcționare
        auto endTime = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::seconds>(endTime - startTime);
        
        // Scrie footer înainte de închidere
        logFile << "\n===== Logger Stopped =====" << std::endl;
        logFile << "Total lines: " << linesWritten << std::endl;
        logFile << "Uptime: " << duration.count() << " seconds" << std::endl;
        logFile << "Time: " << getCurrentTimestamp() << std::endl;
        logFile << "=========================" << std::endl;
        
        // Flush și închide fișierul
        logFile.flush();
        logFile.close();
        
        std::cout << "[Logger] Fișier închis. Total " << linesWritten 
                  << " linii scrise în " << duration.count() << " secunde." << std::endl;
    }
    
    isOpen = false;
}

// Singleton getInstance
Logger& Logger::getInstance(const std::string& filename) {
    std::lock_guard<std::mutex> lock(instanceMutex);
    if (!instance) {
        instance = std::unique_ptr<Logger>(new Logger(filename));
    }
    return *instance;
}

// Distruge instanța Singleton
void Logger::destroyInstance() {
    std::lock_guard<std::mutex> lock(instanceMutex);
    instance.reset();  // Apelează destructorul
}

// Metodă privată pentru timestamp
std::string Logger::getCurrentTimestamp() const {
    auto now = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    
    std::stringstream ss;
    ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
    return ss.str();
}

// Metode de logging
void Logger::log(const std::string& message) {
    std::lock_guard<std::mutex> lock(logMutex);
    if (isOpen && logFile.is_open()) {
        logFile << "[INFO][" << getCurrentTimestamp() << "] " 
                << message << std::endl;
        linesWritten++;
    }
}

void Logger::logError(const std::string& error) {
    std::lock_guard<std::mutex> lock(logMutex);
    if (isOpen && logFile.is_open()) {
        logFile << "[ERROR][" << getCurrentTimestamp() << "] " 
                << error << std::endl;
        linesWritten++;
        logFile.flush();  // Flush imediat pentru erori
    }
}

void Logger::logWarning(const std::string& warning) {
    std::lock_guard<std::mutex> lock(logMutex);
    if (isOpen && logFile.is_open()) {
        logFile << "[WARN][" << getCurrentTimestamp() << "] " 
                << warning << std::endl;
        linesWritten++;
    }
}

void Logger::flush() {
    std::lock_guard<std::mutex> lock(logMutex);
    if (isOpen && logFile.is_open()) {
        logFile.flush();
    }
}

// Getters
size_t Logger::getLinesWritten() const { 
    return linesWritten; 
}

std::string Logger::getFilename() const { 
    return filename; 
}

double Logger::getUptime() const {
    auto now = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - startTime);
    return duration.count();
}

3. Exemplu RAII cu Destructori - Resource Manager

📄 ResourceManager.h
#ifndef RESOURCE_MANAGER_H
#define RESOURCE_MANAGER_H

#include <memory>
#include <vector>
#include <string>

// Clasă pentru gestiunea memoriei dinamice
template<typename T>
class DynamicArray {
private:
    T* data;
    size_t size;
    size_t capacity;
    
    // Statistici de utilizare
    mutable size_t accessCount;
    static size_t totalAllocations;
    static size_t totalDeallocations;
    
    // Metodă privată pentru realocare
    void resize(size_t newCapacity);

public:
    // Constructor implicit
    DynamicArray(size_t initialCapacity = 10);
    
    // Constructor de copiere
    DynamicArray(const DynamicArray& other);
    
    // Constructor de mutare
    DynamicArray(DynamicArray&& other) noexcept;
    
    // DESTRUCTOR - eliberează memoria
    ~DynamicArray();
    
    // Operatori de atribuire
    DynamicArray& operator=(const DynamicArray& other);
    DynamicArray& operator=(DynamicArray&& other) noexcept;
    
    // Metode publice
    void push_back(const T& value);
    void pop_back();
    T& operator[](size_t index);
    const T& operator[](size_t index) const;
    
    // Getters
    size_t getSize() const { return size; }
    size_t getCapacity() const { return capacity; }
    bool isEmpty() const { return size == 0; }
    
    // Statistici
    static void printStatistics();
};

// Clasă pentru gestiunea conexiunilor de rețea
class NetworkConnection {
private:
    int socketFd;
    std::string address;
    int port;
    bool isConnected;
    static int activeConnections;

public:
    NetworkConnection(const std::string& addr, int p);
    ~NetworkConnection();  // Închide conexiunea automat
    
    // Dezactivăm copierea (resursa unică)
    NetworkConnection(const NetworkConnection&) = delete;
    NetworkConnection& operator=(const NetworkConnection&) = delete;
    
    // Permitem mutarea
    NetworkConnection(NetworkConnection&& other) noexcept;
    NetworkConnection& operator=(NetworkConnection&& other) noexcept;
    
    bool send(const std::string& data);
    std::string receive(size_t maxBytes);
    void disconnect();
    
    static int getActiveConnections() { return activeConnections; }
};

#endif
📄 ResourceManager.cpp
#include "ResourceManager.h"
#include <iostream>
#include <cstring>
#include <stdexcept>

// Implementare DynamicArray
template<typename T>
size_t DynamicArray<T>::totalAllocations = 0;

template<typename T>
size_t DynamicArray<T>::totalDeallocations = 0;

template<typename T>
DynamicArray<T>::DynamicArray(size_t initialCapacity) 
    : size(0), capacity(initialCapacity), accessCount(0) {
    data = new T[capacity];
    totalAllocations++;
    std::cout << "[DynamicArray] Alocat: " << capacity 
              << " elemente (" << capacity * sizeof(T) << " bytes)" << std::endl;
}

template<typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& other) 
    : size(other.size), capacity(other.capacity), accessCount(0) {
    data = new T[capacity];
    for (size_t i = 0; i < size; i++) {
        data[i] = other.data[i];
    }
    totalAllocations++;
    std::cout << "[DynamicArray] Copy constructor: " << size << " elemente" << std::endl;
}

template<typename T>
DynamicArray<T>::DynamicArray(DynamicArray&& other) noexcept
    : data(other.data), size(other.size), capacity(other.capacity), 
      accessCount(other.accessCount) {
    other.data = nullptr;
    other.size = 0;
    other.capacity = 0;
    std::cout << "[DynamicArray] Move constructor" << std::endl;
}

// DESTRUCTOR - curățare automată
template<typename T>
DynamicArray<T>::~DynamicArray() {
    if (data != nullptr) {
        std::cout << "[DynamicArray] Destructor: eliberez " << capacity 
                  << " elemente (" << capacity * sizeof(T) << " bytes)" << std::endl;
        std::cout << "               Total accesări: " << accessCount << std::endl;
        delete[] data;
        totalDeallocations++;
    }
}

template<typename T>
void DynamicArray<T>::resize(size_t newCapacity) {
    T* newData = new T[newCapacity];
    size_t elementsToCopy = (size < newCapacity) ? size : newCapacity;
    
    for (size_t i = 0; i < elementsToCopy; i++) {
        newData[i] = std::move(data[i]);
    }
    
    delete[] data;
    data = newData;
    capacity = newCapacity;
    
    std::cout << "[DynamicArray] Redimensionat la: " << capacity << std::endl;
}

template<typename T>
void DynamicArray<T>::push_back(const T& value) {
    if (size >= capacity) {
        resize(capacity * 2);
    }
    data[size++] = value;
}

template<typename T>
T& DynamicArray<T>::operator[](size_t index) {
    if (index >= size) {
        throw std::out_of_range("Index în afara limitelor");
    }
    accessCount++;
    return data[index];
}

template<typename T>
void DynamicArray<T>::printStatistics() {
    std::cout << "\n=== Statistici DynamicArray ===" << std::endl;
    std::cout << "Total alocări: " << totalAllocations << std::endl;
    std::cout << "Total dealocări: " << totalDeallocations << std::endl;
    std::cout << "Scurgeri de memorie: " 
              << (totalAllocations - totalDeallocations) << std::endl;
}

// Implementare NetworkConnection
int NetworkConnection::activeConnections = 0;

NetworkConnection::NetworkConnection(const std::string& addr, int p) 
    : address(addr), port(p), isConnected(false), socketFd(-1) {
    // Simulare creare socket
    static int nextSocket = 100;
    socketFd = nextSocket++;
    isConnected = true;
    activeConnections++;
    
    std::cout << "[Network] Conexiune creată: " << address 
              << ":" << port << " (Socket: " << socketFd << ")" << std::endl;
}

// DESTRUCTOR - închide conexiunea automat
NetworkConnection::~NetworkConnection() {
    if (isConnected) {
        std::cout << "[Network] Destructor: închid conexiunea " 
                  << address << ":" << port << std::endl;
        disconnect();
    }
}

NetworkConnection::NetworkConnection(NetworkConnection&& other) noexcept
    : socketFd(other.socketFd), address(std::move(other.address)),
      port(other.port), isConnected(other.isConnected) {
    other.socketFd = -1;
    other.isConnected = false;
    std::cout << "[Network] Move constructor" << std::endl;
}

void NetworkConnection::disconnect() {
    if (isConnected) {
        std::cout << "[Network] Închid socket: " << socketFd << std::endl;
        // Simulare închidere socket
        socketFd = -1;
        isConnected = false;
        activeConnections--;
    }
}

bool NetworkConnection::send(const std::string& data) {
    if (!isConnected) return false;
    std::cout << "[Network] Trimit: " << data.size() << " bytes" << std::endl;
    return true;
}

// Instanțieri template explicite
template class DynamicArray<int>;
template class DynamicArray<double>;
template class DynamicArray<std::string>;

4. Exemplu Program Principal cu Toate Componentele

📄 main_complete.cpp
#include "Student.h"
#include "Logger.h"
#include "ResourceManager.h"
#include <iostream>
#include <memory>
#include <thread>

// Funcție de test cu scope limitat
void testResourceManagement() {
    std::cout << "\n=== Test Resource Management ===" << std::endl;
    
    {
        // Scope pentru DynamicArray
        std::cout << "\n-- Test DynamicArray --" << std::endl;
        DynamicArray<int> arr(5);
        
        for (int i = 0; i < 10; i++) {
            arr.push_back(i * i);
        }
        
        std::cout << "Array size: " << arr.getSize() << std::endl;
        std::cout << "Array capacity: " << arr.getCapacity() << std::endl;
        
        // Destructor automat la ieșirea din scope
    }
    
    {
        // Scope pentru NetworkConnection
        std::cout << "\n-- Test NetworkConnection --" << std::endl;
        NetworkConnection conn1("192.168.1.1", 8080);
        conn1.send("Hello Server!");
        
        {
            NetworkConnection conn2("10.0.0.1", 443);
            std::cout << "Conexiuni active: " 
                      << NetworkConnection::getActiveConnections() << std::endl;
            // conn2 distrugă aici
        }
        
        std::cout << "Conexiuni active după scope intern: " 
                  << NetworkConnection::getActiveConnections() << std::endl;
        // conn1 distrugă aici
    }
    
    DynamicArray<int>::printStatistics();
}

// Funcție pentru test cu excepții
void testExceptionSafety() {
    std::cout << "\n=== Test Exception Safety ===" << std::endl;
    
    try {
        DynamicArray<double> numbers(3);
        numbers.push_back(3.14);
        numbers.push_back(2.71);
        
        // Forțăm o excepție
        std::cout << "Încerc acces invalid..." << std::endl;
        double