继承与多态

1. JSON
保存当前对象的状态 存储到文件中去
--> 对象持久化
--> 序列化
从文件中恢复对象的状态
--> 反序列化
2. 面向对象的四大特征:
抽象,封装,继承,多态
3. 继承
class Point3D
: public/protected/private Point
{};
三个步骤:
 > 吸收基类的成员
> 改造基类的成员
> 定义自己新的成员
在继承基类时,对基类成员的访问权限:
1. 只有在派生类公有继承基类时,派生类对象才能访问基类的公有成员,其它
 的成员都不能进行访问
2. 对于基类的私有成员,不管派生类以何种方式继承,在派生类内部都不能进行访问
3. 对于基类的非私有成员,不管派生类以何种方式继承,在派生类内部都能进行访问
protected修饰基类成员时,基类对象不能直接访问protected成员;
protected修饰的成员只能交给派生类去使用, 在派生类内部都可以直接访问
> 如果派生类采用public继承,基类的protected成员在派生类内部是protected
> 如果派生类采用private继承,基类的protected成员在派生类内部是private
> 如果派生类采用protected继承,基类的protected成员在派生类内部是protected
C++还支持多重继承
class 派生类
: public/private/protected 基类1
, public/private/protected 基类2
...
{
};
多重继承会带来两种二义性
> 1. 成员名的二义性 --> 直接使用类作用域
> 2. 存储二义性 --> 发生菱形继承 --> 使用虚继承
有哪些是不能继承的:
> 构造函数,析构函数,operator new/delete,operator=,友元
派生类对象的创建总原则是:先创建基类部分,再创建派生类部分
> 如果派生类没有显式定义构造函数,基类必须提供一个默认构造函数
> 如果派生类有显式定义构造函数,则必须要在其构造函数的初始化列表
 中显式调用基类的构造函数
