C/C++ 虚函数与多态(含vector用法) 及获取文件内容、打印字符串实例
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定义类成员函数指针时指针前面必须加类名和命名空间限定符,也就是形如: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最终效果
感谢大家观看~(#.#)
- C++ 一条代码打印vector内容以及random_shuffle函数
- 【C++】sprintf的用法详解 (打印成各种格式的字符串)
- C++采用openfilename打开文件对话框用法实例
- c++字符串分割 和 c++读取文件内容
- C++中vector的用法实例解析
- c++ getline用法实例,读取一行到字符数组+读取一行到字符串
- strings命令_Linux strings 命令用法详解:在对象文件或二进制文件中查找可打印的字符串
- c++ stl容器vector删除(erase),遍历等基本用法介绍及头文件
- PHP截取字符串,获取IP,编码转换,获取远程文件内容
- c++ 打印简单log信息,输出内容到某一文件中
- c++ 十六进制字符串存入文件实例
- c++文件读写,vector用法,运算符重载
- C++中引用(&)的用法和应用实例===引用和多态的关系!!!!!!!!!!!!!!!!!
- 利用jquery的获取JS文件中的字符串内容
- C++获取某一任意字符串每一个字符连续出现次数并打印在控制台上
- seek和tell的用法--获取文件内容大小(字节)
- php获取字符串前几位的实例(substr返回字符串的子串用法)
- http post方法调用接口获取json文件内容 以及获取Json字符串某节点的值
- c++ vector 读INI文件用法
- seek和tell的用法--获取文件内容大小(字节)