C++重学之路 7 类模板array和vector、异常捕获

1 array对象

array对象是一组具有相同类型的连续的内存区域。要引用array对象中的一个特定区域或元素,需通过指定array对象名称和该特定元素在array对象中的位置编号(position number)来完成。

位置编号更正规的叫法是下标(subscript)或索引(index)。每个array对象中第一个元素下标都为0。

下标必须是一个整数或整数表达式(使用任何整数类型)。若程序以一个表达式作为下标,那么程序要计算这个表达式以确定下标。如:

c[a+b] += 2;

一个带下标的array对象名是一个左值——它和其他非array对象的变量名一样,可以在赋值语句左边使用。

array对象下标括起来的方括号实际上是C++中的一个运算符,它和圆括号具有相同的优先级。

2 array对象的声明

array对象的声明语法规范为:

array<类型,大小> 对象名;

表示法<类型,大小>表明array是一个类模板。编译器将根据元素的类型和array对象的大小来分配合适的内存空间。如:

array<int,12> c;

array对象的大小必须是个无符号整型,但大多数数据类型都可以作为其元素的值的类型。如一个string类型的array对象能用来存储字符串。

3 使用array对象的例子

下面给出一些例子说明如何声明array对象,如何初始化对象,以及如何进行一些常见的array对象的操作。

3.1 声明array对象以及用循环来初始化array对象的元素

#include<iostream>
#include<iomanip>
#include<array>
using namespace std;


int main() 
{
    array<int,5> n;

    for (size_t i = 0; i < n.size(); ++i) 
    {
        n[i] = 0;
    }

    cout << "Element" << setw(13) << "Value" << endl;

    for (size_t j = 0; j < 10; ++j) 
    {
        cout << setw(7) << j << setw(13) << n[j] << endl;
    }
}

和其他自动变量一样,自动的array对象不会隐式地初始化为0,尽管static的array对象是这样的。

控制变量i和j指定了array对象的下标,它们的类型被声明为size_t。根据C++标准,size_t表示的是一种无符号的整型,建议任何表示array对象大小和对象下标的变量采用这个类型。

类型size_t在std命名空间中定义并在头文件<cstddef>中,被其他各种各样的头文件所包含。在编译使用了size_t类型的程序时,若产生它未定义的错误,那么只需要将<cstddef>头文件包含到该程序中即可。

3.2 在声明中用初始化列表初始化array对象

在array对象的声明中,也可以对其元素进行初始化。例如:

array<int,5> n = {1,2,3,4,5};

如果初始化值的个数少于array对象元素的个数,那么剩下的array对象元素都被初始化为0。如:

array<int,5> n = {};

如果初始化值的个数大于array对象元素的个数,会导致一个编译错误。

3.3 用常量变量指定array对象的大小

const size_t arraySize = 5;
array<int,arraySize> s;

上面使用const限定符声明一个所谓的常量变量arraySize,其值为5。这个常量变量用于指定array对象的大小,必须在声明时用一个常量表达式来初始化,而且之后不能再修改。常量变量也称为命名常量或只读常量。

int main()
{
    const int x; //Error: x没被初始化
    x = 7;  //Error: 不可以修改一个常量变量
}

3.4 array对象下标的边界检查

在使用[] 运算符访问array对象的元素时,C++并没有提供自动对array对象边界检查的机制,来防止程序员引用一个不存在的元素。因此,一个执行中的程序可以“离开”array对象的任何一端而不会产生警告。这是一个执行时的逻辑错误,并不是语法错误。

3.5 静态局部array对象和自动局部array对象

static应用于局部array对象的声明,使得array对象不会在每次程序调用该函数时都进行创建和初始化,也不会在每次该函数结束时被销毁。这样可以提高性能,特别是在使用大型array对象时。

例如:

void staticArrayInit(void)
{
    static array<int,arraySize> array1;  
}

3.6 基于范围的for语句

