您的位置:首页 > 编程语言 > C语言/C++

C/C++ 虚函数与多态(含vector用法) 及获取文件内容、打印字符串实例

2019-06-05 15:35 337 查看

before|正文之前:

c++实验代码及学习笔记(十)

你好! 这是一个高程实验课的代码记录及学习笔记。我将记录一些重要的知识点、易错点。但是作为大学生,水平很低,敬请指点教导、优化代码。

1问题


题目比较难懂,首先我们从实验目的得知,主要运用虚函数与运行时多态的知识。
我们需要实现的是从文件读取字符串,且一个函数(调用不同类指针)可以实现不同功能。
第一,我们已经从以前的实验中习得,如何获取文件。之前我们使用的c语言的函数,此次我们将自学c++输入流的方法。
第二,这个函数比较难以理解,一开始我以为是抽象类虚函数、派生类不同的readstrings函数,但是这样无法使用类指针。故这个函数在全局范围内使用。
第三,我们来看不同的功能:打印很好实现,而获取包含字符数最多的字符串较难;大小写转换通过查阅博客可以学习,比较简单。获取字符数最多的字符串可以理解为,以空格为分隔,比较不同长度的字符子串。如何分割空格、比较,就成为了本次的难题。
第四,最后一小题需要存储vector,我们将自学vector并进行讲解。

2精讲

2.1初识虚函数

参考文章:
1 c++虚函数详解(你肯定懂了)
2 理解C++虚函数

C++虚函数是定义在基类中的函数,子类必须对其进行覆盖。在类中声明(无函数体的形式叫做声明)虚函数的格式如下:

virtual void display();

为什么要用虚函数?

C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

我们来看看简单的例子。
(1)定义子类对象,并调用对象中未被子类覆盖的基类函数A。同时在该函数A中,又调用了已被子类覆盖的基类函数B。那此时将会调用基类中的函数B,可我们本应该调用的是子类中的覆盖函数B。虚函数即能解决这个问题。


(2)在使用指向子类对象的基类指针,并调用子类中的覆盖函数时,如果该函数不是虚函数,那么将调用基类中的该函数;如果该函数是虚函数,则会调用子类中的该函数。

修改为虚函数则能解决这个问题。
这就是虚函数最简单的理解,具体原理不作讲解,参考文章。

2.2类成员指针

参考文章
1 【C++面向对象】C++的类型成员指针
2 如何使用指向类的成员函数的指针(详解!)

  1. 是什么?
    类型成员指针不是指针成员

类型成员指针可以指向本类任意一个对象的该成员,并可以对成员进行读写操作。请注意这里是说的任意一个对象,也就是说类型成员指针和类对象无关。

  1. 是指针吗?
    类型成员指针不是指针

类型指针是对象成员相对于对象地址的偏移量
当我们用类对象去调用该类型成员指针时,程序就会用类对象地址加上改偏移量去解析其对应的值。

  1. 如何使用类成员函数指针
    1定义类成员函数指针时指针前面必须加类名和命名空间限定符,也就是形如:person::*p;同时等号右边、类成员函数前面必须加上取地址操作符&。
    2使用类成员函数指针时必须把对象名和指针名括起来,形如:(p1.*p)
class person
{
public:
std::string m_name;
person(const char* name):m_name(name){}
void print();
};
int main()
{
void (person::*p)()=&person::print;
person p1("张三",28);
(p1.*p)();
}

成员指针的调用:
成员函数指针的调用必须通过类对象,和操作符 .* 或 ->*

int person:: * pi = &A::i;
Person a;
a.*pi;      // 等同于 a.i
person* a_ptr;
a_ptr->*pi; // 等同于 a_ptr->i;

针对本题,我们需要一个抽象类StrHandler和三个派生类,四个成员函数(虚函数),不同的指针指向不同成员函数。

void readStrings(const std::string &file,StrHandler *p)
{
p->Handle(file);
}
int main()
{...
//三个实现不同功能的派生类
printStr h1;
getLongStr h2;
getLowerStr h3;
//创建指针
StrHandler *a = &h1;

readStrings(file, a);
a = &h2;
readStrings(file, a);
a = &h3;
readStrings(file, a);
...
}

3功能实现

