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

before|正文之前:

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

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

1问题

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

2精讲

2.1初识虚函数

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

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

virtual void display();

为什么要用虚函数?

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

我们来看看简单的例子。
(1)定义子类对象,并调用对象中未被子类覆盖的基类函数A。同时在该函数A中,又调用了已被子类覆盖的基类函数B。那此时将会调用基类中的函数B,可我们本应该调用的是子类中的覆盖函数B。虚函数即能解决这个问题。
C/C++ 虚函数与多态(含vector用法) 及获取文件内容、打印字符串实例_第3张图片
C/C++ 虚函数与多态(含vector用法) 及获取文件内容、打印字符串实例_第4张图片
(2)在使用指向子类对象的基类指针,并调用子类中的覆盖函数时,如果该函数不是虚函数,那么将调用基类中的该函数;如果该函数是虚函数,则会调用子类中的该函数。
C/C++ 虚函数与多态(含vector用法) 及获取文件内容、打印字符串实例_第5张图片
修改为虚函数则能解决这个问题。
这就是虚函数最简单的理解,具体原理不作讲解,参考文章。

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 
#include 
#include 
#include 
#include 
#include 
#include 

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/C++ 虚函数与多态(含vector用法) 及获取文件内容、打印字符串实例_第6张图片
感谢大家观看~(#.#)

你可能感兴趣的:(学习笔记,c/c++,萌新笔记)