第四章 向量容器

来源:互联网 发布:mac ps 需要鼠标吗 编辑:程序博客网 时间:2024/06/05 05:45

标准模板库(STL)把他的类组织为3种种类:顺序容器、适配器容器和关联容器。

4.1 标准模板库包含10种容器类。根据元素的次序和访问数据的不同操作类型,标准模板库将这些容器进行分类。STL容器类的3个种类为:顺序容器、适配器容器和关联容器。

顺序容器按照线性次序位置存储数据。

关联容器按键存储元素。程序通过键访问关联容器中的元素,这些键可能与元素在容器中的位置无关。

适配器包含另一个容器作为其基本存储结构。

顺序容器:向量,双端队列,表

适配器容器:栈,队列,优先级队列

关联容器:集合,多重集   、  映射,多重映射

 

向量容器:向量是由数组推广而来的。她存储具有相同数据类型的一组元素。与数组一样的是她允许使用范围0~n-1内的下标访问其元素,其中n为向量大小。然而与数组不同的是,向量中的数据集可以在序列尾部动态增长或收缩。在向量中间位置的插入和删除的操作效率不高。

当程序需要动态序列和通过元素位置直接访问元素时,可选择向量作为数据结构。

表容器:表是按位置存储元素的数据结构。表中 的每项都有一个数值和一个内存地址(指针),内存地址标识表中的下一项。为了访问表中的特定的数据值,必须从表中第一个位置(表头)开始,随着指针从一个元素到下一个元素,直到找到药找的数据值。因此,表不是直接访问结构。

表容器的特点是在任意位置都可以有效地添加和删除数据项。

栈和队列容器:栈和队列都是对元素进入和离开序列方式进行限制的存储结构。栈仅允许对序列的一端访问,这一端为顶部。队列是仅允许访问列头和尾的容器。数据项从队尾进入,从队首离开。

优先级队列容器:优先级队列是一种具有受限访问操作的存储结构,这些操作与栈,队列的操作类似。元素可以以任意顺序进入优先级队列。一旦元素在优先级队列容器中,删除操作将删除最大(最小)的数值。

集合容器:集合是唯一数值的集合,这些唯一的数值称为键或集合成员。

映射容器:映射容器是实现键-值关系的存储结构。映射不按位置存储元素;但是他允许使用键作为下标。程序员可以使用键访问映射中的元素,就像是访问向量或数组元素一样。

4.2 模板类

把模板机制用于容器类,STL在其每个容器类的声明中都是这样做的。这些容器类可以存储整形,字符型,和其他类型。

4.2.1 构造模板类

类的模板版本在类声明之后有一个模板参数表。数据成员可以把模板类型用作对象类型,成员函数可以使用模板类型,成员函数可以使用模板类型作为其参数和返回类型。下面是基于模板的类的一半结构:

template <typename T>

class  templateClass

{

public:

templateClass(const T& item);             //构造函数,带有类型T参数

T f();  //成员函数返回一个类型T数值

void  g(const T&  item);   // 成员函数有一个类型T参数

private:

T dataValue ;

。。。。。。。。。。。。。。。。。

};

模板参数表是声明中的第一项。所有的数据成员和函数都可以引用模板类型T。通常,模板中的参数通过常量引用传递。堆与程序员自定义的类型,这就避免了按值调用时参数传递所需要的复制,而且这种做法也适合用于C++的基本类型。

对于模板类,在使用某个具体类型引用类时,必须包括类名和角括号中的模板类型T。

程序员可以把模板类中的成员函数作为嵌入代码或类主体外的外部函数来实现。嵌入代码的通常格式是把函数体放在类主体中。实现模板类成员函数的外部函数本身就是模板函数其声明必须包含一个模板参数表。为了把函数与类联系起来,函数头把类作用域运算符表达式 className<T>::与函数名放在一起。

模板自由函数必须有一个类型T的参数。因为类型T是类作用域运算符表达式的一部分,所以,成员函数不受这种限制。

注意,模板语法主要用于模板头文件和函数头文件,对函数体中的代码影响不大。

例如:定义一个模板类store,源代码如下:  (一般的模板类的结构)

#ifndef STORE_CLASS
#define STORE_CLASS

#include <iostream>

using namespace std;

template <typename T>
class store
{
    public:
        store(const T& item = T());       // constructor

        // access and update functions
        T getValue() const;              // return value
        void setValue(const T& item);     // update value

        // overloaded operator << as a friend
        friend ostream& operator<< (ostream& ostr, const store<T>& obj)
        {
            ostr << "Value = " << obj.value;
            return ostr;
        }
    private:
        // data stored by the object
        T value;
};

// use an initialization list to assign value
template <typename T>
store<T>::store(const T& item): value(item)
{}

// return the current value
template <typename T>
T store<T>::getValue() const
{
    return value;
}

//   assign the argument as the new data member value
template <typename T>
void store<T>::setValue(const T& item)
{
    value = item;
}

#endif    // STORE_CLASS

 

4.2.2 声明模板类对象

为了声明模板类的对象,编译器需要根据特定的模板参数,创建一个具体的类型。为了做到这样,把类型放在类名后的尖括号里。

templateClass<type>  object(argument list);

这个声明创建了类的实例,其中的type类型用于数据成员和操作。例如,下面的声明创建不同数据类型的store对象:

store<int> intStore(5) ;  //数据值为整形类型

store<char> charStore(‘a’) ;

store<string> strStore(“templates”) ;  //字符串的一个类实例

下面为一个测试的例子:程序首先从对象strstore返回数值“Template”来说明成员函数getValue()和setValue()。然后附加一个字符串“Class”,新的字符串更新strStore中的数据值。

#include <iostream>
#include <string>
#include "d_store.h"

using namespace std;