根据题目,首先我们要从文件获取字符串,然后进行打印、比较长字符子串、转换小写等操作。
这些操作涉及到输入输出流的知识。如果我们学习了I/O输入输出流,那么这些功能将格外简单。具体知识我们下节再讲。

3.1获取字符串

参考文章:
1C++ 读文件 将文件内容读入到字符串string中的方法

string readFileIntoString(char *filename)
{
ifstream ifile(filename);
ostringstream buf;
char ch;
while (buf&&ifile.get(ch))
buf.put(ch);
return buf.str();
}

int main()
{
char filename[20];
string file;

cout << "请输入文件名:" << endl;
cin.getline(filename, 20);
file = readFileIntoString(filename);
...
}

这样,就可以获取文件内容,储存到字符串中了。

3.2比较子串长度

参考文章:
1C++中的 istringstream 的用法
现在文件字符串内容已经储存到字符串str中了,我们该如何比较字符数最多的子串呢?
根据题意,若文件中含有不止一个单词,以空格为分割,我们可以比较单词的长度。那么,我们如何用空格分割单词并比较?方法是多样的,这里,我们用最简单的字符串输入流进行操作。
如果你不知道什么是istringstream,请阅读参考文章。他能够从文件中读取字符,以空格为分割,且串流可在循环中逐个输出子串。
如“I am your father”,可以被分割成四部分,分别输出“I”、“am”、“your”、“father”。
所以通过字符串流,我们可以把一小块一小块的字符串分别输出给temp,然后再逐个进行比较(比大小)
最后我们输出即可。

void Handle(const std::string &file)
{
str = file;
string p = str;
string temp1, ans = "";
istringstream string_in(str);
while (string_in) {
string_in >> temp1;
if (temp1.length() > ans.length()) {
ans = temp1;
}
}
cout << "字符数最多的字符串是:" <<endl<< ans << endl;

}

3.3大小写转换

参考文章
1transform函数转换字符串string的大小写
2C++ vector的用法(整理)

转换大小写非常简单,只需要一个transform函数即可。

string getLower(const std::string &file)
{
str = file;
transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}

而储存到vector需要对vector系统的学习。其实它并不难,是一个已经编写好的动态数组类。具体使用方法参考文章中讲解得非常详细。

4完整代码

注意头文件,这次实验包含的头文件很多

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iterator>
#include <algorithm>

using namespace std;

//测试文件:a.txt,可选择其他同一目录下的文件进行测试

class StrHandler
{
public:
std::string str;
std::vector<string> v;
public:
StrHandler() {};
virtual void Handle(const std::string &file) {};
};

class printStr
:public StrHandler
{
public:
printStr() {};
void Handle(const std::string &file)
{
str = file;
cout << str << endl;
}
};

class getLongStr
:public StrHandler
{
public:
getLongStr()
:StrHandler()
{};

void Handle(const std::string &file)
{
str = file;
string p = str;
string temp1, ans = "";
istringstream string_in(str);
while (string_in) {
string_in >> temp1;
if (temp1.length() > ans.length()) {
ans = temp1;
}
}
cout << "字符数最多的字符串是:" <<endl<< ans << endl;

}
};

class getLowerStr
:public StrHandler
{
public:
getLowerStr()
:StrHandler()
{};
string getLower(const std::string &file)
{
str = file;
transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
void setVector(const std::string &file)
{
string lowerfile;
string buf;
lowerfile = getLower(file);
stringstream ss(lowerfile);

while (ss >> buf)
this->v.push_back(buf);
}
void Handle(const std::string &file)
{
setVector(file);
cout << "全部转换为小写:"<<endl << str << endl;
}
};

string readFileIntoString(char *filename)
{
ifstream ifile(filename);
ostringstream buf;
char ch;
while (buf&&ifile.get(ch))
buf.put(ch);
return buf.str();
}

void readStrings(const std::string &file,StrHandler *p)
{
p->Handle(file);
}

int main()
{
char filename[20];
string file;

cout << "请输入文件名:" << endl;
cin.getline(filename, 20);
file = readFileIntoString(filename);

printStr h1;
getLongStr h2;
getLowerStr h3;

StrHandler *a = &h1;

readStrings(file, a);
a = &h2;
readStrings(file, a);
a = &h3;
readStrings(file, a);

getchar();
return 0;
}

5最终效果


感谢大家观看~(#.#)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: