#include <iostream>
#include <memory>
#include <utility>
#include <vector>

// ==================== Helper class: for observing construction and destruction ====================
class TraceObject {
public:
    explicit TraceObject(const std::string& name) : name_(name) {
        std::cout << "[TraceObject] " << name_ << " constructed" << std::endl;
    }
    ~TraceObject() {
        std::cout << "[TraceObject] " << name_ << " destructed" << std::endl;
    }
    void greet() const {
        std::cout << "Hello from " << name_ << std::endl;
    }
private:
    std::string name_;
};

// ==================== Circular reference demonstration (incorrect version, using shared_ptr) ====================
class NodeB; // Forward declaration

class NodeA {
public:
    NodeA() { std::cout << "NodeA constructed" << std::endl; }
    ~NodeA() { std::cout << "NodeA destructed" << std::endl; }
    void setPartner(const std::shared_ptr<NodeB>& b) { partner_ = b; }
private:
    std::shared_ptr<NodeB> partner_; // Holding shared_ptr causes circular reference
};

class NodeB {
public:
    NodeB() { std::cout << "NodeB constructed" << std::endl; }
    ~NodeB() { std::cout << "NodeB destructed" << std::endl; }
    void setPartner(const std::shared_ptr<NodeA>& a) { partner_ = a; }
private:
    std::shared_ptr<NodeA> partner_; // Mutual reference causes memory leak
};

// ==================== Circular reference demonstration (correct version, using weak_ptr to break the cycle) ====================
class FixedNodeB; // Forward declaration

class FixedNodeA {
public:
    FixedNodeA() { std::cout << "FixedNodeA constructed" << std::endl; }
    ~FixedNodeA() { std::cout << "FixedNodeA destructed" << std::endl; }
    void setPartner(const std::shared_ptr<FixedNodeB>& b) { partner_ = b; }
    void usePartner() const; // Declaration, defined outside class
    void greet() const {    // Added greet method for FixedNodeB to call
        std::cout << "FixedNodeA::greet() called" << std::endl;
    }
private:
    std::shared_ptr<FixedNodeB> partner_;
};

class FixedNodeB {
public:
    FixedNodeB() { std::cout << "FixedNodeB constructed" << std::endl; }
    ~FixedNodeB() { std::cout << "FixedNodeB destructed" << std::endl; }
    void setPartner(const std::shared_ptr<FixedNodeA>& a) { partner_ = a; }
    void usePartner() const;
private:
    std::weak_ptr<FixedNodeA> partner_; // Use weak_ptr to break circular reference
};

void FixedNodeA::usePartner() const {
    if (partner_) {
        std::cout << "FixedNodeA accessing partner" << std::endl;
        partner_->usePartner(); // Demonstration of mutual calls
    } else {
        std::cout << "FixedNodeA has no partner" << std::endl;
    }
}

void FixedNodeB::usePartner() const {
    if (auto sp = partner_.lock()) { // lock() obtains shared_ptr
        std::cout << "FixedNodeB successfully locked weak_ptr, partner exists" << std::endl;
        sp->greet(); // FixedNodeA now has greet() method
    } else {
        std::cout << "FixedNodeB's weak_ptr has expired, partner already released" << std::endl;
    }
}

// ==================== Custom deleter demonstration ====================
struct CustomDeleter {
    void operator()(TraceObject* p) const {
        std::cout << "Custom deleter releases TraceObject" << std::endl;
        delete p;
    }
};

