继承:实现了代码的可重用性。
类的继承, 是新的类从已有类那里得到已有的特性。 或从已有类产生新类的过程就是类的派生。
原有的类称为基类或父类, 产生的新类称为派生类或子类。派生与继承, 是同一种意义两种称谓。
继承:Is a 的关系 public;
语法:
1、单继承
Class Derive :public Base
{}
2、 多继承
Class Derive:public Base1,public Base2
{}
继承方式与访问权限
继承方式的public、pro、pri 和 子类及子类对象的访问权限public、pro、pri。
继承方式不影响子类成员的访问方式,影响父类成员在子类内的访问权限。
访问:一是在子类内,二是在子类对象内
子类中的访问方式:public、protected、private、inaccess(不可见)
- public 传承接口 间接的传承了数据(protected)。
- protected 传承数据,间接封杀了对外接口(public)。
- private 统杀了数据和接口。
派生类(子类)
构造器
- 初始化顺序的问题:先祖祖祖祖父,后类对象的构造器,最后子类的构造器。析构顺序与此相反,先子类 再类对象 再父类。
- 存在两代以上的构造器,Student派生Graduate,Gra派生Doctor
构造的时候只与父类相关(两代之间解决,不涉及祖类)
***注意赋值方式(因为有私有成员存在,其他方式不可访问)
拷贝构造
1、基类拷贝构造
浅拷贝:等位拷贝,相同的成员进行赋值即可(系统自实现)
2、派生类拷贝构造 ***注意赋值方式***
赋值运算符重载
1、基类
基类 即 简单运算符重载
2、派生类
(1)一是父类的属性的重载,直接用
operator = (another) 重名了,加上作用于Student::
(2) 不能采用 :提前赋值 构造 (原来已有了再构造à深拷贝导致内存泄漏)
类的作用域运算
shadow现象
父类和子类的成员函数重名了,(eg:父类和子类都有 void display () )那么子类成员会将父类成员shadow掉。
这时就需要 基类名+作用域运算符 的方式才能调用基类的同名成员
注意:
重载 overload :同一个作用域 函数名相同 参数列表不同(参数类型、数量、顺序)
shadow :存在于父子类中,只要函数名相同,即可构成shadow(只看名字)
派生类中的友元
由于友元函数并非类成员,因引不能被继承,在某种需求下,可能希望派生类的友元函数能够使用基类中的友元函数。为此可以通过强制类型转换,将派生类的指针或是引用强转为其父类的引用或是指针,然后使用转换后的引用或是指针来调用基类中的友元函数。(满足了子类继承父类的成员函数的需求)
******强转******
#include <iostream>
using namespace std;
class Student
{
friend ostream &operator<<(ostream & out, Student & stu);//重载<<
private:
int a;
int b;
};
ostream &operator<<(ostream & out, Student& stu)//Student的重载实现
{
out<<stu.a<<"--"<<stu.b<<endl;
}
class Graduate:public Student
{
friend ostream &operator<<(ostream & out, Graduate & gra);
private:
int c;
int d;
};
//派生类中的友元函数可以使用几类中的友元函数(强转)?
//将派生类的指针或是引用强转为其父类的引用或是指针,然后使用转换后的引用或是指针来调用基类中的友元函数。
ostream &operator<<(ostream & out, Graduate & gra)
{
// out<<(Student&)gra<<endl;//继承下来的数据的cout调用父类的cout
out<<static_cast<Student&>(gra);//Student& 类型不可变 与Student不同!!!
out<<gra.c<<"**"<<gra.d<<endl;
}
int main()
{
// Student a;
// cout<<a<<endl;
Graduate g;
cout<<g<<endl;
return 0;
}
析构
~~~(1)析构顺序 与构造相反
~~~(2)自己做好自己的事,同级之间析构。子类不用管父类的析构。。
多继承
子类和父类成员重名的时候。子类中的operator = shadow了父类中的operator = 。在子类中只能调用子类的operator =;在完成子类的赋值重载时,不要直接调用operator = ;
可用: 父类::operator = ……(加个前缀 命名空间~)
多继承出现了数据冗余,调用不便。
要解决的问题: 一是实现派生类的数据只有一份。二是数据访问遍历。
多继承之——虚继承
在上述多继承中,多个父类 保留多份数据成员的拷贝, 不仅造成数据冗余,占有较多的存储空间; 还增加了子类及其对象访问的难度。
为此,C++提供一多基类与多继承的机制,实现了在多继承中保存一份数据。(提取父类中的公共成员,统一存储,方便访问)
(1)虚基类:建立一个新(祖父)类来存储多个父类中的共有元素。
(2)虚继承:继承的扩展,解决菱形继承的二义性问题(两个父类中都有,到底访问谁的-->作用域::)
初始化顺序:祖父类-->父类-->子类。
virtual public
(1)解决从父类继承的时候,会有重复的属性造成的数据冗余。
(2)虚继承实现了数据存储一份,但子类可以实现数据遍历。
注意:
(1)使用时父类和祖父类之间用virtual。
子类和父类之间不需虚继承。子类构造的时候需要写入两个父类和祖父类的数据进行构造。
虚继承示例:沙发床(子类)+床、沙发(父类)+描述两者特点(祖父类)
//Headers
//(1)grandfather (祖父类)
class grandfather
{
public:
grandfather(float l,float wi,float we);
void dis();
protected:
float len;
float wid;
float weight;
};
//(2)bed.h
class Bed:virtual public grandfather //注意是虚继承
{
public:
Bed(float l,float wi,float we);
void sleep();
};
//(3)sofa.h
class Sofa:virtual public grandfather
{
public:
Sofa(float l,float wi,float we);
void sofa ();
};
//(4)sofabed (子类)
class SofaBed:public Sofa,public Bed
{
public:
SofaBed(float l,float wi,float we);
};
//cpp文件
//(1)grandfather.cpp
#include "grandfather.h"
#include <iostream>
using namespace std;
grandfather::grandfather(float l, float wi, float we)
:len(l),wid(wi),weight(we)
{
}
void grandfather::dis()
{
cout<<"len = "<<len<<endl;
cout<<"wid = "<<wid<<endl;
cout<<"weight = "<<weight<<endl;
}
//(2)sofa.cpp
#include "sofa.h"
Sofa::Sofa(float l, float wi, float we)
:grandfather(l,wi,we)
{
}
void Sofa::sofa ()
{
cout<<"i am having fun!"<<endl;
}
//(3)bed.cpp
#include "bed.h"
#include<iostream>
using namespace std;
Bed::Bed(float l, float wi, float we)
:grandfather(l,wi,we)
{
}
void Bed::sleep()
{
cout<<"i am sleeping!"<<endl;
}
//(4)sofabed.cpp
#include "sofabed.h"
SofaBed::SofaBed(float l,float wi,float we)
:Bed(l,wi,we),Sofa(l,wi,we),grandfather(l,wi,we) //*****父类、祖父类都要写
{
}
//main
#include <iostream>
#include "sofabed.h"
#include "bed.h"
#include "sofa.h"
#include "grandfather.h"
using namespace std;
int main()
{
Bed b(1,2,3);
b.dis();
cout<<"-------------"<<endl;
SofaBed sb(3,4,5);
sb.dis();
cout<<"-------------"<<endl;
return 0;
}