总所周知,Python语言当中的list是可以存储不同类型的元素的,对应到现代C++当中,可以用std::variant或者std::any实现类似的功能。而Python官方的实现当中用到了二级指针,不过抛开这些,我们也可以自己设计一个list的架构,实现多类型值的存储容器。
下图是自己实现的list的架构,按照这个架构,我们来逐步分析代码。不过为了节省篇幅,我仅仅只实现了一部分的方法,比如append,但是这里我们着重的是容器的设计。
我们自顶向下分析。list这个结构体是最终要实现的容器,里面包含了一个指向__list的指针,__list里面存着一系列的Node节点。除了指针,还有offset偏移量,记录当前__list指针ptr的偏移量,size是list的元素大小,而最后一个联合体u则为了实现多值存储而塞的一个成员。Node这边,含有一个void类型的指针,它可以指向任意元素的地址,待会我们会将它转换回对应的元素类型,从而获取其指向的值。type记录该指针指向的具体类型。
以下对应了这三个结构体的实现。
struct Node {
void *data = nullptr;
int type;
};
struct __list {
Node node;
};
struct list {
__list *ptr;
int offset{};
int size;
U u;
list(int size) : size(size) {
ptr = static_cast(malloc(sizeof(__list) * (size + 1)));
}
list(const list& other) = default;
~list() {
ptr -= offset;
free(ptr);
}
}
在分配内存的时候,要注意额外分配多一个空位,因为ptr是指向list最后元素的下一个位置。析构函数的时候也要记得将ptr回退到最开始的位置,不然会出现内存方面的问题。
在类型方面,这里仅写了几种常用的类型,可以按照实际需要补充更多的类型上去。
enum {
INT,
UINT,
CHAR,
UCHAR,
FLOAT,
DOUBLE
};
append函数,这里我没有使用泛型实现,而是使用了函数重载,觉得比较好写,以下是int类型的实现,其它类型同理,只需要稍微改改。
void append(uint& __data) {
ptr->node.data = static_cast(&__data);
ptr->node.type = UINT;
if (offset + 1
另外,还重载了[]运算符,这里就用到了前面所提到的union了,这里设定了返回值为union,这样可以比较巧妙的处理不同返回值的情况。
U operator[](int index) {
auto it = ptr - offset + index;
auto __data = it->node.data;
int type = it->node.type;
switch (type) {
case INT: {
u.intData = *(static_cast(__data));
u.type = INT;
break;
}
case UINT: {
u.uintData = *static_cast(__data);
u.type = UINT;
break;
}
case CHAR: {
u.charData = *static_cast(__data);
u.type = CHAR;
break;
}
case UCHAR: {
u.ucharData = *static_cast(__data);
u.type = UCHAR;
break;
}
case FLOAT: {
u.floatData = *static_cast(__data);
u.type = FLOAT;
break;
}
case DOUBLE: {
u.doubleData = *static_cast(__data);
u.type = DOUBLE;
break;
}
default: {
assert(0);
}
}
return u;
}
为了最终可以遍历元素并且输出出来,还需要对union进行重载一下。
struct U {
union {
int intData;
uint uintData;
char charData;
u_char ucharData;
float floatData;
double doubleData;
};
// To figure out which type we're using
int type;
friend std::ostream& operator
服务器托管网(能用switch代替if else就尽量代替)
到这里,所设计的list就差不多了,剩下的函数可以由读者来拓展。不过还有局限性,可以看看它怎么使用。
int main() {
list lst{3};
std::vector v{1, 2, 3};
for (int i{}; i
由于没有写对右值数据的处理,所以只能先将想要存的数据存入另一个容器当中。我们再来测试一下。
int main() {
list lst{3};
int a = 1;
double b = 1.1;
char c = 'c';
lst.append(a);
lst.append(b);
lst.append(c);
for (int i{}; i
运行结果是1, 1.1, c,符合预期。
以下是完整代码
#include
#include
#include
#include
#include
enum {
INT,
UINT,
CHAR,
UCHAR,
FLOAT,
DOUBLE
};
struct U {
union {
int intData;
uint uintData;
char charData;
u_char ucharData;
float floatData;
double doubleData;
};
// To figure out which type we're using
int type;
friend std::ostream& operator(malloc(sizeof(__list) * (size + 1)));
}
list(const list& other) = default;
list& operator=(const list& other) = default;
~list() {
ptr -= offset;
free(ptr);
}
void append(int& __data) {
if (offset + 1 node.data = static_cast(&__data);
ptr->node.type = INT;
++ptr;
++offset;
}
else
std::cout node.data = static_cast(&__data);
ptr->node.type = FLOAT;
if (offset + 1 node.data = static_cast(&__data);
ptr->node.type = DOUBLE;
if (offset + 1 node.data = static_cast(&__data);
ptr->node.type = CHAR;
if (offset + 1 node.data = static_cast(&__data);
ptr->node.type = UCHAR;
if (offset + 1 node.data = static_cast(&__data);
ptr->node.type = UINT;
if (offset + 1 node.data;
int type = it->node.type;
switch (type) {
case INT: {
u.intData = *(static_cast(__data));
u.type = INT;
break;
}
case UINT: {
u.uintData = *static_cast(__data);
u.type = UINT;
break;
}
case CHAR: {
u.charData = *static_cast(__data);
u.type = CHAR;
break;
}
case UCHAR: {
u.ucharData = *sta服务器托管网tic_cast(__data);
u.type = UCHAR;
break;
}
case FLOAT: {
u.floatData = *static_cast(__data);
u.type = FLOAT;
break;
}
case DOUBLE: {
u.doubleData = *static_cast(__data);
u.type = DOUBLE;
break;
}
default: {
assert(0);
}
}
return u;
}
};
到这里,一个Pythonic的list就成型了,剩下的其它函数实现方式也就大同小异。在设计list的时候,由于设计到指针,因此对于内存泄露方面需要比较谨慎。以上的实现仅仅涉及到了一级指针,Python官方实现是采用二级指针,感兴趣的话可以去学习学习别人是怎么实现的~
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
系列文章 Nomad 系列文章 Nomad 重要术语 Nomad 安装设置相关术语 agent – 代理。Agent 是在 Server(服务器) 或 Client(客户端) 模式下运行的 Nomad 进程。 client – 客户端。Nomad 客户端负责运…