《C++大学教程》 第7章 类模板array和vector、异常捕获 笔记

0. 前言

《C++大学教程》 第7章 笔记更一下,附部分课后题代码(带注释)。

7. 类模板array和vector、异常捕获

7.1 简介

array对象:由相同类型数据项组成的固定大小的数据集合
vector对象:由相同类型数据项组成的数据集合,但其大小在程序执行期间可以动态增长和收缩
为了使用它们,必须分别包含头文件<array><vector>

7.2 array对象

位置编号更正规的叫法是下标(subscript)索引(index)

7.3 array对象的声明

为了指定array对象所需要的元素类型和元素个数,应该采用如下的声明方式:

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

array对象的大小必须是无符号整型

7.4 使用array对象的例子

按引用传递的方式把array对象传递给函数,使用const限定符来防止被调用函数修改该array对象元素

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

自动的array对象不会隐式地初始化为0,尽管static的array对象是这样的。
经我人为测试,无符号整型array对象当不初始化时的初始赋值为-858993460.

根据C++标准,size_t表示的是一种无符号的整数类型。在此我们建议任何表示array对象大小和array对象下标的变量可以采用这个类型。
在编译使用了size_t类型的程序时,如果产生它未定义的错误,那么只需将<cstddef>头文件包含到该程序中即可。

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

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

7.4.3 用常量变量指定array对象的大小并用计算结果设置array对象元素

int main()
{
   
   const size_t arraySize = 5; 
   array< int, arraySize > s; 
 
   ... ...

}
   

使用const限定符声明一个所谓的常量变量arraySize,其值为5。常量变量也称为命名变量只读变量

在声明常量变量时没有初始化是一个编译错误
在可执行语句中对常量变量进行赋值是一个编译错误

使用常值变量使你能够为字面上的常量提供一个名字,从而帮助你解释某个值在程序中的用途

7.4.6 把array对象元素当作计数器使用

该程序使用array对象跟踪一个骰子各个面出现的次数,同时也利用了C++ 11 中心的随机数生成功能。

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

int main()
{
   // use the default random-number generation engine to
   // produce uniformly distributed pseudorandom int values from 1 to 6
   default_random_engine engine( static_cast< unsigned int >( time(0) ) );
   uniform_int_distribution< unsigned int > randomInt( 1, 6 ); 

   const size_t arraySize = 7; // ignore element zero
   array< unsigned int, arraySize > frequency = {}; // initialize to 0s

   // roll die 6,000,000 times; use die value as frequency index
   for ( unsigned int roll = 1; roll <= 6000000; ++roll )       
      ++frequency[ randomInt( engine ) ];

   cout << "Face" << setw( 13 ) << "Frequency" << endl;

   // output each array element's value
   for ( size_t face = 1; face < frequency.size(); ++face )  
      cout << setw( 4 ) << face << setw( 13 ) << frequency[ face ] 
         << endl;
} // end main

7.4.7 把array对象来汇总调查结果

array对象下标的边界检查

允许程序在array对象边界之外读写array对象元素是常见的安全漏洞
从array对象边界外读数据可以造成程序崩溃,甚至有可能出现使用错误的数据而程序正确执行的情况。
向array对象边界外元素写数据(被视为缓冲区溢出)可以破坏内存中的程序数据,使程序崩溃

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

可以将static应用于局部array对象的声明,使得array对象不会在每次程序调用该函数时都进行创建和初始化,也不会在每次该函数结束时被销毁

7.5 基于范围的for语句

基于范围的for语句的语法形式是:

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

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

使用元素的下标

无论何时只要循环遍历一个array对象的代码不要求访问元素的下标,就可以用基于范围的for语句来代替计数器控制的for语句。

7.7 array对象的排序与查找

#include <iostream>
#include <iomanip>
#include <array>
#include <string>
#include <algorithm> // contains sort and binary_search
using namespace std;

int main()
{
	const size_t arraySize = 7; // size of array colors
	array< string, arraySize > colors = { "red", "orange", "yellow",
	   "green", "blue", "indigo", "violet" };

	// output original array
	cout << "Unsorted array:\n";
	for (string color : colors)
		cout << color << " ";

	sort(colors.begin(), colors.end()); // sort contents of colors

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

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

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

	system("pause");
} 

sort对array对象的元素升序排序,sort函数的实参指定了需要被排序的元素的范围

利用binary_search函数确定一个值是否在array对象中binary_search函数的前两个形参表示查找元素的范围,第三个实参是查找关键字

7.8 多维array对象

array< array< int, columns >, rows > array1 = { 1, 2, 3, 4, 5, 6 };
void printArray(const array< array< int, columns >, rows> & a)
{
	// loop through array's rows
	for (auto const &row : a)
	{
		// loop through columns of current row
		for (auto const &element : row)
			cout << element << ' ';

		cout << endl; // start new line of output
	} // end outer for


} // end function printArray

