手写简单版本STL

Vector

<stdexcept>定义了一些标准的异常类。分为两大类:逻辑错误和运行时错误。其中运行时错误是程序员不能控制的。

#include <sstream>这是一个C++标准库中的头文件,提供了stringstream类,用于将字符串转换成数字或者将数字转换成字符串

#include <algorithm> 是C++中一个常用的预处理指令,它包含了algorithm库。这个库提供了大量用于操作序列(例如数组、向量、列表等容器)的通用算法,这些算法包括查找、排序、复制、移动、修改和其他操作

必须使用delete[]来释放动态数组的内存

关于迭代器

begin指向数组第一个元素的前面

elements[0]第一个元素

begin

const 版本的迭代器函数(如你提供的 const T* begin() const)是为了确保在只读上下文中,调用者不能修改通过迭代器访问的数据

3.other是被拷贝的对象

std::copy需要三个参数:两个指定要复制的元素范围的迭代器(起始迭代器和结束迭代器),以及一个指向目标位置开始的迭代器。 指针也是一种天然的迭代器
4.释放调用者对象的内存
5.size当前元素个数 capacity 设置的可容纳元素个数
 如果容量0表示还没设置,从1开始;否则翻倍
7.throw std::out_of_range("Index out of range"); 抛出 std::out_of_range 异常
  如果不提供第二种形式,那么将无法访问const对象的任何数组元素
  const修饰函数调用const对象 非const不能调用const
