前言

在这一次的作业中,老师希望我们复习课本《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++知识的。代码因为截止日期问题就不公布了,需要可以发邮件给我。

这里的一切都有始有终,却能容纳所有的不期而遇和久别重逢。
最后更新于 2024-01-14