smart_stl::deque相关总结

关于deque的一些观点还有我自己的改进,我都是写在这篇博客上,感兴趣的同学可以和我一起讨论;

总结1 deque的operator []

1.在std::deque::operator中,我们可以的得知,cppreference对它的描述为“返回一个引用的元素在指定的位置pos。没有执行边界检查”。
我对“没有执行边界检查”比较感兴趣;因为对它进行一个边界检查很简单,只需要多加一个条件判断就可以了。
但是为这么没有加呢?
我的理解可能是与operator []的使用习惯是相关的,因为我们在使用一个数组的时候,查看某个元素时是这样使用的:

int a[10];
int p = a[20];

C++中时允许这样使用的,虽然我们不知道这个a[20]是一个什么元素,也就是如果使用它就是一个未定义的行为,但是既然它已经存在这么久了,那么deque中的operator []也就“入乡随俗”吧。

总结2 deque的页式管理

今天对于deque有了一个新的认识,是偶然在stackoverflow上看到了Set deque max_size up to maximal size
倒不是问题吸引了我,而是下面的一个人的回答:关于collection的使用,如果你想在你的collection中的任意位置插入元素,那么你适合使用list;而deque是类似一种“page”的方式管理元素。

—————————-我是愉快的分割线(2015/6/23)————————-

总结3 什么时候调用deque(size_type, const value_type&)和deque(InputIterator first, InputIterator last)?

其实这个问题对于序列式容器都是成立的,当大家看到源码的时候会发现无论是vector、list还是deque中,都是有vector_aux、list_aux、deque_aux的,他们是针对两个构造函数:

template<class T, class Alloc>
deque<T, Alloc>::deque(size_type n, const value_type& val)
{
    typedef typename is_Integer<size_type>::class_type is_Int;
    deque_aux(n, val, is_Int());
}

template<class T, class Alloc>
template<class InputIterator>
deque<T, Alloc>::deque(InputIterator first, InputIterator last)
{
    typedef typename is_Integer<InputIterator>::class_type is_Int;
    deque_aux(first, last, is_Int());
}

其中的is_Integer是我自己写的判断第一个数是不是整数,当然整数的范围包括unsigned int,int,long,long long等;
大家可以看到我在这两个函数中具加入了deque_aux这个函数,其实是为了解决一个问题:
当我们调用构造函数的时候:deque(10,22);时,会调用哪个构造函数?
deque<T, Alloc>::deque(InputIterator first, InputIterator last),是不是与我们之前打算的完全不一样呢?
下面我来解释一下为什么会调用这个构造函数;
在《C++ Primer》中,我们知道精确匹配>类型提升>标准转换>类类型转换,这样在我们调用执行语句deque(10,22);的时候,首先会找function template,第二个函数当InputIterator为int时,函数就是deque(int first, int last);
第一个函数则是deque(size_t n, const T& val);
这是我们就会发现第二个函数时精确匹配,就会调用第二个函数,当然,这个10和22并不是迭代器,那么我们在后续的使用会用到iterator_traits或者type_traits,这样肯定会出错,应为在使用iterator_traits时,里面的的类型不能是内置类型。也就是下面这个vs2008编译时显示的错误:
deque(10,22)调用deque(InputIterator first, InputIterator last)产生的错误

在上面的图片中我们可以看到第一个错误,“‘Iterator’:当后面跟“::”时必须为类或命名空间”。因为InputIterator被匹配成了int,而int不是类而是内置类型,所以产生了这样的错误(MarkDown是真的好用,就是贴图的时候有点麻烦,上传体验不好,可以赶紧一下^^)。

总结4 const对象的数据成员自动具有const属性

在写deque的复制构造函数时,我遇到了比较大的问题,我是这么写deque的构造函数的:

template<class T, class Alloc>
deque<T, Alloc>::deque(const deque& deq)
{
    //第一种方法:distance_type new_sz = deq.finish_ - deq.start_;
    //第二种方法:distance_type new_sz = finish_ - start_;
    //第三种方法:distance_type new_sz = deq.size();
    create_map_and_nodes(new_sz);
    iterator temp = start_;
    uninitialized_copy(deq.start_, deq.finish_, start_);
}

(注:注释的三个语句都是我分别使用的,我会分别介绍他们)
当我用distance_type new_sz = deq.finish_ - deq.start_;的时候编译器给出了如下的错误:

错误用法1

我们可以看到错误描述:“没有找到接受“const smart_stl :: deque_iterator<T, Ref, Ptr>”类型的左操作数的运算符(或没有可接受的转换)”
如果看了我的smart_stl的代码,可以发现我在deque_iterator中重载了operator-,而左操作数就是*this,但是这里为什么不可以了呢?

因为deq.finish_不是deque_iterator<T, Ref, Ptr>& 类型,它是const deque_iterator<T, Ref, Ptr>&类型

