42 多态性与虚函数
https://www.cctry.com/thread-290046-1-1.html
多态:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为。
例如:成员函数的重载
使用虚函数实现多态:
使用基类的指针,指向派生类的对象,调用虚函数的时候,最后调用的是派生类的函数!
int main()
{
CZhongStudent stud_zhong;
stud_zhong.shangke(); //调用的是CZhongStudent的函数
CStudent* pStud;
pStud = &stud_zhong; //父类的指针指向子类的对象
pStud->shangke(); //调用的是CStudent的函数
//所以就是,用类的指针指向子类的对象,用父类的指针调用shangke(),调用的也还是父类的shangke()
return 0;
}
如果想要对象是谁就调用谁的函数(即:比如上面的情况,父类对象pStud引用子类对象stud_zhong,所以实际上对象就是子类对象stud_zhong,那么调用函数的时候想调用的是子类CZhongStudent的shangke()),默认是不行的,但是可以把shangke()这个函数声明成虚函数
在函数声明前加virtual
(要在父类中加,在子类中不用加)
虚函数的注意事项:
①、
virtual
只能用来声明类的成员函数,把它作为虚函数,而不能将类作用域外的普通函数声明成虚函数。因为虚函数的作用是允许在派生类中对基类的虚函数重新定义。显然,他只能用于类的继承层次结构中;
②、一个类中的某个函数被声明成虚函数之后,同一类中就不能再定义一个非virtual的参数和返回值类型都相同的成员函数了。
什么情况下使用虚函数:
一般情况下是某个函数所在的类可能会作为父类/基类,而且该函数有可能会被派生类重写,并被派生类使用,那么这个时候就可以考虑将该函数声明为virtual 虚函数。否则就不用!因为声明成虚函数之后是有开销的,所以不要随随便便的想声明成虚函数就声明。
小作业:
创建一个狗的类 CDog,包含 wangwang 这个虚函数,之后从 CDog 分别派生2-3个子类,比如:有金毛、吉娃娃、藏獒 等。分别实现每个子类的 wangwang 方法,之后试着调用该方法,充分理解虚函数的特性!
#include <iostream>
#include <string>
using namespace std;
class CDog
{
public:
virtual void wangwang()
{
cout << "CDog 汪!" << endl;
}
};
class CJinmaoDog :public CDog
{
public:
void wangwang()
{
cout << "CJinmaoDog 汪!" << endl;
}
};
class CJiwawaDog :public CDog
{
public:
void wangwang()
{
cout << "CJiwawaDog 汪!" << endl;
}
};
class CZangaoDog :public CDog
{
public:
void wangwang()
{
cout << "CZangaoDog 汪!" << endl;
}
};
int main()
{
CDog *p_dog;
CJinmaoDog jinmao;
CJiwawaDog jiwawa;
CZangaoDog zangao;
p_dog = &jinmao;
p_dog->wangwang();
p_dog = &jiwawa;
p_dog->wangwang();
p_dog = &zangao;
p_dog->wangwang();
return 0;
}
43 文件IO操作之打开与关闭
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
short var = 10000;
ofstream fs1; //输出到文件
fs1.open("d:\\1.txt");
fs1 << var << endl;
fs1.close();
ifstream fs2;
fs2.open("d:\\1.txt"); //从文件输入
var = 0;
fs2 >> var;
cout << var << endl;
//此时文件占7个字节,“10000”是5个,剩下两个是endl的,显示“10000 CR LF ”
//下面是关于二进制的操作:
fs1.open("d:\\1.txt");
fs1.write((char*)&var, sizeof(short));
fs1.close();
fs2.open("d:\\1.txt");
fs2.read((char*)&var, sizeof(short));
cout << var << endl;
//此时文件占2个字节, 显示为“ N”,因为本身short占2字节
//所以就是文本文件易读,但占空间多,二进制文件占空间小,但看不懂。根据需要选择
return 0;
}
C++中的文件操作类:
fstream(输入输出文件流):支持文件的输入与输出操作;
ifstream(输入文件流):支持从文件中输入操作;
ofstream(输出文件流):支持向文件写入的操作;
文件的打开操作:构造函数与open函数
ofstream fs("z:\\123.txt");
fs.open("z:\\123.txt");
这两种操作都是可以的。
实际上open函数有多个参数,第2个参数也很常用,表示打开文件的方式,取值有如下:
ios::in
为输入(读)而打开文件
ios::out
为输出(写)而打开文件
ios::ate
初始位置:文件尾
ios::app
所有输出附加在文件末尾
ios::trunc
如果文件已存在则先删除该文件
ios::binary
二进制方式
这些方式是能够进行组合使用的,以“或”运算(“|”)的方式,例如:
ofstream fs;
fs.open("123.txt", ios::in|ios::out|ios::binary);
ios::in
ios::out
如果不写,会默认存在:
ofstream fs1("123.txt", ios::out);
ifstream fs2("123.txt", ios::in);
fstream fs3("123.txt", ios::in|ios::out);
判断文件是否成功打开:
几种方法:
①、直接 if 判断 fs 对象;
②、用 is_open 方法判断;
③、用 good 方法判断;
④、用 fail 方法判断;
①②常用
if(!fs)
{
cout << "open error" << endl;
}
if(!is_open)
{
cout << "open error" << endl;
}
文件流的关闭:fs.close()
小作业:
使用 fstream 文件流来对文件进行读写操作:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
short var = 10000;
fstream fs;
fs.open("d:\\1.txt");
fs << var <<endl;
fs.close();
fs.open("d:\\1.txt"); //一定要关了重新开,不然的话读不到文件里的数据,var输出会是200
var = 200;
fs >> var;
fs.close();
cout << var << endl; //输出10000,证明是从文件里读到的
return 0;
}
44 文件IO操作之读写
文件的写操作:
写操作对应的有:<<、put、write 等
①、写文本类型/ASCII码类型的可见字符串:<<
、put
ofstream fs;
fs.open("d:\\1.txt");
if (!fs) return 0;
fs << 2000 << endl;
fs << 2.15 << endl;
fs << "hello" << endl;
fs.put('Y'); //只能写入字符
②、二进制类型写文件:write
int var = 12345;
fs.write((const char*)&var,sizeof(var)); //第一个参数是开始的地址,第二个参数是长度
//这样子写入,显示的是乱码
//也可以显示明文的:
char s[] = "你好";
fs.write(s, sizeof(s));
常用的是<<和write()
文件的读操作:
①、读文本类型/ASCII码类型的文件:>>、get、getline
ifstream fs_read;
fs_read.open("d:\\1.txt");
int a = 0;
fs_read >> a; //把文本文件里的第1个值2000读到a中
float f_var = 0;
fs_read >> f_var; //把文本文件里的第2个值2.15读到f_var中
char s_var[100] = { 0 };
fs_read >> s_var; //把文本文件里的第3个值hello读到f_var中
char ch = fs_read.get(); //从文本文件只读一个字符,把值‘\n’ 读到ch中
ch = fs_read.get(); //从文本文件只读一个字符,把值‘Y’ 读到ch中
getline 函数:从文件中读取一行数据(所谓的一行就是有换行符 \n,或者达到 getline 的缓冲区大小,或者读到文件末尾。)
ifstream fs_read;
fs_read.open("d:\\1.txt");
char s2[100];
for (int i = 0; i < 4; i++)
{
memset(s2, 0, 100);
fs_read.getline(s2, 100);
cout << s2 << endl;
}
②、读取二进制文件类型:read
ofstream fs;
fs.open("d:\\1.txt");
if (!fs) return 0;
int var = 12345;
fs.write((const char*)&var, sizeof(var)); //第一个参数是开始的地址,第二个参数是长度
char s[] = "你好";
fs.write(s, sizeof(s));
fs.close();
ifstream fs_read;
fs_read.open("d:\\1.txt");
int var2 = 0;
fs_read.read((char*)&var2, sizeof(int));
cout << var2 << endl;
char s2[100] = { 0 };
fs_read.read(s2, 100);
cout << s2 << endl;
fs_read.close();
小作业:
如何判断读写文件的操作/函数是否成功了呢?以及如何判断是否读到文件末尾了呢?
判断文件是否为空时使用peek函数,若peek返回EOF则文件为空;读取非char型时,使用peek判断文件尾将不再适用
(第一问不会。。)