手撕代码系列

本文展示了C++中实现的一些基础内存操作函数,如memmov用于处理可能有内存重叠的复制,strcpy用于字符串复制,以及atoi将字符串转换为整数。此外,还介绍了LRU缓存的实现,使用双向链表和哈希映射保持最近最少使用的数据项。最后,自定义了一个简单的Vector类和String类,模拟C++标准库中的容器和字符串行为。
摘要由CSDN通过智能技术生成

- memmov

#include <iostream>
#include <cstring>

void* memmov(void* destination, const void* source, size_t num)
{
    char* dest = static_cast<char*>(destination);
    const char* src = static_cast<const char*>(source);

    // Check for overlapping memory regions
    if (dest < src)
    {
        // Copy forward starting from the beginning
        for (size_t i = 0; i < num; ++i)
        {
            dest[i] = src[i];
        }
    }
    else if (dest > src)
    {
        // Copy backward starting from the end
        for (size_t i = num; i > 0; --i)
        {
            dest[i - 1] = src[i - 1];
        }
    }

    return destination;
}

int main()
{
    char source[] = "Hello, World!";
    char destination[20];

    memmov(destination, source, sizeof(source));

    std::cout << "Destination: " << destination << std::endl;

    return 0;
}

在上面的代码中,memmov 函数接受三个参数:目标内存区域的指针 destination、源内存区域的指针 source 和要复制的字节数 num。函数的返回类型是 void*,表示指向目标内存区域的指针。

在函数内部,我们使用指针操作来逐字节复制内存内容。首先,我们将 destinationsource 强制转换为 char* 类型的指针,以便进行字节级别的复制。然后,我们检查目标区域和源区域是否有重叠。如果目标区域的起始地址小于源区域的起始地址,则我们从头开始逐字节复制;如果目标区域的起始地址大于源区域的起始地址,则我们从尾部开始逐字节复制。

最后,我们在 main 函数中测试 memmov 函数,将一个字符串从源数组复制到目标数组,并打印目标数组的内容。

请注意,虽然我们使用 C++ 编写了上述代码,但该函数的实现与 C 语言中的 memmove 函数是相同的。

 

- strcpy

#include <iostream>
#include <cstring>

char* strcpy(char* destination, const char* source)
{
    char* dest = destination;
    const char* src = source;

    while (*src != '\0')
    {
        *dest = *src;
        ++dest;
        ++src;
    }

    *dest = '\0';

    return destination;
}

int main()
{
    char source[] = "Hello, World!";
    char destination[20];

    strcpy(destination, source);

    std::cout << "Destination: " << destination << std::endl;

    return 0;
}

在上述代码中,strcpy 函数接受两个参数:目标字符串的指针 destination 和源字符串的指针 source。函数的返回类型是 char*,表示指向目标字符串的指针。

在函数内部,我们使用指针操作来逐字符复制字符串。首先,我们将 destinationsource 的地址分别赋给 destsrc 指针。然后,我们使用 while 循环,将 src 指针所指向的字符复制到 dest 指针所指向的位置,并递增 destsrc 指针,直到遇到源字符串的结尾符 '\0'。最后,我们在目标字符串的末尾添加一个结尾符 '\0',以确保复制后的字符串正确终止。

最后,我们在 main 函数中测试 strcpy 函数,将一个源字符串复制到目标字符串,并打印目标字符串的内容。

请注意,虽然我们使用 C++ 编写了上述代码,但该函数的实现与 C 语言中的 strcpy 函数是相同的。

 

- atoi

#include <iostream>
#include <cstdlib>

int atoi(const char* str)
{
    int result = 0;
    int sign = 1;
    int i = 0;

    // Skip leading whitespaces
    while (str[i] == ' ')
    {
        ++i;
    }

    // Check for sign
    if (str[i] == '+' || str[i] == '-')
    {
        sign = (str[i] == '-') ? -1 : 1;
        ++i;
    }

    // Convert digits to integer
    while (str[i] >= '0' && str[i] <= '9')
    {
        result = result * 10 + (str[i] - '0');
        ++i;
    }

    return result * sign;
}

int main()
{
    const char* str = "12345";

    int number = atoi(str);

    std::cout << "Number: " << number << std::endl;

    return 0;
}

 