嵌套的基于范围的for语句

外层循环迭代遍历行,而内层循环迭代遍历一个给定行的列。
auto关键字能通知编译器根据变量的初始值来确定它的数据类型

嵌套的计数器控制的for语句

for (size_t row = 0; row < a.size(); ++row)
{
	for (size_t column = 0; column < a[row].size(); ++column)
		... ...	
} 

7.9 实例研究:利用二维array对象的GradeBook类

GradeBook类的头文件

#include <string>
#include <array>

class GradeBook
{
public:
	static const size_t students = 10;
	static const size_t tests = 3;

	GradeBook(const std::string &, const std::array<std::array<int,tests>, students> &);

	void setCourseName(const std::string &);
	std::string getCourseName() const;
	void displayMessage() const;
	void processGrades() const;
	int getMinimum() const;
	int getMaximum() const;
	double getAverage(const std::array< int, tests > &) const;
	void outputBarChart() const;
	void outputGrades() const;

private:
	std::string courseName;
	std::array< std::array< int, tests>, students> grades;
};

GradeBook成员函数的源代码文件(部分)

#include <iostream>
#include <iomanip>
#include "GradeBook.h"

using namespace std;

GradeBook::GradeBook(const string &name,
	const array< std::array< int, tests>, students> &gradesArray)
	:courseName(name), grades(gradesArray)
{
	
}

... ...

double GradeBook::getAverage(const std::array< int, tests > &setOfGrades ) const
{
	
	int total = 0;
	for ( int grade : setOfGrades )
	{
		total += grade;
	}

	return static_cast<double> (total) / setOfGrades.size();

}
void GradeBook::outputGrades() const
{
	... ...

	for (rsize_t student = 0; student < grades.size(); ++student)
	{
		... ...
		double average = getAverage(grades[student]);
		... ...
	}		
}
... ...

部分代码解释

  1. 将课程名和存储学生成绩的array对象传入GradeBook类的构造函数,初始化courseNamegrades
    2. GradeBook::getAverage求得每位学生三次测试成绩的均值,total为总和,setOfGrades.size()为测试次数。在GradeBook::outputGrades()中对10名同学循环求均值。

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

标准类模板vector在头文件<vector>中定义,并且属于命名空间std

在默认情况下,每一个vector对象的所有元素都被设置为0。

// Fig. 7.25: fig07_25.cpp
// Demonstrating C++ Standard Library class template vector.
#include <iostream>
#include <iomanip>
#include <vector>
#include <stdexcept> // for out_of_range exception class
using namespace std;

void outputVector( const vector< int > & ); // display the vector
void inputVector( vector< int > & ); // input values into the vector

int main()
{
   vector< int > integers1( 7 ); // 7-element vector< int >
   vector< int > integers2( 10 ); // 10-element vector< int >
   ... ...
  
   // input and print integers1 and integers2
   /*cout << "\nEnter 17 integers:" << endl;
   inputVector( integers1 );
   inputVector( integers2 );*/
   integers1 = { 1, 2, 3, 4, 5, 6, 7 };
   integers2 = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
   ... ...
   
   // create vector integers3 using integers1 as an
   // initializer; print size and contents
   vector< int > integers3( integers1 ); // copy constructor

   ... ...

   // use assignment (=) operator with vector objects
   integers1 = integers2; // assign integers2 to integers1
 
   ... ...
   
   // use square brackets to create lvalue
   integers1[ 5 ] = 1000;
   cout << "integers1:" << endl;
   outputVector( integers1 );

   // attempt to use out-of-range subscript
   try
   {
      cout << "\nAttempt to display integers1.at( 5 )" << endl;
      cout << integers1.at( 5 ) << endl; // ERROR: out of range
   } // end try
   catch ( out_of_range &ex )
   {
      cerr << "An exception occurred: " << ex.what() << endl;
   } // end catch

   
   // changing the size of a vector
   cout << "\nCurrent integers3 size is: " << integers3.size() << endl;
   integers3.push_back( 1000 ); // add 1000 to the end of the vector
   cout << "New integers3 size is: " << integers3.size() << endl;
   cout << "integers3 now contains: ";
   outputVector( integers3 );
} // end main

// output vector contents
void outputVector( const vector< int > &items )
{
   for ( int item : items )
      cout << item << " ";

   cout << endl;
} // end function outputVector

// input vector contents
void inputVector( vector< int > &items )
{
   for ( int &item : items )
      cin >> item;
} // end function inputVector

C++标准库类模板vector允许程序员在创建一个新的vector对象时,用一个已有的vector对象的内容来拷贝它。

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

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