Base Derived
Derived d;
派生类对象的销毁:
> 跟创建的顺序相反
> 先调用派生类的析构函数,然后再去自动调用基类的析构函数
基类与派生类的相互转换 --> 类型适应 --> 只有派生类能适应基类
> 可以把派生类对象赋给基类的引用
> 可以把派生类对象赋给基类的对象
> 可以把基类的指针指向派生类对象 (向上转型)
Base base;
Derived derived;
void func(Derived & rhs);
func(base);// Derived & rhs = base;//Error
void func2(Base & base);
void func3(Base base);
void func4(Base * pbase);
func(derived);//Base & base = derived;//Right 可以把派生类对象赋给基类的引用
func(&derived);//Base * pbase = &derived;
Base * p1 = & derived;
Derived * p2 = (Derived*) p1;(向下转型) 可以
Base * p3 = & base;
Derived * p4 = (Derived*) p3;(向下转型) 不可以
派生类对象的复制控制:
> 派生类没有显式定义复制控制函数, 但是基类部分显式定义了,
基类部分会自动调用复制控制函数
> 派生类也有显式定义复制控制函数, 基类部分显式定义了,
此时,派生类部分执行派生类部分的复制, 但是基类部分不会
再自动调用复制控制函数, 必须手动调用复制控制函数
1. 多态 -- 面向对象设计的精髓
对于同一种行为,不同的对象表现出不同的形态
静态多态: 函数重载,运算符重载 发生的时机是在 编译期
动态多态: 虚函数 发生的时机是在 运行期
虚函数的定义:
virtual 返回值 成员函数名(参数列表);
虚函数实现机制:虚表
一旦一个类定义了一个虚函数,在其对象的
存储空间中就会多一个虚函数指针vfptr,
该虚函数指针指向了一个虚函数表,虚函数表
之中存放的是虚函数的入口地址
虚函数表放哪里? 文字常量区或者是只读段
构造函数可不可以成为虚函数?不可以
虚函数的调用时机:
1. 基类定义了一个虚函数,派生类覆盖了虚函数
2. 使用了基类的指针或者引用指向了一个派生类对象
3. 通过该指针或者引用调用虚函数
(有了虚函数指针之后; 虚函数指针的创建是对象创建完毕之后)
特殊的函数可以成为虚函数:析构函数
一旦类中定义了一个虚函数, 都要把其析构函数也设置为虚函数
这样做之后, 其派生类的析构函数会自动成为虚函数,不管其前面有
没有virtual关键字。
虚函数如果是用对象直接调用的,此时并不表现出虚函数特性
通过普通成名函数进行访问,并且使用this指针,也会表现出虚函数特性
在构造函数和析构函数里面调用虚函数时,采用的是静态联编,并不表现出虚函数特性
纯虚函数:一般是作为接口存在,它的实现要交给派生类来做
virtual void 成员函数名()=0;
定义了纯虚函数的类是一个抽象类,抽象类是不能进行实例化,可以定义其指针
一个抽象类中可以定义多个纯虚函数,其派生类只要有一个纯虚函数没有实现,
该派生类也成为抽象类
定义了protected型的构造函数的类也是抽象类
重载(overload):同一个类,函数名称相同,参数不同
隐藏(oversee):父子类之间,函数名称相同(通过对象进行调用)
覆盖(override):父子类之间,函数名称及参数完全相同,返回值也相同,虚函数
class Base
{
public:
virtual void display()
{
cout << "Base::display()" << endl;
}
virtual ~Base()
{
delete [] _pdata;
}
private:
char * _pdata;
};
class Child : public Base
{
public:
virtual void display()
{
cout << "Child::display()" << endl;
}
~Child()
{
delete [] _pdata2;
}
private:
char * _pdata2;
};
Base * p = new Child;
p->display();//调用的是Child::display()
delete p;
1. 多态 - 虚继承
在VS项目属性添加一个选项 /d1reportSingleClassLayoutB
// 测试一:单个虚继承,不带虚函数
// 虚继承与继承的区别
// 1. 多了一个虚基指针
// 2. 虚基类位于派生类存储空间的最末尾
// 测试二:单个虚继承,带虚函数
// 1.如果派生类没有自己的虚函数,此时派生类对象不会产生
//  虚函数指针
// 2.如果派生类拥有自己的虚函数,此时派生类对象就会产生自己本身的虚函数指针,
// 并且该虚函数指针位于派生类对象存储空间的开始位置
// 测试三:多重继承(带虚函数)
// 1. 每个基类都有自己的虚函数表
// 2. 派生类如果有自己的虚函数,会被加入到第一个虚函数表之中
// 3. 内存布局中, 其基类的布局按照基类被声明时的顺序进行排列
// 4. 派生类会覆盖基类的虚函数,只有第一个虚函数表中存放的是
// 真实的被覆盖的函数的地址;其它的虚函数表中存放的并不是真实的
// 对应的虚函数的地址,而只是一条跳转指令
// 测试四:钻石型虚继承
//虚基指针所指向的虚基表的内容:
// 1. 虚基指针的第一条内容表示的是该虚基指针距离所在的子对象的首地址的偏移
// 2. 虚基指针的第二条内容表示的是该虚基指针距离虚基类子对象的首地址的偏移
1. 多态
动态多态 运行时 虚函数 --> 虚函数表(虚函数的入口地址)(虚函数指针vfptr)
解决菱形继承的问题:
虚继承 --> 虚基表(虚基指针vbptr) -->
测试一: 单个虚继承,不采用虚函数
1. 只要使用虚继承,虚基类子对象的位置位于派生类对象的末尾
2. 派生类对象的存储布局之中会多一个虚基指针(vbptr)
class A
{
private:
int _ia; sizeof(A) = 4;
};
class B : virtual public A
{
private:
int _ib; sizeof(B) = 12;
};
测试二:单个虚继承,采用虚函数
1. 如果派生类没有定义自己新的虚函数,在派生类对象的头部不会产生虚函数指针
2. 如果派生类有定义自己新的虚函数,在派生类对象的头部会产生虚函数指针
class A
{
public:
virtual void func();
private:
int _ia; sizeof(A) = 8;
};
class B : virtual public A
{
public:
virtual void func1();
private:
int _ib; sizeof(B) = 16;
};
测试三:多重继承,采用虚函数
class A1
{
public :
virtual void a();
virtual void b();
virtual void c();
private:
int _ia1; sizeof(A1) = 8
};
class A2
{
public :
virtual void a();
virtual void b();
virtual void c(); sizeof(A2) = 8
private:
int _ia2;
};
class A3
{
public :
virtual void a(); sizeof(A3) = 8
virtual void b();
virtual void c();
private:
int _ia3;
};
class B : public A1, public A2, public A3
{
public:
void a();
void b();
virtual void d();
private:
int _ib;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值