继承
c++中 类和类直接存在某种特殊的关系,定义这些类时,成员除了有自己的特性,还有上一级类的共性,这个时候可以利用继承,减少代码的重复
继承的好处:减少代码的重复
语法: class 子类 (派生类): 继承方式 父类(基类)
class Base()
{
public :
int m_A;
};
class son : public Base
{
public :
int m_ b ; //父类Base中的m_A通过继承的方式传递了下来
//类son中本质上有两个属性。 m_A被隐藏了
};
继承方式:公共继承(public) 、 保护继承(protected)、私有继承(private)
各继承方式的共同点:子类都不能访问父类中的私有成员
各继承方式的区别:public(公共继承)子类继承父类中的各个权限的属性;protected(保护继承)子类除父类中的私有权限之外, 父类中的其它权限在子类中全部变成protected保护权限;
private(私有继承)子类除父类中的私有权限之外,父类中的其它权限在子类中全部变成私有权限
如下图理解:
注意: 父类中的所以成员都会被继承下来(包括私有成员只是不能访问),只是在子类中被隐藏了。
继承中对象模型:
通过VS的开发者人员命令窗口 可以访问对象的具体布局;如下图:
同名成员函数和静态同名成员函数的处理
当子类和父类中出现同名的成员时,有两种方法可以区分:
(1)访问子类中的同名成员 -- 直接访问, 子类对象 . 同名成员
(2)访问父类中的同名成员 - - 需要加作用域: 当通过子类对象访问父类中同名成员时需要加上作用域 如: s. Base :: m_A;
注意:如果子类和父类有同名成员函数,子类中的同名成员函数会隐藏掉父类中的所有同名成员函数(包括同名成员函数的所有重载版本),此时想访问父类中的同名成员函数需要加上作用。
静态同名成员函数的处理与同名成员函数的处理相似
类中成员可以通过两种方式来访问,一种是通过对象访问, 一种是通过类名访问, 如果想用子类类名访问父类作用域下的成员时,需要加上 :: :: 如: Son :: Base :: m_A ; 第一个 ::表示通过类名访问,第二个 :: 表示访问父类作用域下的成员。
多继承: class 子类 : 继承方式 父类,继承方式 父类
实际开放中不提倡使用多继承的方法。
多态
多态分为两类:静态多态和动态多态
静态多态:运算符重载、函数重载等,符用函数名,地址早绑定,在编译阶段就确定了地址
动态多态:派生类和虚函数实现运行时多态,地址晚绑定,在运行阶段才确定地址
virtual 是虚函数的关键字 , 令虚函数等于0 就变成了纯虚函数,用于纯虚函数的类也称为抽象类
抽象类的特点:不能实例化对象, 子类必须重写父类中的抽象类,不然也属于抽象类
多态产生的条件:
要有继承关系 ; 子类必须重写父类中的虚函数(重写:函数返回类型 函数名 参数列表 都相同)
子类中的virtual可以不写。
多态实现的原理:
解释:当类中有虚函数之后,会产生一个vfptr - 虚函数指针 ,此指针指向虚函数表 vftable
虚函数表中的内容是 被virtual修饰的函数, 当子类继承父类之后,子类如果没有重写父类中的虚函数表的内容的话,子类中的虚函数表存放的是父类虚函数表中的函数,重写之后,子类中的虚函数表中的内容就替换成立重写之后的函数;当父类指针 或引用 执行子类对象时就发生多态 也就实现了传进哪个调用哪个。
虚析构和纯虚析构
当子类中有属性开辟在堆区时, 父类指针无法调用子类的析构函数,可能会出现内存泄漏问题
利用纯、虚析构来解决问题
虚析构和纯虚析构的共性
(1)可以通过父类指针来释放子类对象
(2)都需要有具体的实现
区别:
如果是纯虚析构,那么该类属于抽象类,无法实例化对象,纯虚析构需要具体的实现,也需要声明,在类内声明,类外具体实现 : 类名 :: ~类名 ()
文件操作
在c++中,程序运行时产生的数据都属于临时数据,运行结束就会被释放,通过文件可以使数据持久化。
C++中对文件的操作需要包含头文件 fstream
文件类型分为两种:
文本文件:文件以文本的ASCII码值形式存储在计算机中
二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
文件操作的三大类:
ifstream(读文件)、 ofstream(写文件) 、 fstream(读写文件)
文本文件的写和读
(1)写
首先包含头文件 fstream ; 然后创建一个 流对象 : ofstream ofs; 再打开文件: ofs.open("文件的路径",打开方式) ; 写: ofs << "内容" << endl; 最后关闭文件: ofs.close();
文件的打开方式有: ois :: in (以读的形式打开)、 ios ::out(以写的形式打开) 、 ios :: binary (以二进制的方式打开)
打开方式 可以两个一起 用 | 隔开
#include<fstream>
ofstream ofs;
ofs.open("test.txt" ios :: out ); //如果不指定路径,默认放在源文件路径下
ofs << "hello world " << endl;
ofs.close();
(2)读
首先还是包含头文件fstream ; 创建流对象 ifstream ifs; 打开文件这里要判断是否打开成功 用is_open函数; 然后再读文件; 最后关闭
读文件的方式有四种
#include<fstream>
ifstream ifs;
ifs.open("text.txt" ios :: in) ;
//判断文件是否打开成功
if(! ifs.is_open()) // 利用!取反符
{
cout << "文件打开失败" << endl;
}
//读的第一种方式
char buf[1024] = { 0 };
while(ifs >> buf)
{
cout << buf << endl;
}
//第二种
char buf[1024] = { 0 };
while(ifs.getline(buf,sizeof(buf)))
{
cout << buf << endl;
}
//第三种
string buf;
while(getline(ifs,buf))
{
cout << buf << endl;
}
//第四种
char c;
while((c = ifs.get()) !=EOF) //EOF end of file 文件结束标志
{
cout << c << endl;
}
ifs.close();
读的方法个人喜欢第三种,其次使第二种, 第四种不推荐
二进制文件的读和写
具体步骤都差不多,在打开文件那一步 需要加上二进制打开方式 如:ios :: in | ios :: binary
第四步写文件 需要利用write函数 : ofs.write((const char * )&p ,sizeof(p)); 指针指向一个对象,sizeof计算写入此对象的大小,注意需要先创建一个对象在进行写入
读的步骤也差不多,但是在第四步 读的时候需要利用read函数 如:ifs.read((char *)&p,sizeof(p))
sizeof计算的是读的大小,也必须先有一个对象,对此对象进行读和写
#include<iostream>
#include<fstream>
int main()
{
ofstream ofs;
ofs.open ("person.txt",ios :: out | ios :: binary);
//写的时候利用write函数
//首先需要创建对象写内容
Person p("zhangsan",20);
ofs.write((const char *) &p,sizeof(p)); //利用write函数写入
ofs.close
ifstream ifs;
ifs.open("person.txt",ios :: in | ios :: binary);
Person p;
ifs.read((char *) &p,sizeof(p)); //利用read函数读取对象种的内容
cout << p << endl; //需要利用左移运算符重载
ifs.close();
return 0;
}