首页 > 编程知识 正文

c中数组的定义与使用,c语言一维数组的定义方式

时间:2023-05-06 04:07:03 阅读:148820 作者:1551

定义c迭代器的所有STL容器都定义了以下内容:

容器的迭代器类型。 例如,迭代器、const_iterator容器的begin(end )方法不能将这两个容器视为二等公民,无论它们是否为通用容器。 不能与通用算法一起使用来定义迭代器类型和begin,end方法

如果使用嵌套STL容器中的迭代器STL容器作为内部数据成员,则只需将迭代器和两个方法的实现交给STL容器即可。

例如,假设有一个CourseList类,表示学生参与的课程列表,其中使用Student的Vector保存课程列表。 这样的定制容器只需要依赖STL容器Vector。

classcourselist { private 3360 typedefvectorstudentstudentlist; StudentList students; public :类型说明文件列表:3360迭代器; typedefstudentlist :3360 const _ iterator const _ iterator; 迭代器begin ((return students.begin ) ); } iterator end () { return students.end ); (…); 换句话说,我们所做的

将CourseList的迭代器定义为student list :迭代器,将其const_iterator定义为student : const _ iterator。 CourseList的begin(end ) )方法直接返回到其内部容器StudentList的begin(end ) )方法指针以实现迭代器c指针,例如将StudentList作为c数组来实现

classcourselist { private 3360 typedefstudentstudentlist [ 100 ]; StudentList students; 公共:类型支撑*迭代器; typedefconststudent * const _ iterator; interator begin ((return students [0]; } //iterator end () { return students[100]; (//); 要使用现有迭代器定义新的迭代器,可能需要常见容器迭代器的变体,例如可以对迭代器进行计数,或者可以检查迭代器是否在合法范围内

要定义这种特殊的迭代器,通常使用代理模式。 新的迭代器构造函数初始化现有的迭代器和其他所需的信息,并将现有的迭代器用作私有成员变量。 然后,此自定义迭代器的operator operator*添加其他功能,并调用现有迭代器变量的*操作符

定义新容器迭代器的最后一种情况是定义新类型容器的迭代器。

由于STL不使用类的分层结构和继承,因此在定义迭代器时,不能以“迭代器-超类”为基础进行定义。 但是,如果定义了迭代器的部分或全部操作符,则称为迭代器。

的基本迭代器运算符如下:

操作器*操作器操作器!=实现环形队列作为容器的示例,如下所示:

环形队列队列是具有有限大小的队列,其大小不会超过固定大小。 相反,新元素会复盖旧元素。 环形队列保持对数字流中最新值的跟踪,并在添加新元素时自动丢弃旧元素。 例如,环状队列可以用于获得最近n个数字的平均值。

实现这样的环形队列。

RingQueue int,5 ringQueue;//组成5个要素的环形队列像普通队列一样,可以在队伍的末尾压入要素,从队伍的开头压出要素,但有一些不同:

环形队列只保存最近按入的n个元素。 push_back ) )和pop_front ) )而不是push(pop ) )来支持平移算法和back_inserterpush_front ()的功能

myBuffer:N个元素的数组myBegin :标识队列开头的数字mySize :初始情况下,队列中的myBegin为0,即标识数组中第一个元素的数量。 环形队列的最末尾可以通过以下计算得到。

m y E n d=

( m y B e g i n + m y S i z e )   %   N myEnd = (myBegin + mySize) % N myEnd=(myBegin+mySize) % N
可以定义如下的访问器:

front() :myBuffer[myBegin],当mySize!=0时back() :myBuffer[myEnd],当mySize!=0时

改变myBegin和mySize的规则:

pop_front()增加myBegin并减少mySizepush_back() 将值存放到myBuffer[myEnd]并增加mySize直到N为止,此后它仍将值存放到myBuffer[myEnd],并增加myBegin当myBegin到达N时,它将被重设为0;mySize不会超过N,也不会小于0

例:

环形队列的迭代器

从直观来讲,我们想把begin()和end()函数定义为:

begin(): &myBuffer[myBegin]end():&myBuffer[myEnd]

但这么做是有问题的

RingQueue<int, 4> rq;for ( int i = 0; i < 10 ++i ) rq.push_back( i * i );cout << "There are " << rq.size() << " elements." << endl;cout << "Here they are:" << endl;copy( rq.begin(), rq.end(), ostream_iterator<int>( cout, "n" ) );cout << "Done" << endl;

输出:

There are 4 elements.Here they are:Done

并不能正常输出。

注意到当环形队列满的时候,myBegin=myEnd,因此下面这样的代码会失效:

while ( rq.begin() != rq.end() ) ...

一个明显的正确方法是维护一个以myBegin为基准的偏移量:

偏移量=0 意味着在队列的开始偏移量为mySize表示在队尾偏移量介于0和mySize之间的表示在队列中

因此环形队列的迭代器必须包含两个东西:

对环形队列的引用一个记录偏移量的变量

其运算符如下:

operator!=为真当两个迭代器包含着不同的环形队列或有不同的偏移量operator++ 增加偏移量operator* 返回索引为(myBegin+offset)%N的数组元素 一些要注意的C++语法细节 细节1:让容器和迭代器建立联系

容器要和其迭代器建立联系:

在容器的begin() end()方法中,需要构造并返回迭代器在迭代器中,要有一个数据成员是对容器的引用

二者的相互引用意味着我们要在迭代器之前定义容器,并且迭代器常常需要访问容器的私有成员才能完成功能。

出于这些原因,可归纳处定义容器和其迭代器的典型模式:

将迭代器的声明前置定义容器类在容器中将迭代器类声明为友元类定义迭代器类

代码如下:

// 迭代器声明前置 template < class T, int N > class RingIter; // 定义容器,并使其迭代器成为友元 template < class T, int N >class RingQueue { friend class RingIter< T, N >; ...}; // 定义迭代器类 template < class T, int N >class RingIter {...}; 细节2:使容器的迭代器易于使用

某些STL函数,如:back_inserter(),需要一些关于容器的关键信息才能构建迭代器,这些信息由容器类开头的一系列typedef来传达,例如容器类应定义类型value_type和iterator使得其它函数可以知道这个容器可以装入什么类型的对象,以及该容器的迭代器类型是什么

typedef RingIter<T, N> iterator; typedef ptrdiff_t difference_type; typedef size_t size_type; typedef T value_type; typedef T * pointer; typedef T & reference; 细节3:begin()和end()

容器中的主要负责返回迭代器的函数是begin()和end()。通常,它们只是调用迭代器类的构造器,构造器一般最少需要两个参数:

容器自身一个能标识迭代器指向何处的参数 iterator begin() { return iterator( *this, 0 ); } iterator end() { return iterator( *this, mySize ); } 细节4:在迭代器中存储容器的引用 template < class T, int N >class RingIter {private: RingQueue<T, N> & myRingQueue;//对实际容器的引用 int mySize;public: RingIter( RingQueue & rq, int size ) : myRingQueue( rq ), mySize ( size ) //为了能让引用类型的成员变量初始化 {} 细节5:为迭代器定义operator!=()

若迭代器的数据成员只是指针(表示容器)以及int等基本类型,则默认的operator==和operator!=就够用了

细节6:为迭代器定义operator*() *dest = *source;

*dest被称为左值,即能存储其它值的东西。

定义operator*意味着返回一个对某位置的引用,这确保了无论对迭代器的解引用发生在等号的哪一侧都使其是合法的(引用也确保了该对象一定不是NULL的)

T & operator*() { return myRingQueue[ myIncrement ]; } 细节7:为迭代器定义operator++()

自增运算符有两种调用方式,一种是前置,一种是后置:

前置:++iter后置:iter++

前置++ 的实现比较简单,只需要将迭代器中表示位置的变量增加、设为下一位置即可,并返回迭代器本身。

iterator & operator++() { ++myOffset; return *this; }

后置的版本有些令人困惑,原因有二:

如何区分后置++与前置++后置++必须修改迭代器,并且要将修改之前的迭代器返回

第一个问题是将后置++的参数列表设为一个没有实际用处的int

第二个问题需要创建一个修改前的迭代器的拷贝并将其返回

iterator operator++ ( int ) //只能返回值类型。而不能返回引用{ RingIter<T, N> clone( *this );//调用复制构造函数,拷贝一份原迭代器 ++myOffset; //修改当前迭代器 return clone;} 其它C++语法上的细节

const iterator:我们需要定义两类迭代器,一种就是上面的常规迭代器,另一种是const迭代器,它唯一的区别是其成员函数返回的引用都是常引用

typedef RingIter<T, N> iterator;typedef RingCIter<T, N> const_iterator;...iterator begin() { return iterator( *this, 0 ); }iterator begin() const { return const_iterator( *this, 0 ); }

反向迭代器:我们需要定义rbegin()和rend()来返回反向迭代器;并且也需要定义其const版本

Iterator traits:某些复杂的STL算法和适配器通过查看包含了迭代器特质traits的数据结构来确定可以用对容器和其迭代器做哪些事。traits表示诸如:这个迭代器是双向的,指向整数类型的…它是迭代器数据成员的一部分。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。