在上述代码中,atoi 函数接受一个指向以 null 结尾的字符串的指针 str,并返回相应的整数值。

函数内部首先初始化结果 result 为 0,符号 sign 为 1,并定义一个索引变量 i 来遍历字符串。

然后,我们使用一个循环来跳过字符串中的前导空格。接着,我们检查字符串中是否有符号标记 '+' 或 '-',并根据符号标记更新 sign 的值,并将索引 i 向后移动。

最后,我们使用另一个循环将字符串中的数字字符转换为整数。我们将 result 乘以 10,并加上当前数字字符与字符 '0' 的差值,以得到相应的数字。然后,我们递增索引 i,继续处理下一个字符,直到遇到非数字字符为止。

最后,我们将结果乘以符号 sign,以考虑到正负号,并将最终的结果返回。

main 函数中,我们测试了 atoi 函数,将一个字符串转换为相应的整数,并打印结果。

- LRU

#include <iostream>
#include <unordered_map>
#include <list>

class LRUCache {
private:
    std::unordered_map<int, std::pair<int, std::list<int>::iterator>> cache;
    std::list<int> lruList;
    int capacity;

public:
    LRUCache(int capacity) {
        this->capacity = capacity;
    }

    int get(int key) {
        if (cache.find(key) == cache.end()) {
            return -1;  // Key not found
        }

        // Move accessed key to the front of the LRU list
        lruList.erase(cache[key].second);
        lruList.push_front(key);
        cache[key].second = lruList.begin();

        return cache[key].first;
    }

    void put(int key, int value) {
        if (cache.find(key) != cache.end()) {
            // Key already exists, update the value and move it to the front of the LRU list
            lruList.erase(cache[key].second);
        }
        else if (cache.size() >= capacity) {
            // Remove the least recently used key from the cache and the LRU list
            int lastKey = lruList.back();
            lruList.pop_back();
            cache.erase(lastKey);
        }

        // Add the new key-value pair to the cache and the front of the LRU list
        lruList.push_front(key);
        cache[key] = { value, lruList.begin() };
    }
};

int main() {
    LRUCache cache(2);

    cache.put(1, 1);
    cache.put(2, 2);
    std::cout << cache.get(1) << std::endl;  // Output: 1

    cache.put(3, 3);
    std::cout << cache.get(2) << std::endl;  // Output: -1

    cache.put(4, 4);
    std::cout << cache.get(1) << std::endl;  // Output: -1
    std::cout << cache.get(3) << std::endl;  // Output: 3
    std::cout << cache.get(4) << std::endl;  // Output: 4

    return 0;
}

在上述代码中,LRUCache 类实现了一个简单的 LRU 缓存。它使用一个无序哈希映射 cache 来存储键值对,其中键是缓存的键,值是包含缓存值和对应的 LRU 列表迭代器的 pair 对象。LRU 列表 lruList 是一个双向链表,存储了最近访问的键,最近访问的键位于链表的前端。

在构造函数中,我们初始化了缓存的容量 capacity

get 方法用于获取给定键的值。如果键不存在于缓存中,返回 -1;否则,将访问的键移动到 LRU 列表的前端,并更新其对应的迭代器。然后,返回键对应的值。

put 方法用于将键值对放入缓存中。如果键已经存在于缓存中,更新对应的值,并将键移动到 LRU 列表的前端。如果缓存已满,需要移除最近最少使用的键(即 LRU 列表的末尾键),然后再将新键值对插入到缓存和 LRU 列表的前端。

main 函数中,我们创建一个容量为 2 的缓存对象 cache,并演示了一些使用情况,包括插入键值对和获取键对应的值。

- vector

#include <iostream>
#include <stdexcept>

template <typename T>
class Vector {
private:
    T* elements;
    size_t size;
    size_t capacity;

public:
    Vector() : elements(nullptr), size(0), capacity(0) {}

    ~Vector() {
        delete[] elements;
    }

    size_t Size() const {
        return size;
    }

    bool Empty() const {
        return size == 0;
    }

    T& operator[](size_t index) {
        if (index >= size) {
            throw std::out_of_range("Index out of range");
        }
        return elements[index];
    }

    const T& operator[](size_t index) const {
        if (index >= size) {
            throw std::out_of_range("Index out of range");
        }
        return elements[index];
    }