基于范围的for语句可以不适用计时器就完成所有元素的遍历,从而不需要自己实现对边界的检查功能。

语法格式为:

for(范围变量声明: 表达式)
   语句

其中范围变量声明含有一个类型名称和一个标识符(例如 int item),表达式是需要迭代遍历的array对象。范围变量声明的类型必须和array对象的元素类型一致。

例如:

array<int,5> items = {1,2,3,4,5};
for(int item: items)
   cout<<item<<" ";

第2、3行等价于:

for(int counter = 0;counter < items.size();++counter)
   cout<<items[counter]<<" ";

使用基于范围的for语句不需要访问元素的下标。

4 array对象的排序和查找

排序

对数据排序,即按照某种特定的次序(升序或降序)对数据进行排列,是最重要的计算应用之一。

查找

常常可能需要判断一个array对象是否有与某个关键值相匹配的值。在一个array对象中发现一个特定元素的过程称为查找。

示范说明

#include <iostream>
#include<iomanip>
#include <array>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
	const size_t arraySize = 7;
	array<string, arraySize> colors = { "red","orange","yellow","green","blue","indigo","violet" };
	cout << "Unsorted array:\n";
	for (string color : colors)
		cout << color << " ";

	sort(colors.begin(), colors.end());

	cout << "\nSorted array:\n";
	for (string item : colors)
		cout << item << " ";

	bool found = binary_search(colors.begin(), colors.end(), "indigo");
	cout << "\n\n\"indigo\" " << (found ? "was" : "was not")
		<< " found in colors" << endl;

	found = binary_search(colors.begin(), colors.end(), "cyan");
	cout << "\"cyan\" " << (found ? "was" : "was not")
		<< " found in colors" << endl;
}

运行结果为:

Unsorted array:
red orange yellow green blue indigo violet
Sorted array:
blue green indigo orange red violet yellow

"indigo" was found in colors
"cyan" was not found in colors

上面例子中,先创建了一个未排序的array对象,然后使用C++标准库函数sort对array对象colors按升序进行排序。sort函数的实参制定了需要排序的元素的范围。

后用binary_search函数确定一个值是否在array对象中,首先值的序列必须是已按升序排好序的,而且binary_search函数不会核实这件事。

5 多维array对象

可以用两个维度(即下标)的array对象表示数值表格,这样的数值表格包含的信息是按照行和列排列的。

按照惯例,第一个下标表示元素的行,第二个下标表示元素的列。其中每个元素都用a[i][j] 形式的元素名称来标识。

TIPS:

使用a[x,y] 不正确地引用一个二维array对象元素a[x][y] 是一个错误。事实上,由于C++计算表达式x,y 的值得到的是y(逗号分隔的表达式的最后一项),所以a[x,y]被认为是a[y]

#include <iostream>
#include <array>
using namespace std;

const size_t rows = 2;
const size_t columns = 3;
void printArray(const array<array<int, columns>, rows>&);
int main()
{
	array<array<int, columns>, rows> array1 = { 1,2,3,4,5,6 };
	array<array<int, columns>, rows> array2 = { 1,2,3,4,5 };

	cout << "Values in array1 by row are:" << endl;
	printArray(array1);

	cout << "\nValues in array2 by row are:" << endl;
	printArray(array2);
}

void printArray(const array<array<int, columns>, rows> & a)
{
	for (auto const& row : a)
	{
		for (auto const& element : row)
		{
			cout << element << " ";
		}
		cout << endl;
	}
}

执行结果是:

Values in array1 by row are:
1 2 3
4 5 6

Values in array2 by row are:
1 2 3
4 5 0

嵌套的array对象的类型声明中,每个array对象其元素的类型被指定为:

array<int,columns>

即每个array对象都包含两个元素,每个元素又是一个含3个int元素的一维array对象。

为处理二维array对象的元素,我们采用一个嵌套的基于范围的for语句。

