文章目录
- 0. 前言
- 7. 类模板array和vector、异常捕获
- 7.1 简介
- 7.2 array对象
- 7.3 array对象的声明
- 7.4 使用array对象的例子
- 7.4.1 声明array对象以及用循环来初始化array对象的元素
- 7.4.2 在声明中用初始化列表初始化array对象
- 7.4.3 用常量变量指定array对象的大小并用计算结果设置array对象元素
- 7.4.6 把array对象元素当作计数器使用
- 7.4.7 把array对象来汇总调查结果
- 7.4.8 静态局部array对象和自动局部array对象
- 7.5 基于范围的for语句
- 7.7 array对象的排序与查找
- 7.8 多维array对象
- 7.9 实例研究:利用二维array对象的GradeBook类
- 7.10 C++标准库类模板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]);
... ...
}
}
... ...
部分代码解释
- 将课程名和存储学生成绩的array对象传入GradeBook类的构造函数,初始化
courseName
和grades
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的基础即可完成。
所有问题,都是数学逻辑问题,编程并不是最难的。
最近在思考,作为一个非机专业出身的人,编程到底要学多少,学到什么程度,才能算一个合格的控制硕士? 是只需要仿真数据处理? 还是别的什么? 求解答疑惑……
新年第一更,大家过年好~
个人水平有限,有问题欢迎各位大神批评指正!