// ==================== Main Program ====================
int main() {
    std::cout << "========== C++ Smart Pointer Demonstration ==========" << std::endl;

    // 1. Basic unique_ptr usage
    std::cout << "\n--- 1. unique_ptr demonstration ---" << std::endl;
    {
        // Create unique_ptr
        auto ptr1 = std::make_unique<TraceObject>("unique_1");
        ptr1->greet();

        // Cannot copy, but can move ownership
        std::unique_ptr<TraceObject> ptr2 = std::move(ptr1);
        if (!ptr1) std::cout << "ptr1 is now null" << std::endl;
        ptr2->greet();

        // reset releases the original object
        ptr2.reset(new TraceObject("unique_2"));
        ptr2->greet();

        // release releases ownership (requires manual delete, not usually recommended)
        TraceObject* raw = ptr2.release();
        delete raw; // Note: manual release required

        // Custom deleter (using lambda)
        auto customDeleterLambda = [](TraceObject* p) {
            std::cout << "Lambda deleter releases TraceObject" << std::endl;
            delete p;
        };
        std::unique_ptr<TraceObject, decltype(customDeleterLambda)> ptr3(
            new TraceObject("custom_deleter"), customDeleterLambda);
        ptr3->greet();
        // Custom deleter automatically called when leaving scope
    }
    std::cout << "unique_ptr demonstration complete" << std::endl;

    // 2. Basic shared_ptr usage and reference counting
    std::cout << "\n--- 2. shared_ptr demonstration ---" << std::endl;
    {
        // Create using make_shared (more efficient)
        auto sp1 = std::make_shared<TraceObject>("shared_1");
        std::cout << "Reference count: " << sp1.use_count() << std::endl;

        {
            auto sp2 = sp1; // Copy, reference count increases
            std::cout << "After copy, reference count: " << sp1.use_count() << std::endl;
            sp2->greet();
        } // sp2 goes out of scope, reference count decreases

        std::cout << "After sp2 destroyed, reference count: " << sp1.use_count() << std::endl;

        // reset decreases reference and may release object
        sp1.reset();
        std::cout << "After reset, reference count: " << (sp1 ? sp1.use_count() : 0) << std::endl;
    }

    // 3. Basic weak_ptr usage (not involving circular references)
    std::cout << "\n--- 3. weak_ptr basic demonstration ---" << std::endl;
    {
        auto sp = std::make_shared<TraceObject>("weak_demo");
        std::weak_ptr<TraceObject> wp = sp;

        std::cout << "weak_ptr not expired, lock result: " << (wp.lock() ? "valid" : "invalid") << std::endl;
        sp.reset(); // Release object
        std::cout << "After shared_ptr released, weak_ptr expired: " << wp.expired() << std::endl;
        std::cout << "lock result: " << (wp.lock() ? "valid" : "invalid") << std::endl;
    }

    // 4. Circular reference problem demonstration (incorrect approach, memory leak)
    std::cout << "\n--- 4. Circular reference demonstration (using shared_ptr mutual references, causing memory leak) ---" << std::endl;
    {
        std::shared_ptr<NodeA> a = std::make_shared<NodeA>();
        std::shared_ptr<NodeB> b = std::make_shared<NodeB>();
        a->setPartner(b);
        b->setPartner(a);
        // When leaving scope, both a and b have reference count of 2 (self + shared_ptr held by the other)
        // Reference count will never reach 0, so NodeA and NodeB will never be destructed
        std::cout << "Note: The above NodeA and NodeB will not be destructed, causing memory leak" << std::endl;
    }
    std::cout << "Circular reference block ended, but NodeA and NodeB not destructed, no destructor output visible." << std::endl;

    // 5. Breaking circular reference using weak_ptr (correct approach)
    std::cout << "\n--- 5. Breaking circular reference using weak_ptr (correct approach) ---" << std::endl;
    {
        auto a = std::make_shared<FixedNodeA>();
        auto b = std::make_shared<FixedNodeB>();
        a->setPartner(b);
        b->setPartner(a);

        // Mutual call demonstration
        a->usePartner();
        // When leaving scope, reference count normally decreases to 0, objects properly destructed
        std::cout << "Scope ending, FixedNodeA and FixedNodeB should be properly destructed." << std::endl;
    }
    std::cout << "Circular reference fix block ended, destructors called correctly." << std::endl;

    // 6. Smart pointers with containers example
    std::cout << "\n--- 6. Storing smart pointers in containers ---" << std::endl;
    {
        std::vector<std::shared_ptr<TraceObject>> vec;
        vec.push_back(std::make_shared<TraceObject>("vec_elem_1"));
        vec.push_back(std::make_shared<TraceObject>("vec_elem_2"));
        vec.push_back(std::make_shared<TraceObject>("vec_elem_3"));

        std::cout << "Iterating container: " << std::endl;
        for (const auto& elem : vec) {
            elem->greet();
        }
        // Clear container, all objects released
        vec.clear();
        std::cout << "Container cleared, all elements released" << std::endl;
    }

    // 7. Converting unique_ptr to shared_ptr
    std::cout << "\n--- 7. Converting unique_ptr to shared_ptr ---" << std::endl;
    {
        auto up = std::make_unique<TraceObject>("to_be_shared");
        std::shared_ptr<TraceObject> sp = std::move(up);
        std::cout << "unique_ptr ownership transferred to shared_ptr, reference count: " << sp.use_count() << std::endl;
        sp->greet();
        // up is now null
    }

    std::cout << "\n========== Program ended, all resources properly released ==========" << std::endl;
    return 0;
}

results matching ""

    No results matching ""