auto关键字告诉编译器根据这个变量的初始化值来确定它的数据类型。

6 C++标准库类模板vector的介绍

C++标准库类模板vector与类模板array类似,而且支持动态的大小调整。标准类模板vector在头文件<vector>中定义,并且属于命名空间std。

创建vector对象

#inlcude<iostream>
#include<vector>
int main()
{
	vector<int> integers1(7);
}

上面创建了一个存储int类型值的vector对象,其中integers1包含7个元素。在默认情况下,每个vector对象的所有元素都被设置为0。

array对象一样,可以定义vector对象来存储大多数数据类型的数据,只需要把vector<int>中的int替换成适当的数据类型即可。

vector成员函数size

可以使用vector的成员函数size来获得integers1的大小(即元素个数):

integers1.size()

vector对象比较

if (integers1 == integers2)
{
	cout << "integers1 and integers2 are equal" << endl;
}

一个vector对象可以用==!= 运算符直接与另一个vector对象进行比较。

一个vector对象用另一个vector对象来初始化及赋值

C++标准库类模板vector允许再创建一个新的vector对象时,用一个已有的vector对象的内容来初始化它。

vector<int> integers3(integers1);

这里创建一个vector对象integers3,并用integers1的一个副本初始化它,此举会调用vector的拷贝构造函数,执行复制操作。

integers1 = integers2;

vector对象间可以使用赋值运算符(=)。

使用运算符[]访问vector对象的元素

array对象一样,使用方括号访问vector对象的元素时,C++不需要将进行边界检查。

标准类模板vector在它的成员函数at中提供了边界检查的能力(类模板array也是这么做的)。

异常处理:处理超出边界的下标

异常处理能够创建可以解决异常的容错程序。很多情况下,在处理异常的同时还允许程序继续运行,就像没遇到异常一样。

若函数检测到一个问题,例如一个无效的array对象下标或一个无效的实参,它抛出一个异常,也就是一个异常发生了。

try-catch语句

为了处理一个异常,需要将可能抛出一个异常的任何代码放置在一个try语句中。catch语句包含了如果异常发生则处理异常的代码。

try
{
	cout << "\nAttempt to display integers1.at(15)" << endl;
	cout << integers1.at(15) << endl;
}
catch (out_of_range& ex)
{
	cerr << "An exception occured: " << ex.what() << endl;
}

其中vector成员函数at提供了边界检查和抛出异常的功能,即这个函数的实参是一个无效的下标时,就会抛出一个异常。在默认情况下将导致程序终止。如果下标有效,at函数返回在此指定位置上的元素。

当程序以实参15调用vector成员函数at时(第4行),这个函数试图访问在位置15上的元素,超出了vector对象integers1的边界。此刻,integers1只有10个元素。因为边界检查是在执行期间进行的,因此 vector成员函数at产生一个异常。具体地说,就是第4行抛出一个out_of_range异常(在头文件<stdexcept>中),通知程序这个问题。在这个时候,try语句块立即终止,同时catch语句块开始执行。请注意,在try语句块中声明的任何变量此时都超出了它们的作用域,也就是说在catch语句块中都不可访问。

这个catch语句块声明了一个类型(out_of_range)和一个接收引用的异常形参(ex)。

异常形参的what成员函数

当第6 ∼ \sim 9行捕捉到异常时,程序显示一则消息说明出现的问题。第8行调用这个异常对象的what成员函数,来得到并显示存储在此异常对象中的错误消息。在这个例子中,一旦消息被显示,这个异常就视为被处理了,然后程序继续执行catch语句块结束的右花括号之后的语句。

改变vector对象的大小

vector对象和array对象之间的一个主要区别是vector对象可以动态增长以容纳更多的元素。

integer3.push_back(1000);

这里调用vector对象的push_back成员函数,在这个对象尾部加一个新的元素,这个元素的值是1000,integers3的大小也会增加1。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lum0s!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值