文章目录
前言
在这一次的作业中,老师希望我们复习课本《C++ Primer Plus 6th》的前九章内容。前九章中大部分内容我们都在学习C语言的时候有涉及,还有一些C++特有的功能等。接下来我将对其中一些我认为值得注意的任务进行记录。
作业文档:https://files.hoyue.fun/documents/hmk6_knowledge_points.pdf
作业说明
这次作业覆盖了以下内容:
- 命令行编译多文件,C++11下,以及多文件链接。
- .h文件、.cpp文件之间的链接。
- 头文件中
#ifndef #define #endif
的使用。 - struct, union, enum的使用。
- 函数指针和函数引用的使用。
- static和extern数据的使用。
- 动态数组的使用。
- 函数作为参数的使用。
- 文件输入输出。
- 函数模板显式与隐式表达式。
- 函数的重载。
- 命名空间的使用。
- string类、vector、array的使用。
- 还有一些基本的特性...
任务说明
Task 3
Task3主要是定义结构体和Union在结构体中的定义。
结构体的定义在C语言中已经有过说明(https://hoyue.fun/linkedlist_c.html),这里讲讲union。共用体(或称联合,英文union) 是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。
例如:结构可以同时存储int、long long和double类型数据, 共用体只能存储int、long long 或double。
例如:一本书有了ISBN就不要id了,如果没有ISBN就给它一个ID。它们就是“或”的关系。
union Book{
char isbn[14];
int library_id;
}
Book book;
此时book变量就具有char *类型或int类型,我们输入的时候输入了一个另一个就不能存入。
在结构体怎么写呢,我们有两种方法,一种是不匿名,另一种是匿名的。
不匿名:
struct book{
std::string title;
std::string authors;
double price;
union Identify{
char isbn[14];
int library_id;
}identity;
}Best;
//例如输入ISBN的时候
std::cin>>Best.identity.isbn;
匿名:
struct book{
std::string title;
std::string authors;
double price;
union {
char isbn[14];
int library_id;
};
}Best;
//同上,输入ISBN
std::cin>>Best.isbn;
对比我们可以知道,在结构体中的union我们最好写成匿名的形式,可以使代码变得方便。当然匿名只能是在结构或类中,单独在外定义为匿名的话需要马上定义变量。
Task 7 & 11
命名空间的声明与定义。我们需要自己定义命名空间的时候,可以把声明放在.h文件中,定义放在.cpp中,例如:
在文件data.h中声明
namespace SCSE{
extern std::string url;
void welcome();//referential declaration
}
这里的声明任务还需要url只读,我们知道字符串要是只读的话需要定义为string类或char *的形式,string类比较方便于是就在此定义。
使用extern是因为要在其他文件中引用,定义为全局的。当然不只是在namespace里需要extern,如果我们需要全局变量在其他文件中引用,均需要在定义变量前添加extern关键字。
在文件data.cpp中定义
std::string FIE::url="https://www.must.edu.mo/fie";
void FIE::welcome()
{
std::cout<<"Welcome to the Faculty of Innovative Engineering";
}
在cpp文件中不需要再写一次namespace来声明,只需要初始化即可。
Task 12 & 14
这两个任务是要求我们写函数模板,并声明和定义。
先来个错误试例:
因为是多文件,所以在.h文件中声明先。因为是模板函数,所以要写一行template。
template <typename T> void sort_arr(T *arr, int n);
然后在.cpp文件中定义:在这里写一个选择排序。
template <typename T>//define void sort_arr(T *arr, int n) { using namespace std; for (int i=1;i<n;i++)//suppose arr[0] is ordered. { T tmp = arr[i]; int j = i - 1; while (j >= 0 && arr[j] >= tmp) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = tmp; } }
然后问题来了,在编译的时候编译器显示:"Undefined sort_arr(T *arr, int n)" 然后排除了好久的错误,最后终于发现了错误。
在写函数模板的时候,如果是在同一个文件中就不会发生这种情况,但如果是在不同的文件中,那也需要写在一起。
所以我们应该直接在.h文件中,这么写:
template <typename T>
void sort_arr(T *arr, int n);//declare
/*
arr is array name, and n is the length of array.All the sort functions are from smallest to largest.
*/
template <typename T>//define
void sort_arr(T *arr, int n)
{
using namespace std;
for (int i=1;i<n;i++)//suppose arr[0] is ordered.
{
T tmp = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] >= tmp)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = tmp;
}
}
这样就不会出现编译不通过的情况了~总结一下:
对于函数或类模板的声明与实现放在不同文件中的情况,在main()函数使用时,需要引用类模板的实现所在的文件,而不是声明所在的文件。
所以还是需要把函数或类模板的声明与实现放在一个文件中。
Task 28
这个任务是写一个输出函数,输出斐波那契数列,参数是一个输出流。
再来一个错误试例:
.h文件中声明:
void print_fibs(std::ofstream stm);
然后main函数中调用报错。
那我们该如何写输出流作为参数呢?
void print_fibs(std::ofstream &stm);
我们应该使用引用,因为我们需要使用这个流进行输出操作...
Task 39.2
这个任务是输入一串字符,因为我们不知道长度,我们可以用string类来写,或者一个个读入,一个个读入的写法:
std::string tmpmotto;
for(int i=0;;i++)
{
char ch;
std::cin>>std::noskipws>>ch;
if(ch=='\n') break;
else tmpmotto.append(1,ch);
}
其中string.append(int,char)是向string后尾添加n个字符,noskipws是忽略空格和换行。
Task 42 & 43
函数指针,即用一个指针代替这个函数,那就需要知晓原函数的原型才能定义,例如我们写一个strlen的函数指针:
size_t (*fp1)(const char *str); // strlen is size_t strlen(const char *_str);
fp1=strlen;
当然我们还可以使用auto,一行解决。
auto (*fp1)(const char *str)=strlen
Task 56 & 57
我们在使用函数模板的时候有显式表达式和隐式表达式,他们的写法有哪些不同呢,例如都是sort_arr()的模板函数,arr1就是隐式表达式,因为需要编译器自己决定类型,而arr2就是显式表达式了,因为直接告诉了编译器类型:
sort_arr(arr1,5);
sort_arr<double *>(arr2,5);
后记
大概就是这么多内容暂时,因为作业工作量大,但是并不难,好好做还是可以巩固C++知识的。代码因为截止日期问题就不公布了,需要可以发邮件给我。
Comments NOTHING