    void PushBack(const T& value) {
        if (size >= capacity) {
            size_t newCapacity = (capacity == 0) ? 1 : capacity * 2;
            Reserve(newCapacity);
        }

        elements[size] = value;
        ++size;
    }

    void PopBack() {
        if (size > 0) {
            --size;
        }
    }

    void Reserve(size_t newCapacity) {
        if (newCapacity <= capacity) {
            return;
        }

        T* newElements = new T[newCapacity];
        for (size_t i = 0; i < size; ++i) {
            newElements[i] = elements[i];
        }

        delete[] elements;
        elements = newElements;
        capacity = newCapacity;
    }
};

int main() {
    Vector<int> vec;

    vec.PushBack(1);
    vec.PushBack(2);
    vec.PushBack(3);

    std::cout << "Size: " << vec.Size() << std::endl;

    for (size_t i = 0; i < vec.Size(); ++i) {
        std::cout << vec[i] << " ";
    }
    std::cout << std::endl;

    vec.PopBack();
    std::cout << "Size after PopBack: " << vec.Size() << std::endl;

    return 0;
}

在上述代码中,Vector 类模板定义了一个动态数组。它使用指针 elements 来存储数组元素,size 表示当前存储的元素个数,capacity 表示当前数组的容量。

类中包含了一些常用的成员函数:

  • Size 返回当前数组的大小。
  • Empty 判断数组是否为空。
  • operator[] 重载了下标操作符,可以通过下标访问数组元素。
  • PushBack 在数组尾部插入一个元素。
  • PopBack 删除数组尾部的一个元素。
  • Reserve 调整数组的容量,以便能够存储更多的元素。

main 函数中,我们演示了如何使用 Vector 类来创建一个动态数组,插入元素,访问元素,删除元素,并打印数组的大小。

请注意,上述代码是一个简化的 Vector 类示例,没有实现所有可能的成员函数和功能。在实际应用中,

 

- string

#include <iostream>
#include <cstring>

class String {
private:
    char* data;
    size_t length;

public:
    String() : data(nullptr), length(0) {}

    String(const char* str) {
        length = std::strlen(str);
        data = new char[length + 1];
        std::strcpy(data, str);
    }

    String(const String& other) {
        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);
    }

    ~String() {
        delete[] data;
    }

    size_t Length() const {
        return length;
    }

    const char* CStr() const {
        return data;
    }

    String& operator=(const String& other) {
        if (this != &other) {
            delete[] data;
            length = other.length;
            data = new char[length + 1];
            std::strcpy(data, other.data);
        }
        return *this;
    }

    String operator+(const String& other) const {
        String result;
        result.length = length + other.length;
        result.data = new char[result.length + 1];
        std::strcpy(result.data, data);
        std::strcat(result.data, other.data);
        return result;
    }

    char& operator[](size_t index) {
        if (index >= length) {
            throw std::out_of_range("Index out of range");
        }
        return data[index];
    }

    const char& operator[](size_t index) const {
        if (index >= length) {
            throw std::out_of_range("Index out of range");
        }
        return data[index];
    }
};

int main() {
    String s1 = "Hello";
    String s2 = " World";

    String s3 = s1 + s2;
    std::cout << "Concatenated String: " << s3.CStr() << std::endl;

    std::cout << "Length of s1: " << s1.Length() << std::endl;

    s1[0] = 'h';
    std::cout << "Updated s1: " << s1.CStr() << std::endl;

    return 0;
}

在上述代码中,String 类实现了一个简单的字符串类。它使用指针 data 来存储字符串的字符数组,length 表示字符串的长度。

类中包含了一些常用的成员函数:

  • Length 返回字符串的长度。
  • CStr 返回字符串的 C 风格字符数组。
  • operator= 重载了赋值运算符,用于将一个 String 对象赋值给另一个对象。
  • operator+ 重载了加号运算符,用于连接两个字符串。
  • operator[] 重载了下标操作符,用于访问字符串的字符。

main 函数中,我们演示了如何使用 String 类来创建字符串对象,连接字符串,获取字符串长度,并修改字符串中的字符。

请注意,上述代码是一个简化的 String 类示例,没有实现所有可能的成员函数和功能。在实际应用中,可能需要考虑更多的字符串操作,如插入、删除、查找等。此外,为了确保安全性和效率,可能需要实现更多的成员函数和优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值