int main()
{
    //declare three types of store object
    store<int> intsStore(5);
    store<double> realStore(2.718);
    store<string> strStore("Template");

    cout<<"The values stored by the objects are:"<<endl;
    cout<<intsStore<<endl;
    cout<<realStore<<endl;
    cout<<strStore<<endl;

    cout<<endl;

    cout<<"The concatenation of 'template' in strStore and 'Class' is : "<<endl;

    strStore.setValue(strStore.getValue()+"Class");
    cout<<strStore<<endl;

    return 0;

}

4.3 允许动态改变大小,内部存储大小,并允许对象之间的赋值。就是向量容器。

API提供了构造函数和成员函数的原型说明,每个操作的作用以及前提条件和后置条件的注释。利用API,可以在应用程序中使用数据结构,而不必处理类的正式声明。

4.3.1 向量容器入门

向量是基于模板的类。她存储相同数据类型的元素。与数组相同的是她允许用下标运算符直接访问元素,不同的是,她存储其大小信息,且提供相关的size()函数,程序员利用此函数可以访问向量的元素个数。如长度为V.size()的向量,其下标范围是0~V.size()-1 。

声明向量对象:因为向量是类,所以,对象的声明和初始化涉及到使用构造函数。向量类包括3个不同的构造函数,这提供了很大的灵活性。第一个构造函数版本包括两个参数:大小和一个类型为T的初始值。第二个参数默认值为T()。这个构造函数的作用是为向量分配指定大小,并把初始值赋给每个元素。下面的语句声明了一个向量对象,但只使用了size参数。初始值为默认值T()。初始值为0的整形向量,0是整形的默认值。字符串向量中的元素初始值为“”(空字符串)。

vector<int>  intVector(5);  //长度为5的向量,包含整形数值0

vector<string>  strVector(10);  //长度为10的向量;每个元素都包含空字符串

构造函数允许用初始值作为第二个参数,这便在声明中提供 了更大的灵活性。

vector<char>  line(80 , ’’);

向量构造函数的另一个版本是从数组初始化其值。与数组不同的是,在声明向量时,不能用大括号中的初始化表。这项任务必须通过下面的步骤完成:先声明数组,然后把数组地址范围作为参数传递给构造函数。

int intArr[5] = {9,2,4,7,8,6,5,2};

vector<int>  intVector(intArr , intArr + 8 );    向量类的地址集合为半开集合[arr,arr + n) ;

int intArr[5] = {9,2,4,7,8,6,5,2};

int arrSize = sizeof(intArr) / sizeof(int) ;

vector<int>  intVector(intArr , intArr + arrSize );

 

vector::vector
explicit vector(const A& al = A());explicit vector(size_type n, const T& v = T(), const A& al = A());vector(const vector& x);vector(const_iterator first, const_iterator last,    const A& al = A());

All constructors store the allocator object al (or, for the copy constructor, x.get_allocator()) in allocator and initialize the controlled sequence. The first constructor specifies an empty initial controlled sequence. The second constructor specifies a repetition of n elements of value x. The third constructor specifies a copy of the sequence controlled by x. The last constructor specifies the sequence [first, last).

In this implementation, if a translator does not support member template functions, the template:

template<class InIt>    vector(InIt first, InIt last, const A& al = A());

is replaced by:

vector(const_iterator first, const_iterator last,    const A& al = A());

All constructors copy N elements and perform no interim reallocation.

向量类的第三个构造函数,即默认构造函数。在声明中没有包含参数,最终的向量为空,size = 0 ;如果我们不能添加新元素,空向量是没有什么价值的。

添加和删除向量元素

vector::push_back
void push_back(const T& x);

The member function inserts an element with value x at the end of the controlled sequence.向量大小自动加1 。

vector::pop_back
void pop_back();

The member function removes the last element of the controlled sequence, which must be non-empty.吧向量大小减1 。

向量类提供了函数back()来访问和更新尾部元素。对于非常量向量,此函数可以用在赋值语句的任何一边。对于常量向量,back()提供了向量中的最后元素数值的只读访问。下面的声明用数组创建字符向量,5字符vowel作为向量的初始值。

char  vowel[] = {‘a’,’e’,’i’,’o’,’u’};

int vowelSize = sizeof(vowel) / sizeof(char) ;

vector<char>  v(vowel , vowel +vowelSize) ;

cout<<v.back();  //’u’

v.push_back(‘w’);  //在向量末尾添加‘w’

v.back() = ‘y’;  //把尾部的‘w’变为‘y’

 

reference back();const_reference back() const;

The member function returns a reference to the last element of the controlled sequence, which must be non-empty.

改变向量大小

vector::resize
void resize(size_type n, T x = T());

The member function ensures that size() henceforth returns n. If it must lengthen the controlled sequence, it appends elements with value x.

如果新的大小小于当前大小,resize()操作通过砍掉向量尾部的元素来收缩向量。

4.3.2 向量API

vector();创建一个空向量

vector(int n , const T& value = T());创建具有n个元素的向量,每个元素具有指定的数值

vector(T*  first , T*  last);用地址范围[first , last )初始化向量。符号*first和*last是指针表示方法。

T&  back();

const T&  back() const;

bool empty() const ;

T&  operator[](int i);可以取得或修改下标为i的向量元素。后置条件:如果运算符出现在赋值语句的左边,则右边的表达式改变下标引用的元素。

const T&  operator[](int i) const

void push_back(const T& value) ;

void  pop_back();

void resize(int n , const T& fill = T());

int  size() const ;

一般情况下,插入排序在运行时间复杂度为平方级的排序算法中的表现最好。当表已经排序时,插入排序是线性的(O(n))。当表基本上排序时,插入排序依旧是线性的。

原创粉丝点击