C++中创建通用类型链表的实践

在C++中创建通用类型链表

在本文中,我们将探讨如何在C++中创建一个自定义链表结构,用于储存任意类型的数据文章中包含了两个完整的用例,自定义一个包含当前坐标和前一个坐标的点的结构体,并用我们的通用链表储存起来。

在C++中,一个通用的链表结构体通常涉及到模板的使用,这样可以允许链表存储任何类型的数据。以下是使用C++模板实现的一个通用链表类的示例,包括了链表节点结构体和链表类本身。链表节点使用指针来存储数据,而链表类提供了在链表前后插入和删除节点的方法,以及其他有用的功能。

#include <iostream>
#include <memory>

// 链表节点的定义
template <typename T>
struct ListNode {
    std::unique_ptr<T> data; // 使用unique_ptr来自动管理内存
    ListNode<T>* next;

    // 构造函数,用于初始化节点
    ListNode(T* data) : data(data), next(nullptr) {}

    // 禁止拷贝构造和赋值
    ListNode(const ListNode&) = delete;
    ListNode& operator=(const ListNode&) = delete;

    // 支持移动构造和赋值
    ListNode(ListNode&&) = default;
    ListNode& operator=(ListNode&&) = default;
};

// 链表类的定义
template <typename T>
class LinkedList {
private:
    ListNode<T>* head;
    ListNode<T>* tail;

public:
    // 构造函数
    LinkedList() : head(nullptr), tail(nullptr) {}

    // 禁止拷贝构造和赋值
    LinkedList(const LinkedList&) = delete;
    LinkedList& operator=(const LinkedList&) = delete;

    // 支持移动构造和赋值
    LinkedList(LinkedList&&) = default;
    LinkedList& operator=(LinkedList&&) = default;

    // 析构函数
    ~LinkedList() {
        clear();
    }

    // 在链表前端插入节点
    void push_front(T* data) {
        ListNode<T>* node = new ListNode<T>(data);
        if (!head) {
            head = tail = node;
        } else {
            node->next = head;
            head = node;
        }
    }

    // 在链表末端插入节点
    void push_back(T* data) {
        ListNode<T>* node = new ListNode<T>(data);
        if (!head) {
            head = tail = node;
        } else {
            tail->next = node;
            tail = node;
        }
    }

    // 从链表前端删除节点
    std::unique_ptr<T> pop_front() {
        if (!head) {
            return nullptr;
        }
        ListNode<T>* old_head = head;
        head = head->next;
        if (head == nullptr) {
            tail = nullptr;
        }
        return std::move(old_head->data);
    }

    // 从链表末端删除节点
    std::unique_ptr<T> pop_back() {
        if (!head) {
            return nullptr;
        }
        if (head == tail) {
            return pop_front();
        }
        ListNode<T>* current = head;
        while (current->next != tail) {
            current = current->next;
        }
        std::unique_ptr<T> data = std::move(tail->data);
        delete tail;
        tail = current;
        tail->next = nullptr;
        return data;
    }

    // 检查链表是否为空
    bool empty() const {
        return head == nullptr;
    }

    // 清除链表中的所有节点
    void clear() {
        while (head != nullptr) {
            pop_front();
        }
    }

    // 打印链表中的元素(需要T类型支持输出操作)
    void print() const {
        ListNode<T>* current = head;
        while (current != nullptr) {
            std::cout << *(current->data) << " -> ";
            current = current->next;
        }
        std::cout << "null" << std::endl;
    }
};

// 示例使用
int main() {
    LinkedList<int> list;

    // 在链表末端添加元素
    list.push_back(new int(1));
    list.push_back(new int(2));
    list.push_back(new int(3));

    // 打印链表
    list.print();

    // 从链表前端删除元素
    auto data = list.pop_front();
    if (data) {
        std::cout << "Popped from front: " << *data << std::endl;
    }

    // 再次打印链表
    list.print();

    // 清除链表
    list.clear();

    return 0;
}