这是因为deq的是const型的,那么它的成员函数start_也具有const属性,当然不符合operator-的左操作数的要求。
如果我们采用第三种方法呢?此时会产生错误信息error C2662: “smart_stl::deque<T>::size”: 不能将“this”指针从“const smart_stl::deque<T>”转换为“smart_stl::deque<T> &”因为deque是const类型了,即const smart_stl::deque<T>所以我们在使用const类型对象的时候一定要小心。

—————————–新一天的分割线(2015/6/29)————————–

总结5 deque的中控区的相关阐述

如果你也看过deque中关于insert的相关源码,也许你也会发疯,一个函数嵌套着一个函数,其实如果你对map的改变熟悉的话(也就是reallocate_map)的话,那么关于insert的相关函数,你就会理解快一点;否则你将感受不到stl的快乐了。
那么下面进入正题:reallocate_map的作用;
reallocate_map并不是要进行缓冲区内存的分配,因为这样会使得该函数与其他的相关函数耦合性增大,这样到后面的使用会发现越来越乱;reallocate_map的作用其实只有一个:修改中控区的布局!!!


| | | |O |O |O |O |O | | | | |

我们假定上图中是map中控区的现状,O表示该中控点上有对应的缓冲区,那么如果我们insert一个缓冲区的元素的话,那么肯定要重新改变中控区的布局位置。
就会变成如下的样子:


| | | |X |O |O |O |O |O | | | |

其中“X”表示新增的点,所以可以看到我们只是通过reallocate_map改变了中控区中的中控点,并没有做与缓冲区有关的任何操作,这样的低耦合性使得思路清晰。
并且最重要的就当修改中控区布局的时候,同时修改了start_、finish_、map和mapsize,使得使用这个函数的用户感觉到似乎reallocate_map什么都没做;

这里需要再说一点,对于start_和finish_这两个成员变量,在reallocate_map中只对start_和finish_改变着他们的node,没有改变他们的cur;这本来就不用改变,因为start_和finish_始终指向的缓冲区是没有变的,只是他们指向的中控区改变了!

——————————忧郁的分割线(2015/6/30)————————–

总结6 时刻保持对size_type和distance_type的警惕

到现在我才发现,我将ptrdiff_t定义成了distance_type,而源码貌似定义成了difference_type,这没有什么关系其实,不过我觉得这样定义更好~
我要说的也是这个,当我今天写完insert的时候,调试时发现了很多有符号和无符号不匹配的问题,这是因为我进场拿distance_type和size_type进行比较,这样是一个非常大的漏洞,因为一个是unsigned 一个是long,所以时刻要保持对这两个类型的变量比较时的细心。
还有,马上就要找工作了,可能一天只能写一两个小时的smart_stl了,争取在校招前全部搞定吧

——————————思路清晰的分割线(2015/7/6)————————
这几天没事的时候看了看韩国的职场连续剧《未生》,从中学到了很多,一个人无论什么时候都要保持清晰的思路,“我要让自己知道,我是因为不够努力才被世界抛弃的”多少有点伤感,却让你感觉到当人生走向下坡时有一种强大的反弹力…说多了,我们还是回到stl中吧。

总结7 尽量避免friend函数

这个小点中,我们并不是讲deque,而是queue,因为queue的Sequence就是deque,单独写一个文章太浪费纸张了,并且我说过这个是总结,总结太少也不好,所以就写到这个文章中;
如果你读过queue或者stack的源码,你会发现关于操作符的重载,只有将==和<设置成了friend函数,而其他的操作符重载均是以下这种:

template<class T, class Sequence>
bool operator != (const queue<T, Sequence>& lhs, const queue<T, Sequence>& rhs)
{
    return !(lhs.c == rhs.c);
}

template<class T, class Sequence>
bool operator >= (const queue<T, Sequence>& lhs, const queue<T, Sequence>& rhs)
{
    return !(lhs.c < rhs.c);
}

template<class T, class Sequence>
bool operator > (const queue<T, Sequence>& lhs, const queue<T, Sequence>& rhs)
{
    return rhs.c < lhs.c;
}

template<class T, class Sequence>
bool operator <= (const queue<T, Sequence>& lhs, const queue<T, Sequence>& rhs)
{
    return !(lhs.c > rhs.c);
}

这几个函数均不是friend函数,而是non-member函数,为什么呢?为什么不将这些函数也设置成friend函数呢,在《effective C++》中有相关的阐述我觉得比较好,copy到此:

“member函数的反面是non-member函数,而不是friend函数。太多的程序员假设,如果一个“与某class相关”的函数不该称为member(由于其所有实参可能都需要类型转换),就该是个friend。本例表明这样的理由过于牵强。无论何时如果你可以避免friend函数就该避免,因为像真实的世界一样,朋友带来的麻烦往往多于其价值(注:最后一句话我还是不能同意的==)”

其实我觉得从整体考虑的话,应该是尽量提高类型的封装性,因为friend是可以访问class中的private成员,这样越少可以访问私有成员的函数约束,封装性越好;封装性越好,这个类型的可扩展性就强,因为很少的函数知道private member的存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值