list 2
指针字节数只看系统架构和模板 T 无关32 位 x86 平台所有指针都是 4 字节64 位 x64 平台VS 默认现在都是 64 位所有指针都是 8 字节继续在 类模板list_iterator完成重载1后置 --下图用到了tmp(*this)拷贝构造但是类模板list_iterator没写这里用的是浅拷贝的但是不影响要的就是指向同一块空间所以析构也不需要写2.迭代器模拟指针的行为所以要将—重载假如有一个类AA将AA当作T传入并测试如下黑圈代码有对象AA data,其中存有两值_a11,_a21运行失败为什么库里面的operator不支持自己定义的类法1 修改输出方式.法2. 重载—,_node迭代器内部成员是list_nodeT*指针指向链表节点。_node-_data节点内部的实体对象T。_node-_data取这个T对象的地址返回T*指针。使用是这样写运算符重载可以显示调用现在加入下图函数模板print_container并打印在测试函数test_list1()中调用它会显示函数模板print_container中范围for不支持再将for循环单独拿出来加入到It对象的函数内测试如下图。将上图横线代码屏蔽如下右图运行下面左图代码验证for循环单独拿出来加入到It对象的函数内能否运行成功。发现运行成功。为什么print_container内的范围for不支持单独拿出来的就支持范围for底层是迭代器。print_container形参是const修饰表示这个函数只读不修改容器所以迭代器类型是常量迭代器 。其范围for单独拿出来lt就是个普通容器其迭代器当然也是普通迭代器。所以要实现const迭代器因为所以用右图第二种思考迭代其如何让其指向内容不能修改?我们在类模板list_iterator中用operator operator-来获取数据所以在这两个函数上做文章修改为下图即可支持print_container内的范围for所以我们的类模板list_iterator也要有两个版本一个普通list_iterator一个list_const_iterator。在类模板list中补充list_const_iterator中其他与普通list_iterator一样只有operator operator-变为上上图将类模板list再补充beginend() 的const版本普通对象和const对象都可调用const成员函数注意将之前print_container内迭代器名称修改过来如下图红色横线。print_container内的范围for不支持和单独拿出来就支持两者原理如下两图。运行程序此时运行的是测试函数test_list1()如下下图。print_containerhan函数运行代码此时已注释掉。发现能运行成功但仔细分析上上图 *返回的是const T 不能再改变返回值所以*it 10错误 但为什么运行成功因为模板是按需实例化实例化调用模板之后才会严谨的走代码逻辑不实例化只会找出基础语法错误比如少括号少写我们现在只运行测试函数test_list1()如上上图并没有调用函数模板print_containerhan。函数模板print_containerhan并没有实例化。现在运行函数模板print_containerhan则报错将*it 10注释掉运行成功·测试迭代器失效原来迭代器it绑定的节点值为 1本身没有被移动、没有被销毁迭代器仍然有效依然指向原来那个节点。insert无迭代器失效问题因为其内存不连续erase呢?以删除所有偶数为例在上面代码后续写以下代码it还是指向被删节点it已经是野指针不能继续在下图文件包上assert.herase中有文件否则报错解决办法用将erase后的返回值是下一个节点的迭代器将it更新。当然我们自己的erase的返回值下一个节点的迭代器还没有该功能。实现erase的返回值是下一个节点的迭代器以下打印 成功将自己的insert没有返回插入节点的迭代器与库中的insert返回插入节点的迭代器功能同步实现析构函数实现拷贝构造默认生成的还是浅拷贝得自己写深拷贝实现如下行吗不行因为push_back调用的前提是得有头节点已经自己指向了自己这里的头节点是this指针指向的It2的头节点虽然已经给头节点分配了内存但是其中是乱码并没有自己指向自己所以判空并创建链表运算符重载 赋值 默认生成浅拷贝先自己实现一个交换函数调用std::swap函数模板再在赋值函数中复用它库中初始化可以一次性赋多个值如何做到1.首先了解 std::initializer_listvalue_type ilinitializer_listTC11 新增的模板轻量容器类专门接收{ ... }大括号初始化列表value_type模板参数。对于listintvalue_type就是int代表列表里面存放的元素类型il参数名字你可以随便改比如叫initstd::initializer_listvalue_type il用法如下2.该模类同样有成员函数函数作用(constructor)构造函数用来创建initializer_list对象一般编译器自动调用程序员很少手写调用size()返回元素个数il.size()begin()成员版返回指向第一个元素的迭代器const T*end()成员版返回末尾后一位迭代器const T*非成员函数重载begin(initializer_list)end(initializer_list)3.它被当作构造函数的形参配置器不用管该构造函数就是用来支持list{val1,val2,val3}花括号初始化模拟完成可以支持一次性传多个数据的的单参数构造函数有两种用法 结果一样语法过程不一样1️⃣ lt0( {1,2,3,4,5,6} ); 直接构造圆括号初始化{1,2,3,4,5,6}被编译器生成临时std::initializer_listint对象直接作为实参传给构造函数。语法直接初始化 流程直接调用 list 接收initializer_list的构造函数在 lt0 的内存上就地构造对象。2️⃣ lt1 {1,2,3,4,5,6}; 拷贝初始化隐式转换不是赋值运算符 这里不会调用 operator 赋值重载为什么叫隐式转换隐式 编译器偷偷帮你做类型转换代码上你看不见转换的代码没有写任何强制转换语法。你写的源码listint lt1 {1,2,3,4,5,6}表面右边只是一堆数字大括号{1,2,3,4,5,6}它本身并不是 listint类型。编译器在你看不见的地方完成两步转换把{1,2,3,4,5,6} → 生成std::initializer_listint临时对象再拿这个initializer_listint调用 list 的构造函数隐式构造出一个临时的listint对象。你源代码没有写listint({1,2,3,4,5,6})但是编译器自动帮你补上了这就叫隐式类型转换。流程直接调用 list 接收initializer_list的构造函数在 lt0 的内存上就地构造对象。还可以这样用所以还可以这样传函数参数当函数参数为链表时可以如下图最后一行代码一样传参。原因如下图