这个链表类使用了unique_ptr来自动管理内存,确保不会发生内存泄漏。请注意,这个实现假设存储在链表中的类型T支持输出操作(即重载了operator<<),这样才能在print方法中打印每个元素。如果类型T不支持输出操作,你需要移除或者修改print方法。

在实际使用中,你可能需要根据实际情况调整或者扩展这个链表类的功能,以满足特定的需求。例如,你可能会添加遍历、查找或排序等方法。

自定义数据结构

在任何数据结构的设计中,首先要确定的是它将如何存储和处理数据。在我们的例子中,我们需要存储一系列的点。为了创建一个包含当前坐标 (x, y) 和前一个坐标 (prev_x, prev_y) 的自定义结构体,并将这些结构体组织成链表。

在这个示例中,Point结构体包含了两个整数对,分别表示当前点的xy坐标,以及先前点的prev_xprev_y坐标。此外,Point结构体还重载了输出操作符<<,以便于在LinkedListprint方法中直接打印点的信息。

当你在链表中添加Point实例时,你需要手动管理这些实例的内存,因为我们使用了裸指针。在本例中,我们通过new操作符创建了Point的实例,并通过push_back方法添加到链表中。当我们通过pop_front方法从链表中移除一个Point实例时,unique_ptr会自动释放这个实例占用的内存。在实际应用中,你可能需要考虑使用智能指针来进一步简化内存管理。

#include <iostream>
#include <memory>
// ... (包含先前提供的ListNode和LinkedList的定义)

// 定义一个表示点的结构体
struct Point {
    int x, y; // 当前点的坐标
    int prev_x, prev_y; // 先前点的坐标

    Point(int x, int y, int prev_x, int prev_y)
        : x(x), y(y), prev_x(prev_x), prev_y(prev_y) {}

    // 输出操作符重载,用于打印点的信息
    friend std::ostream& operator<<(std::ostream& os, const Point& point) {
        os << "Current: (" << point.x << ", " << point.y << "), "
           << "Previous: (" << point.prev_x << ", " << point.prev_y << ")";
        return os;
    }
};

// 示例使用
int main() {
    // 创建一个链表,存储Point结构体
    LinkedList<Point> pointList;

    // 添加点到链表
    pointList.push_back(new Point(1, 2, 0, 0));
    pointList.push_back(new Point(3, 4, 1, 2));
    pointList.push_back(new Point(5, 6, 3, 4));

    // 打印链表中的点
    pointList.print();

    // 从链表前端删除点
    auto data = pointList.pop_front();
    if (data) {
        std::cout << "Popped from front: " << *data << std::endl;
    }

    // 再次打印链表
    pointList.print();

    // 清除链表
    pointList.clear();

    return 0;
}

完整代码

#include <iostream>
#include <memory>

// 链表节点的定义
template <typename T>
struct ListNode {
    std::unique_ptr<T> data; // 使用unique_ptr来自动管理内存
    ListNode<T>* next;

    // 构造函数,用于初始化节点
    ListNode(T* data) : data(data), next(nullptr) {}

    // 禁止拷贝构造和赋值
    ListNode(const ListNode&) = delete;
    ListNode& operator=(const ListNode&) = delete;

    // 支持移动构造和赋值
    ListNode(ListNode&&) = default;
    ListNode& operator=(ListNode&&) = default;
};

// 链表类的定义
template <typename T>
class LinkedList {
private:
    ListNode<T>* head;
    ListNode<T>* tail;

public:
    // 构造函数
    LinkedList() : head(nullptr), tail(nullptr) {}

    // 禁止拷贝构造和赋值
    LinkedList(const LinkedList&) = delete;
    LinkedList& operator=(const LinkedList&) = delete;

    // 支持移动构造和赋值
    LinkedList(LinkedList&&) = default;
    LinkedList& operator=(LinkedList&&) = default;

