基于Redis 设计与实现实现的redis,根据书上的API描述,自己实现C++版本的redis。
list是常见的数据结构,但是redis有更加抽象的实现,就很,妙,但是也会有很多bug,他用了函数指针,指来指去的。
上代码
#pragma once
#include <memory>
#include <functional>
struct listNode
{
// 前置节点
std::shared_ptr<listNode> prev=nullptr;
// 后置节点
std::shared_ptr<listNode> next=nullptr;
// 节点的值
void* value=nullptr;
listNode(void *value):value(value){};
// 返回给定节点的前置节点
std::shared_ptr<listNode> listPrevNode(){
return this->prev;
};
// 返回给定节点的后置节点
std::shared_ptr<listNode> listNextNode(){
return this->next;
};
// 返回给定节点当前保存的值
void* listNodeValue(){
return this->value;
};
};
class list {
private:
// 表头节点
std::shared_ptr<listNode> head=nullptr;
// 表尾节点
std::shared_ptr<listNode> tail=nullptr;
// 链表所包含的节点数量
unsigned long len=0;
// 节点值复制函数
std::function<void *(void *)> dup=nullptr;
// 节点值释放函数
std::function<void(void *)> free=nullptr;
// 节点值对比函数
std::function<int(void *, void *)> match=nullptr;
public:
// 设置链表的节点值复制函数
void listSetDupMethod(std::function<void *(const void *)> dup){
this->dup=dup;
};
// 返回当前链表正在使用的节点值复制函数
std::function<void *(void *)> listGetDupMethod(){
return this->dup;
};
// 设置链表的节点值释放函数
void listSetFreeMethod(std::function<void(void *)> free){
this->free=free;
};
// 返回当前链表正在使用的节点值释放函数
std::function<void(void *)> listGetFree(){
return this->free;
};
// 设置链表的节点值对比函数
void listSetMatchMethod(std::function<int(const void *, const void *)> match){
this->match=match;
};
// 返回当前链表正在使用的节点值对比函数
std::function<int(void *, void *)> listGetMatchMethod(){
return this->match;
};
// 返回链表的长度
unsigned long listLength() const{
return this->len;
};
// 返回链表的表头节点
std::shared_ptr<listNode> listFirst(){
return this->head;
};
// 返回链表的表尾节点
std::shared_ptr<listNode> listLast(){
return this->tail;
};
// 创建一个新的空链表
list(){
}
list(void *value){
listAddNodeHead(value);
}
~list(){
listRelease();
}
// 添加一个新节点到链表的表头
std::shared_ptr<listNode> listAddNodeHead(void *value){
std::shared_ptr<listNode> node=std::make_shared<listNode>(this->dup(value));
node->next=this->head;
if(this->head){
this->head->prev=node;
}
this->head=node;
if(!this->tail){
this->tail=node;
}
this->len++;
return node;
};
// 添加一个新节点到链表的表尾
std::shared_ptr<listNode> listAddNodeTail(void *value){
std::shared_ptr<listNode> node=std::make_shared<listNode>(this->dup(value));
node->prev=this->tail;
if(this->tail){
this->tail->next=node;
}
this->tail=node;
if(!this->head){
this->head=node;
}
this->len++;
return node;
};
// 将一个新节点插入到指定节点的前面或后面
std::shared_ptr<listNode> listInsertNode(std::shared_ptr<listNode> old_node, void *value, int after){
//after:插入位置,如果设为 1,则在旧节点之后插入,如果设为 0,则在旧节点之前插入
if(this->len == 0){
return listAddNodeHead(value);
}
std::shared_ptr<listNode> node=std::make_shared<listNode>(this->dup(value));
if(after){
node->prev=old_node;
node->next=old_node->next;
if(old_node->next){
old_node->next->prev=node;
}
old_node->next=node;
if(old_node == this->tail){
this->tail=node;
}
}else{
node->next=old_node;
node->prev=old_node->prev;
if(old_node->prev){
old_node->prev->next=node;
}
old_node->prev=node;
if(old_node == this->head){
this->head=node;
}
}
this->len++;
return node;
};
// 查找并返回链表中包含指定值的节点
std::shared_ptr<listNode> listSearchKey(void *key){
std::shared_ptr<listNode> node=this->head;
while(node){
if(this->match(node->value, key) == true){
return node;
}
node=node->next;
}
return nullptr;
};
// 返回链表指定索引上的节点
std::shared_ptr<listNode> listIndex(long index){
if(index < 0 || (unsigned long)index >= this->len){
return nullptr;
}
std::shared_ptr<listNode> node=this->head;
while(index--){
node=node->next;
}
return node;
};
// 从链表中删除指定的节点
void listDelNode(std::shared_ptr<listNode> node){
std::shared_ptr<listNode> curr=this->head;
while(curr){
if(curr==node){
if(node->prev){
node->prev->next=node->next;
}
if(node->next){
node->next->prev=node->prev;
}
this->len--;
break;
}
curr=curr->next;
}
//更新head和tail
if(node == this->head){
this->head=node->next;
}
if(node == this->tail){
this->tail=node->prev;
}
//释放节点
if(this->free){
this->free(node->value);
}
};
// 将链表的表尾节点移动到表头
void listRotate(){
if(this->len == 0 || this->len == 1){
return;
}
if(head->next){
head->next->prev=this->tail;
tail->next=head->next;
}
if(tail->prev){
tail->prev->next=this->head;
head->prev=tail->prev;
}
head->next=nullptr;
tail->prev=nullptr;
std::swap(head, tail);
};
// 复制一个给定链表的副本
std::shared_ptr<list> listDup(){
std::shared_ptr<list> newlist=std::make_shared<list>();
newlist->dup=this->dup;
newlist->free=this->free;
newlist->match=this->match;
newlist->len=this->len;
std::shared_ptr<listNode> node=std::make_shared<listNode>(this->dup(this->head->value));
std::shared_ptr<listNode> curr=this->head;
newlist->head=node;
while(curr->next){
node->next=std::make_shared<listNode>(this->dup(curr->next->value));
node->next->prev=node;
node=node->next;
curr=curr->next;
}
newlist->tail=node;
return newlist;
};
//释放给定链表,以及链表中的所有节点。
void listRelease(){
std::shared_ptr<listNode> curr=this->head;
while(curr){
std::shared_ptr<listNode> next=curr->next;
if(this->free){
this->free(curr->value);
}
curr=next;
}
this->head=nullptr;
this->tail=nullptr;
this->len=0;
}
};
数据结构
listnode没啥好说的,看list
head , tail 分别存储了头指针和尾指针
len 是链表的长度
dup 函数指针,用于指向节点的复制函数。这里需要提一下,为啥要用这个函数指针?想到函数的浅拷贝,当传入的一个值是指针的时候,浅拷贝只会复制指针,不会新new一个数据,这里也是一样的,当链表的数据是指针的时候,需要自己new一个新地址。
free 函数指针,用于指向节点的释放函数。和dup相对应,有构造,就有释放
match 函数指针,比较两个节点是否相等。
测试代码
不想说啥了,gpt写的,难受
#include <gtest/gtest.h>
#include "list.h"
#include <iostream>
using namespace std;
class ListTest : public ::testing::Test {
protected:
void SetUp() override {
// Set up code
myList.listSetDupMethod([](const void *value) {
int myvalue=*(int *)value;
int *newValue = new int;
*newValue = myvalue;
cout<<std::hex<<newValue<<" is created \n";
return newValue;
});
myList.listSetFreeMethod([](void *value) {
if(value!= nullptr){
cout<<std::hex<<value<<" is delete \n";
delete static_cast<int *>(value);
}
});
myList.listSetMatchMethod([](const void *a, const void *b) {
return (*(static_cast<const int *>(a)) == *(static_cast<const int *>(b))) ? 1 : 0;
});
}
// Declare the list instance
list myList;
};
// Test adding nodes to the head of the list
TEST_F(ListTest, AddNodeHead) {
int value1 = 10;
std::shared_ptr<listNode> node1 = myList.listAddNodeHead(&value1);
ASSERT_EQ(myList.listLength(), 1);
int *headValue = static_cast<int *>(myList.listFirst()->listNodeValue());
ASSERT_EQ(*headValue, value1);
//myList.listGetFree()(headValue); 不要调用free,否则会导致内存泄漏,会有析沟函数自动释放
}
// Test adding nodes to the tail of the list
TEST_F(ListTest, AddNodeTail) {
int value1 = 10;
int value2 = 20;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
ASSERT_EQ(myList.listLength(), 2);
int *tailValue = static_cast<int *>(myList.listLast()->listNodeValue());
ASSERT_EQ(*tailValue, value2);
//myList.listGetFree()(tailValue);
}
// Test inserting nodes into the list
TEST_F(ListTest, InsertNode) {
int value1 = 10;
int value2 = 20;
int value3 = 30;
std::shared_ptr<listNode> node1 = myList.listAddNodeHead(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
std::shared_ptr<listNode> node3 = myList.listInsertNode(node2, &value3, 1);
ASSERT_EQ(myList.listLength(), 3);
std::shared_ptr<listNode> nextNode = node1->listNextNode();
ASSERT_EQ(nextNode, node2);
}
// Test node value comparison
TEST_F(ListTest, NodeValueComparison) {
int value1 = 10;
std::shared_ptr<listNode> node1 = myList.listAddNodeHead(&value1);
int value2 = 20;
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
ASSERT_EQ(myList.listGetMatchMethod()(node1->value, &value1), 1);
ASSERT_EQ(myList.listGetMatchMethod()(node2->value, &value2), 1);
}
// Test list search key
TEST_F(ListTest, ListSearchKey) {
int value1 = 10;
int value2 = 20;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
std::shared_ptr<listNode> result = myList.listSearchKey(&value1);
ASSERT_EQ(result, node1);
result = myList.listSearchKey(&value2);
ASSERT_EQ(result, node2);
int value3 = 30;
result = myList.listSearchKey(&value3);
ASSERT_EQ(result, nullptr);
}
TEST_F(ListTest, ListSearchKeyEmptyList) {
int value = 10;
std::shared_ptr<listNode> result = myList.listSearchKey(&value);
ASSERT_EQ(result, nullptr);
}
// Test list index
TEST_F(ListTest, ListIndex) {
int value1 = 10;
int value2 = 20;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
std::shared_ptr<listNode> result = myList.listIndex(0);
ASSERT_EQ(result, node1);
result = myList.listIndex(1);
ASSERT_EQ(result, node2);
result = myList.listIndex(2);
ASSERT_EQ(result, nullptr);
}
TEST_F(ListTest, ListIndexOutOfRange) {
int value = 10;
std::shared_ptr<listNode> result = myList.listIndex(-1);
ASSERT_EQ(result, nullptr);
result = myList.listIndex(0);
ASSERT_EQ(result, nullptr);
}
// Test list delete node
TEST_F(ListTest, ListDelNode) {
int value1 = 10;
int value2 = 20;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
myList.listDelNode(node1);
ASSERT_EQ(myList.listFirst(), node2);
ASSERT_EQ(myList.listLength(), 1);
}
TEST_F(ListTest, ListDelNodeTail) {
int value1 = 10;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
myList.listDelNode(node1);
ASSERT_EQ(myList.listFirst(), nullptr);
ASSERT_EQ(myList.listLast(), nullptr);
ASSERT_EQ(myList.listLength(), 0);
}
// Test list rotate
TEST_F(ListTest, ListRotate) {
int value1 = 10;
int value2 = 20;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
myList.listRotate();
ASSERT_EQ(myList.listFirst(), node2);
ASSERT_EQ(myList.listLast(), node1);
}
TEST_F(ListTest, ListRotateSingleElement) {
int value1 = 10;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
myList.listRotate();
ASSERT_EQ(myList.listFirst(), node1);
ASSERT_EQ(myList.listLast(), node1);
}
// Test list duplicate
TEST_F(ListTest, ListDup) {
int value1 = 10;
int value2 = 20;
std::shared_ptr<listNode> node1 = myList.listAddNodeTail(&value1);
std::shared_ptr<listNode> node2 = myList.listAddNodeTail(&value2);
std::shared_ptr<list> newList = myList.listDup();
ASSERT_EQ(newList->listLength(), myList.listLength());
std::shared_ptr<listNode> newNode1 = newList->listFirst();
ASSERT_EQ(*(static_cast<int *>(newNode1->value)), value1);
std::shared_ptr<listNode> newNode2 = newList->listLast();
ASSERT_EQ(*(static_cast<int *>(newNode2->value)), value2);
}