本章一共包含七个编程习题:
以下习题答案全部通过OJ,使用编译器为:G++(9.3(with c++17))
1. 简单的SumArray
考点:函数模板,指针
解析:本题需要调用SumArray()把两个不同类型的数组的元素,相加在一起,所以我们先来看看它的参数是什么
// 要把array里面的所有元素相加,那么我们需要遍历所有元素一边
// 这里可以把两个参数看成两个元素指针,第一个表示数组第一个元素的起始地址,
// 第二个表示数组的末尾元素地址 + 1,即表示的元素范围为:[ start_ptr, end_ptr ] or [ start_ptr, end_ptr + 1 )
SumArray( array, array + 4 )
// 对于为什么是末尾元素地址 + 1,我们看看下面的长度为5的数组的例子
0x0 0x1 0x2 0x3 0x4 0x5
a b c d e
^ ^
s s + 5
确定参数类型后,在函数内部,我们只需遍历每一个元素,进行累加操作即可:
// 获取数组第一个元素
T t = *s;
for (
// 跳过第一个元素
s += 1;
// 只要s(数组开始地址)不等于e(数组末尾地址+1),则继续遍历相加
s != e;
// 遍历下一个元素
s += 1
) {
t += *s; // 累加
}
return t;
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
template <class T>
T SumArray( T *s, T *e ) {
// 在此处补充你的代码
T t = *s;
for ( s += 1; s != e; s += 1 ) {
t += *s;
}
return t;
}
2. 简单的foreach
考点:函数模板,函数指针
解析:题目的关键是下面两行代码:
std::string array[ 100 ];
int a[ 100 ];
MyForeach( array, array + m, Print );
MyForeach( a, a + n, Inc );
函数参数的前两个形式,我们已经很熟悉了,还是不熟悉的童鞋可以参考上题:简单的SumArray,那么第三个参数是不同类型的函数指针,所以MyForeach()函数需要两个模板变量,一个表示数组元素类型,另一个表示调用函数的类型,并且根据题意,遍历每个元素时,需要调用函数参数进行处理。另外需要注意,题目调用的函数都是引用或对象,不是指针,所以调用时需要对指针进行取值操作。
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
// 在此处补充你的代码
template<class T, class F>
void MyForeach( T *s, T *e, F *f ) {
for ( ; s != e; s += 1 ) {
f( *s ); // 对指针进行取值操作
}
}
3. 简单的Filter
考点:函数模板,函数指针
解析:思路和上题差不多,唯一需要注意的就是返回值是目标数组的最后一个指针地址 + 1,因为外面遍历的时候,使用了地址运算,求得一共需要遍历多少个元素:
// p是as2最后一个元素地址 + 1,as2又是第一个元素地址,那么p - as2 + 1表示一共有多少个元素
// i < p - as2就是遍历所有元素的下标
for ( int i = 0; i < p - as2; ++i )
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
// 在此处补充你的代码
template<class T, class F>
T *Filter(
// 需要filter的数组第一个元素地址
T *s,
// 需要filter的数组最后一个车元素地址 + 1
T *e,
// 目标数组第一个元素地址,这个数组用于存储filter后的结果
T *R,
// filter函数指针
F *f
) {
for ( ; s != e; s += 1 ) {
if ( f( *s ) ) {
*R = *s;
R += 1;
}
}
return R;
}
4. 你真的搞清楚为啥 while(cin >> n) 能成立了吗?
考点:类型转换运算符
解析:本题第一需要重载运算符>>,用于cin的输入,这个不难,第二个就是如何实现下面语句:
while ( m >> n1 >> n2 )
这里 m >> n1 >> n2 返回值是MyCin对象是不能当作bool类型使用的,所以我们需要强制转换成bool类型,需要使用到下面语法:
explicit operator bool() const {
// 返回需要的值,最后强制转换后的bool值
}
因为我们只需要当输入为-1时,结束程序,我们就用一个bool成员变量纪录程序状态,当输入为-1时,设置为false,然后强制转换时返回它就行。
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
class MyCin {
// 在此处补充你的代码
bool f = true;
public:
MyCin &operator>>( int &n ) {
if ( !f ) return *this;
std::cin >> n; // Always enter this line.
if ( n == -1 ) f = false;
return *this;
}
explicit operator bool() const {
return f;
}
};
5. 山寨版istream_iterator
考点:类模板
解析:本题需要重载两个运算符:
// 从cin中读取当前的值
n1 = *inputInt;
// 移动iterator指向下一个值
inputInt++;
重载运算符*时,我们只需返回类里面从cin读取的值,重载运算符++时,我们从cin中读取一个新的值,也就是iterator指向了下一个值,通过这种方法来实现我们自己的istream_iterator。
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
template<class T>
class CMyistream_iterator {
// 在此处补充你的代码
T v; // 相当于iterator
std::istream &is; // std::cin
public:
CMyistream_iterator( std::istream &is_ ) : is( is_ ) { is >> v; }
T &operator*() {
return v; // 返回当前值
}
CMyistream_iterator<T> operator++( int n ) {
CMyistream_iterator t = *this;
// 从cin里面读取下一个值
is >> v;
return t;
}
};
6. 这个模板并不难
考点:类模板
解析:题目要求在输入的每个元素之后,插入一个逗号,那么我们可以在构造函数里面初始化一个数组用于存储输入数据,然后再Show()函数里面进行输出。这里注意需要输出的数组数据类型不一致,所以需要模板来实现。
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
template<class T>
class myclass {
// 在此处补充你的代码
T *p;
size_t size;
public:
myclass( const T* C_, size_t n_ ) {
size = n_;
p = new T[ n_ ];
std::copy( C_, C_ + n_, p );
}
~myclass() {
delete[] p;
}
void Show() {
for ( int i = 0; i < size; i++ ) {
std::cout << p[ i ] << ",";
}
std::cout << std::endl;
}
};
7. 排序,又见排序!
考点:函数模板
解析:题目要求实现一个可以接受任意类型的sort函数,那么我们就需要考虑使用模板进行实现,这里需要有两个模板类型,一个时数组的类型,一个是排序比较函数的类型,题主这里使用了最简单的冒泡排序,所以时间复杂度是O( n )
答案:完整源码地址
// 这里只给到需要补完的代码,完整代码请移步到github
// 在此处补充你的代码
template<class T1, class T2>
void mysort( T1 *s, T1 *e, T2 *f ) {
// Bubble sort
// 这里都是用指针进行数组遍历
for ( ; s != e; s += 1 ) { // 第一层遍历每个元素
T1 *t = s + 1;
for ( ; t != e; t += 1 ) { // 第二次遍历,找到当前最大的元素
if ( !f( *s, *t ) ) { // 交换
T1 tv = *s;
*s = *t;
*t = tv;
}
}
}
}
上一章:多态
下一章:标准模板库STL(一)
8. 参考资料
9. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;