    // 析构函数
    ~LinkedList() {
        clear();
    }

    // 在链表前端插入节点
    void push_front(T* data) {
        ListNode<T>* node = new ListNode<T>(data);
        if (!head) {
            head = tail = node;
        } else {
            node->next = head;
            head = node;
        }
    }

    // 在链表末端插入节点
    void push_back(T* data) {
        ListNode<T>* node = new ListNode<T>(data);
        if (!head) {
            head = tail = node;
        } else {
            tail->next = node;
            tail = node;
        }
    }

    // 从链表前端删除节点
    std::unique_ptr<T> pop_front() {
        if (!head) {
            return nullptr;
        }
        ListNode<T>* old_head = head;
        head = head->next;
        if (head == nullptr) {
            tail = nullptr;
        }
        return std::move(old_head->data);
    }

    // 从链表末端删除节点
    std::unique_ptr<T> pop_back() {
        if (!head) {
            return nullptr;
        }
        if (head == tail) {
            return pop_front();
        }
        ListNode<T>* current = head;
        while (current->next != tail) {
            current = current->next;
        }
        std::unique_ptr<T> data = std::move(tail->data);
        delete tail;
        tail = current;
        tail->next = nullptr;
        return data;
    }

    // 检查链表是否为空
    bool empty() const {
        return head == nullptr;
    }

    // 清除链表中的所有节点
    void clear() {
        while (head != nullptr) {
            pop_front();
        }
    }

    // 打印链表中的元素(需要T类型支持输出操作)
    void print() const {
        ListNode<T>* current = head;
        while (current != nullptr) {
            std::cout << *(current->data) << " -> ";
            current = current->next;
        }
        std::cout << "null" << std::endl;
    }
};

// 定义一个表示点的结构体
struct Point {
    int x, y; // 当前点的坐标
    int prev_x, prev_y; // 先前点的坐标

    Point(int x, int y, int prev_x, int prev_y)
        : x(x), y(y), prev_x(prev_x), prev_y(prev_y) {}

    // 输出操作符重载,用于打印点的信息
    friend std::ostream& operator<<(std::ostream& os, const Point& point) {
        os << "Current: (" << point.x << ", " << point.y << "), "
           << "Previous: (" << point.prev_x << ", " << point.prev_y << ")";
        return os;
    }
};


// 示例使用
int main() {
    LinkedList<int> list;

    // 在链表末端添加元素
    list.push_back(new int(1));
    list.push_back(new int(2));
    list.push_back(new int(3));

    // 打印链表
    list.print();

    // 从链表前端删除元素
    auto data = list.pop_front();
    if (data) {
        std::cout << "Popped from front: " << *data << std::endl;
    }

    // 再次打印链表
    list.print();

    // 清除链表
    list.clear();


    // 创建一个链表,存储Point结构体
    LinkedList<Point> pointList1;

    // 添加点到链表
    pointList1.push_back(new Point(1, 2, 0, 0));
    pointList1.push_back(new Point(3, 4, 1, 2));
    pointList1.push_back(new Point(5, 6, 3, 4));

    // 打印链表中的点
    pointList1.print();

    // 从链表前端删除点
    auto data1 = pointList1.pop_front();
    if (data1) {
        std::cout << "Popped from front: " << *data1 << std::endl;
    }

    // 再次打印链表
    pointList1.print();

    // 清除链表
    pointList1.clear();
    return 0;
}

总结

通过本文的指导,你现在可以在C++中创建一个功能齐全的通用链表。这种数据结构不仅增加了代码的灵活性和可重用性,而且通过智能指针的使用,还提高了程序的安全性和内存管理效率。记住,无论是创建链表还是其他复杂数据结构,核心在于理解和应用面向对象的原则,以及熟悉C++的模板和内存管理机制。随着实践的增加,你将能更加熟练地运用这些技术来解决各种编程挑战。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值