模仿std::vector写线性表的几点感想

数据结构还是很早之前学的了,当时才刚学过C语言,实现得都很简单,最近决定重新打牢基础,于是重新开始实现书上的数据结构和算法。

模仿C++ Primer的StrVec以及std::vector,使用模板类+allocater分配内存,遵循“三/五原则”,期间遇到了几个小问题,记录下。

1、unsigned和signed的比较:

在实现insert操作时,写了个私有方法去把数组index处开始的元素向后移动n位。分两种情况处理,一种是容量不够需要重新分配内存,另一种是容量足够,直接向后移动。

前一种分配内存空间后可以直接用uninitialized_copy搬运元素,也可以用Allocater::construct构造元素。而后一种是在原先分配的内存上重新构造元素,因此要注意先更新的位置就不要再使用。

 

如上图,把index为1开始的元素向后移动1位。如果从前往后移动,第一次就会改动位置2处的元素,但是位置2处的元素需要移动到位置3处,这样就造成了错误。

因此需要从后移动,我之前的代码就是  for (size_t i = count - 1; i >= index; --i)

乍看之下没有问题,但是在index为0的时候就出错了。size_t是unsigned int,假如是为0再自减,会变成unsigned包含的最大的数,具体原因我之前写过一篇博客说过。

C语言的补码表示和unsigned及signed的转换

所以我把size_t改成了int,代码变成  for (int i = count - 1; i >= index; --i)

但是依旧出错,调试发现:int i = -1; unsigned index = 0; 那么i > index为true。其实编译器已经把问题提出来了:warning c4018 : signed/unsigned mismatch
signed和unsigned进行比较时会都转换成unsigned比较,也就是说虽然i自减成了负数,但是进行比较时会被强制转换为unsigned,这个循环依旧是个死循环。

解决方法:

1、把index强制转换为int,这样就会是signed之间比较;

2、改成for (int i = 0; i < count - index; ++i)然后改变循环体内关于i的部分,这样比较麻烦,而且i不能直接体现出表达的意义。

2、类模板中定义友元函数

如果友元函数的定义在类中则没有什么问题,如果要写在类的外部就有点麻烦。以下面代码为例。

template <typename T>
class SqList;

template <typename T>
std::ostream &operator<<(std::ostream &, const SqList<T> &);

template <typename T>
class SqList
{
    friend std::ostream &operator<< <T>(std::ostream &, const SqList<T> &);
     // ...   
}

template <typename T>
std::ostream &operator<< (std::ostream &os, const SqList<T> &lst)
{
    for (auto iter = lst.begin(); iter != lst.end(); ++iter)
        os << *iter << ' ';
    return os;
}

首先,友元函数需要在类之前进行声明,这点跟普通类的友元函数不同(具体原因需要复习下模板的知识,暂不深究)。而由于友元函数会用到类模板作为参数,所以必须再对类模板进行前置声明。

其次,模板类的友元函数本质上是个函数模板,因此在类模板中进行友元声明时在函数名后用模板参数实例化,否则就会出现经典的外部链接错误error LINK 2019,因为会判断类外部的函数实现(operator<<)并不是对类内的operator<<的实现,所以在调用这个友元方法时编译器找不到定义。

 

posted @ 2016-06-06 12:07  Harley_Quinn  阅读(525)  评论(0编辑  收藏  举报