vector成员函数at提供了边界检查和抛出异常的功能,也就是说如果这个函数的实参是一个无效的下标,就会抛出一个异常。在默认的情况下,将导致C++程序终止。

因为边界检查是在执行期间进行的,因此vector成员函数at抛出一个out_of_range异常(在头文件<stdexcept>中),通知程序这个问题。
在这个时候,try语句块立即终止,同时catch语句块开始执行

catch 块声明了一个类型( out_of_range)和一个接收引用的异常形参( ex )。
异常对象的what成员函数,可以得到存储在此异常对象中的错误信息。
注:
当出现异常时,此程序会显示:

An exception occurred: invalid vector<T> subscript

catch 语句块可以处理指定类型的异常。

vector对象的push_back成员函数,能够在对象的尾部增加一个新的元素。

C++ 11允许vector对象(以及其他的C++标准库数据结构)用初始化列表进行初始化。

练习题

7.13 利用array对象对重

测试程序:

#include <iostream>
#include <array>

using namespace std;

int main()
{
	// array对象初始化
	array< double, 5 > store = {};

	// 确定array对象初始化为0
	/*for (int i = 0; i < 5; ++i)
	{
		cout << store[i];
	}*/

	// 引导用户输入
	cout << "Input 5 double numbers(10—100):" << endl;

	// 初始化不相同数的个数
	int index = 0;

	// 5次循环输入
	for (int i = 0; i < 5; ++i)
	{
		// 初始化读入数据、标志位
		int number = 0;
		int flag = 0;

		// 对读入数据进行判断
		cin >> number;
		for (int j = 0; j < i; ++j)
			if (number == store[j])
			{
				flag = 1;
				break;
			}

		// 如果不重复,则存入array对象
		if (flag == 0)
		{
			store[index] = number;
			++index;
		}			
	}

	// 输出显示
	for (int i = 0; i < index; ++i)
	{
		int number = 0;
		cout << store[i] << endl;
	}

	// 暂停
	system("pause");
}

7.28 回文

测试程序:

#include <iostream>
#include <string>
#include <array>

using namespace std;

bool testPalindrome(string source)
{
	// 定义array对象大小,并初始化
	static const size_t Size = 1000;
	array< char, Size> a = {};
	array< char, Size> b = {};

	// 读取传函source的大小
	int num = source.size();
	//cout << num << endl;
	
	// 分别读取正序a和倒序b
	for (int i = 0; i < num; ++i)
	{
		a[i] = source[i];
		b[num - 1 - i] = source[i];
	}

	/*
	for (int i = 0; i < num; ++i)
	{
		cout << a[i] << endl;
	}
	for (int i = 0; i < num; ++i)
	{
		cout << b[i] << endl;
	}*/

	
	// 判断
	bool flag = false;
	if (a == b)
		flag = true;
	return flag;
}

int main()
{
	//读出字符串
	string test;
	cout << "Please enter the string:" << endl;
	getline(cin, test);
	//cout << test;

	// 判断并输出
	bool flag;
	flag = testPalindrome(test);
	cout << boolalpha << flag;

	// 暂停
	system("pause");
}

7.30 打印array对象

测试程序:

#include <iostream>
#include <array>

using namespace std;
static const size_t Size = 100;

int printArray(const array<int, Size> &num,int num1,int num2)
{
	for (int i = num1; i < num2; ++i)
	{
		cout << num[i]<<endl;
	}
	return 0;
}

int main()
{
	// 初始化array对象
	const array<int, Size> numbers = {11, 22, 33, 44, 55, 66, 77, 88, 99};
	
	//读取开始下标和结束下标
	int num1 = 0;
	int num2 = 0;
	cout << "Please enter the start subscript and the end subscript:" << endl;
	cin>> num1 >> num2;
	//cout <<num1 <<endl<<num2;

	// 打印处理
	if(num1 < num2)
		printArray(numbers, num1, num2);
	

	// 暂停
	system("pause");
}

结语

第七章在学校就已看完,但是临放假比较忙。作为年前的第十篇,也没有完成上一年的任务,唉~

在家躺了这么久,今个终于更完这一篇。

附上的自己敲的代码,第一次加上了注释。但这几道题并不难理解,靠着本科C的基础即可完成。

所有问题,都是数学逻辑问题,编程并不是最难的。

最近在思考,作为一个非机专业出身的人,编程到底要学多少,学到什么程度,才能算一个合格的控制硕士? 是只需要仿真数据处理? 还是别的什么? 求解答疑惑……

新年第一更,大家过年好~

个人水平有限,有问题欢迎各位大神批评指正!

发布了24 篇原创文章 · 获赞 15 · 访问量 5370

猜你喜欢

转载自blog.csdn.net/qq_34122861/article/details/104074479