8.将insert及之后的元素后移一位
9.不实际释放内存,这意味着即使调用了 pop_back,capacity 保持不变。
10.std中vector没提供
12.函数后const 只读

    getchar();
    std::string line;
    // 接收命令
    for (int i = 0; i < N; i++) {
        std::getline(std::cin, line);
        std::istringstream iss(line);
        std::string command;
        iss >> command;
        int value;

        if (command == "push_back") {
            iss >> value;
            myList.push_back(value);
        }
  • getchar();:由于 std::cin >> N; 读取了输入的整数 N,但是不会读取输入后的回车符(换行符),因此紧接着的 getchar(); 用来消耗掉这个回车符,避免它在后续的 std::getline() 中被误认为是输入的一部分。

  • std::string line;:声明一个字符串line,用于存储每次输入的整行命令。

  • for (int i = 0; i < N; i++):循环读取N次命令,其中N应该是预先定义的命令数量。

  • std::getline(std::cin, line):使用getline函数从标准输入读取一整行的命令,存储到line中。

  • std::istringstream iss(line):使用istringstream来解析输入的命令,将字符串line封装为一个输入流对象iss

  • std::string command;

  • iss >> command;:从输入流iss中读取第一个单词,将其赋值给字符串command,用于判断具体是哪种命令。

  • if (command == "push_back"):判断是否输入的命令是push_back,如果是,就继续读取一个整数value,并将其加入myList列表的末尾。

面试6.在使用 std::vector 的 erase 函数时,通常不需要直接使用 vector:: 前缀。
只要你通过一个 std::vector 对象或实例来调用 erase 函数,它就会正常工作
    std::vector<int> vec = {1, 2, 3, 4, 5};
    vec.erase(vec.begin() + 2);  // 移除第三个元素
如果你在代码中有多个 erase 函数(例如在不同的类或命名空间中),
并且你需要明确调用的是 std::vector 的 erase 函数,你可以使用 vector::erase。

    vec.vector::erase(vec.begin() + 2);  // 明确调用 std::vector 的 erase
面试7 野指针 悬垂指针
野指针:是未初始化的指针,指向随机的内存地址。它们可能引发不可预测的行为。
解决方法:在声明指针时将其初始化为 nullptr,或者立即将其指向一个合法的内存地址。

悬垂指针:是曾经指向有效内存,但该内存已经被释放或重新分配的指针。
继续使用它们同样可能引发未定义行为。
解决方法:在释放内存后,将指针设置为 nullptr。避免返回指向局部变量的指针。

#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <stdexcept>

template <typename T>
class Vector
{
private:
    T *elements;     // 指向动态数组的指针
    size_t capacity; // 数组的容量
    size_t size;     // 数组中元素的个数

public:
    // 构造函数
    Vector() : elements(nullptr), capacity(0), size(0) {}

    // 析构函数
    ~Vector()
    {
        delete[] elements;
    }

    // 拷贝构造函数
    Vector(const Vector &other) : capacity(other.capacity), size(other.size)
    {
        elements = new T[capacity];
        std::copy(other.elements, other.elements + size, elements);
    }

    // 拷贝赋值操作符 不是删other指针而是调用者对象指针
//比如g1 g2已有对象,将g1付给g2,要删掉g2原指针
    Vector &operator=(const Vector &other)
    {
        if (this != &other)
        {
            delete[] elements;
            capacity = other.capacity;
            size = other.size;
            elements = new T[capacity];
            std::copy(other.elements, other.elements + size, elements);
        }
        return *this;
    }

    // 添加元素到数组末尾
    void push_back(const T &value)
    {
        if (size == capacity)
        {
            // 如果数组已满,扩展容量
            reserve(capacity == 0 ? 1 : 2 * capacity);
        }
        elements[size++] = value;
    }

    // 获取数组中元素的个数
    size_t getSize() const
    {
        return size;
    }

    // 获取数组的容量
    size_t getCapacity() const
    {
        return capacity;
    }

    // 访问数组中的元素
    T &operator[](size_t index)
    {
        // 检查索引是否越界
        if (index >= size)
        {
            throw std::out_of_range("Index out of range");
        }
        return elements[index];
    }

    // const版本的访问数组中的元素
    const T &operator[](size_t index) const
    {
        // 检查索引是否越界
        if (index >= size)
        {
            throw std::out_of_range("Index out of range");
        }
        return elements[index];
    }

    // 在指定位置插入元素
    void insert(size_t index, const T &value)
    {
        if (index > size)
        {
            throw std::out_of_range("Index out of range");
        }
        if (size == capacity)
        {
            reserve(capacity == 0 ? 1 : capacity * 2);
        }
        for (size_t i = size; i > index; --i)
        {
            elements[i] = elements[i - 1];
        }
        elements[index] = value;
        ++size;
    }

    // 删除数组末尾的元素 
//删元素就是控制size
    void pop_back()
    {
        if (size > 0)
        {
            --size;
        }
    }

    // 清空数组
    void clear()
    {
        size = 0;
    }

    // 使用迭代器遍历数组的开始位置
    T *begin()
    {
        return elements;
    }

    // 使用迭代器遍历数组的结束位置
    T *end()
    {
        return elements + size;
    }

    // 使用迭代器遍历数组的开始位置(const版本)
    const T *begin() const
    {
        return elements;
    }

    // 使用迭代器遍历数组的结束位置(const版本)
    const T *end() const
    {
        return elements + size;
    }

    // 打印数组中的元素
    void printElements() const
    {
        for (size_t i = 0; i < size; ++i)
        {
            std::cout << elements[i] << " ";
        }
        std::cout << std::endl;
    }

private:
    // 扩展数组容量 
//需要改变指针 因为指针指向大小改变
    void reserve(size_t newCapacity)
    {
        if (newCapacity > capacity)
        {
            T *newElements = new T[newCapacity];
            std::copy(elements, elements + size, newElements);
            delete[] elements;
            elements = newElements;
            capacity = newCapacity;
        }
    }
};

int main()
{
    // 创建一个 Vector 对象
    Vector<int> myVector;

    int N;
    std::cin >> N;
    // 读走回车
    getchar();

    std::string line;
    for (int i = 0; i < N; i++)
    {
        // 读取整行
        std::getline(std::cin, line);
        std::istringstream iss(line);
        std::string command;
        iss >> command;

        if (command == "push")
        {
            int value;
            iss >> value;
            myVector.push_back(value);
        }
        else if (command == "print")
        {
            if (myVector.getSize() == 0) {
                std::cout << "empty" << std::endl;
                continue;
            }
            myVector.printElements();
        }
        else if (command == "size")
        {
            std::cout << myVector.getSize() << std::endl;
        }
        else if (command == "get")
        {
            int index;
            iss >> index;
            std::cout << myVector[index] << std::endl;
        }
        else if (command == "insert")
        {
            int index, value;
            iss >> index >> value;
            myVector.insert(index, value);
        }
        else if (command == "pop")
        {
            myVector.pop_back();
        }
        else if (command == "iterator")
        {
            if (myVector.getSize() == 0)
            {
                std::cout << "empty" << std::endl;
                continue;
            }
            for (auto it = myVector.begin(); it != myVector.end(); ++it)
            {
                std::cout << *it << " ";
            }
            std::cout << std::endl;
        }
        else if (command == "foreach")
        {
            if (myVector.getSize() == 0)
            {
                std::cout << "empty" << std::endl;
                continue;
            }
            for (const auto &element : myVector)
            {
                std::cout << element << " ";
            }
            std::cout << std::endl;
        }
        else if (command == "clear")
        {
            myVector.clear();
        }
    }
    return 0;
}

List

std::vector 是基于数组的动态数组,而 std::list 是基于双向链表的数据结构。
list适用于需要在序列中执行频繁插入和删除操作的场景。
要访问list中的元素,必须通过迭代器进行;
std::list 是一个双向链表容器,它和数组或 std::vector 不同,无法通过下标访问元素。
这是因为链表的节点在内存中不是连续存储的,而是通过指针进行链接的,
因此无法像数组那样通过索引直接访问特定位置的元素。
 list在进行插入和删除操作时,能够更好地保持迭代器的有效性。这意味着在进行这些操作后,不会导致所有迭代器失效
 由于list使用了链表结构,可能引入一些额外的内存开销。在内存使用方面,vector和deque可能更为紧凑
 每次插入都要添加一个结构体大小的内存
 链表的默认结构是先进先出
 双向链表:可以在尾部插入元素,随后在尾部移除元素,这样也能实现先进后出
 List类控制结构, 包含head, tail, size三个成员, 分别控制链表的头尾指针和大小
 
 创建新节点:将原tail的下一个指向新节点,然后更新tail;
 
 访问节点元素,因为节点指针head和tail指向的是节点,并没指向元素
 
 删除链表末尾元素:如果仅有一个元素,head=nullptr
 
 获取指定值节点:&node->next 第next节点数据val的地址
 
 清空链表:清空所有节点指针 size=0
 



#include <iostream>
#include <stdexcept>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>

template <typename T>
class List
{

private:
    // 定义节点结构
    struct Node
    {
        T data;     // 数据
        Node *next; // 指向下一个节点的指针
        Node *prev; // 指向前一个节点的指针

        // 构造函数
        Node(const T &value, Node *nextNode = nullptr, Node *prevNode = nullptr)
            : data(value), next(nextNode), prev(prevNode) {}
    };

    Node *head;  // 头节点指针
    Node *tail;  // 尾节点指针
    size_t size; // 链表中节点的数量

public:
    // 构造函数
    List() : head(nullptr), tail(nullptr), size(0) {}

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

    // 在链表末尾添加元素
    void push_back(const T &value)
    {
        // 创建新的节点
        Node *newNode = new Node(value, nullptr, tail);

        if (tail)
        {
            // 如果链表非空,将尾节点的 next 指针指向新节点
            tail->next = newNode;
        }
        else
        {
            // 如果链表为空,新节点同时也是头节点
            head = newNode;
        }

        // 更新尾节点指针和链表大小
        tail = newNode;
        ++size;
    }

    // 在链表开头添加元素
    void push_front(const T &value)
    {
        // 创建新的节点
        Node *newNode = new Node(value, head, nullptr);

        if (head)
        {
            // 如果链表非空,将头节点的 prev 指针指向新节点
            head->prev = newNode;
        }
        else
        {
            // 如果链表为空,新节点同时也是尾节点
            tail = newNode;
        }

        // 更新头节点指针和链表大小
        head = newNode;
        ++size;
    }

    // 获取链表中节点的数量
    size_t getSize() const { return size; }

    // 访问链表中的元素
    T &operator[](size_t index)
    {
        // 从头节点开始遍历链表,找到第 index 个节点
        Node *current = head;
        for (size_t i = 0; i < index; ++i)
        {
            if (!current)
            {
                // 如果 index 超出链表长度,则抛出异常
                throw std::out_of_range("Index out of range");
            }
            current = current->next;
        }

        // 返回节点中的数据
        return current->data;
    }

    // const版本的访问链表中的元素
    const T &operator[](size_t index) const
    {
        // 从头节点开始遍历链表,找到第 index 个节点
        Node *current = head;
        for (size_t i = 0; i < index; ++i)
        {
            if (!current)
            {
                // 如果 index 超出链表长度,则抛出异常
                throw std::out_of_range("Index out of range");
            }
            current = current->next;
        }

        // 返回节点中的数据
        return current->data;
    }

    // 删除链表末尾的元素
    void pop_back()
    {
        if (size > 0)
        {
            // 获取尾节点的前一个节点
            Node *newTail = tail->prev;

            // 删除尾节点
            delete tail;

            // 更新尾节点指针和链表大小
            tail = newTail;
            if (tail)
            {
                tail->next = nullptr;
            }
            else
            {
                head = nullptr; // 如果链表为空,头节点也置为空
            }

            --size;
        }
    }

    // 删除链表开头的元素
    void pop_front()
    {
        if (size > 0)
        {
            // 获取头节点的下一个节点
            Node *newHead = head->next;

            // 删除头节点
            delete head;

            // 更新头节点指针和链表大小
            head = newHead;
            if (head)
            {
                head->prev = nullptr;
            }
            else
            {
                tail = nullptr; // 如果链表为空,尾节点也置为空
            }

            --size;
        }
    }

    // 获取指定值的节点
    Node *getNode(const T &val)
    {
        Node *node = head;
        while (node != nullptr && node->val != val)
        {
            node = node->next;
        }

        return node;
    }

    T *find(const T &val)
    {
        Node *node = getNode(val);
        if (node == nullptr)
        {
            return nullptr;
        }
        return &node->val;
    }

    // 删除指定值的节点
    void remove(const T &val)
    {
        Node *node = head;
        while (node != nullptr && node->data != val)
        {
            node = node->next;
        }

        if (node == nullptr)
        {
            // 没有找到
            return;
        }
        if (node != head && node != tail)
        {
            // 既不是头结点也不是尾结点
            node->prev->next = node->next;
            node->next->prev = node->prev;
        }
        else if (node == head && node == tail)
        {
            // 既是头结点也是尾结点
            head = nullptr;
            node = nullptr;
        }
        else if (node == head)
        {
            // 是头结点
            head = node->next;
            head->prev = nullptr;
        }
        else
        {
            // 是尾结点
            tail = node->prev;
            tail->next = nullptr;
        }

        --size;

        delete node;
        node = nullptr;
    }

    bool empty() { return size == 0; }

    // 清空链表
    void clear()
    {
        while (head)
        {
            // 从头节点开始,依次删除节点
            Node *temp = head;
            head = head->next;
            delete temp;
        }

        // 更新尾节点指针和链表大小
        tail = nullptr;
        size = 0;
    }

    // 使用迭代器遍历链表的开始位置
    Node *begin() { return head; }

    // 使用迭代器遍历链表的结束位置
    Node *end() { return nullptr; }

    // 使用迭代器遍历链表的开始位置(const版本)
    const Node *begin() const { return head; }

    // 使用迭代器遍历链表的结束位置(const版本)
    const Node *end() const { return nullptr; }

    // 打印链表中的元素
    void printElements() const
    {
        for (Node *current = head; current; current = current->next)
        {
            std::cout << current->data << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    // 创建一个 List 对象
    List<int> myList;
    
    int N;
    std::cin >> N;
    // 读走回车
    getchar();
    std::string line;
    // 接收命令
    for (int i = 0; i < N; i++) {
        std::getline(std::cin, line);
        std::istringstream iss(line);
        std::string command;
        iss >> command;
        int value;

        if (command == "push_back") {
            iss >> value;
            myList.push_back(value);
        }

        if (command == "push_front") {
            iss >> value;
            myList.push_front(value);
        }

        if (command == "pop_back") {
            myList.pop_back();
        }

        if (command == "pop_front") {
            myList.pop_front();
        }

        if (command == "remove") {
            iss >> value;
            myList.remove(value);
        }

        if (command == "clear") {
            myList.clear();
        }

        if (command == "size") {
            std::cout << myList.getSize() << std::endl;
        }

        if (command == "get") {
            iss >> value;
            std::cout << myList[value] << std::endl;
        }

        if (command == "print") {
            if (myList.getSize() == 0) {
                std::cout << "empty" << std::endl;
            } else {
                myList.printElements();
            }
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值