多态原理分析

#include <iostream>

using namespace std;

问题导出

//1 函数重写print

//2赋值兼容性原则(把子类对象赋给父类指针或者引用

//3函数重写遇上赋值兼容性原则

//这个就是面向对象的新需求

//针对这个函数void howToPrintf(Parent *base),我希望是

//如果传来父类对象,那么应该执行父类函数

//如果传来子类对象,那么执行子类函数

但是不管如何调用总是调用父类方法。。。

//多态

//c++编译器给我们提供的多态方案是 虚函数

 

class Parent

{

public:

Parent(int a = 0)

{

this->a = a;

}

virtual void print()

{

cout<<"父类a"<<a<<endl;

}

protected:

private:

 int a;

};

 

class Child : public Parent

{

public:

Child(int b = 0)

{

this->b = b;

}

virtual void print()

{

cout<<"子类b"<<b<<endl;

}

protected:

private:

int b;

};

void howToPrintf(Parent *base)

{

base->print(); //同样一句话,能实现多种功能;有多种形态

}

void howToPrintf2(Parent &base)

{

base.print();

}

void main()

{

Parent  p1; //正常

//p1.print();

Child c1;

//c1.print();//正常

Parent *base = NULL;

base = &p1;

//base->print(); //只调用父类方法

base = &c1;

//base->print(); //没有打印子类的函数,只调用父类方法

// p2 c1的别名 ,是c1本身

Parent &p2 = c1;

//p2.print();//只调用父类方法

 

//函数参数

howToPrintf(&p1); //只调用父类方法

howToPrintf(&c1); //只调用父类方法

system("pause");

}

静态联编:编译时,编译器自动根据指针的类型判断指向什么样的对象

当加上virtual关键字时候,编译器会动态联编(当编译器调用该方法时,如果发现该方法是虚函数,则会进行迟绑定)

多态案例

#include <iostream>

using namespace std;

class HeroFighter

{

public:

virtual int AttackPower()

{

return 10;

}

};

class EnemyFighter

{

public:

int DestoryPower()

{

return 15;

}

};

 

class HeroAdv2Fighter : public HeroFighter

{

public:

int AttackPower()

{

//HeroFighter::AttackPower()*2;

return 20;

}

protected:

private:

};

 

//一个模型 如果你把这个函数做成框架那。。。。。。。

void ObjFighter(HeroFighter *pBase, EnemyFighter *pEnemy)

{

if (pBase->AttackPower() > pEnemy->DestoryPower())

{

printf("主角win\n");

}

else

{

printf("主角挂了\n");

}

}

void main81()

{

HeroFighter h1;

EnemyFighter e1;

HeroAdv2Fighter hAdvF;

 

if (h1.AttackPower() > e1.DestoryPower())

{

printf("主角win\n");

}

else

{

printf("主角挂了\n");

}

if (hAdvF.AttackPower() > e1.DestoryPower())

{

printf("主角win\n");

}

else

{

printf("主角挂了\n");

}

system("pause");

}

void main()

{

HeroFighter h1;

EnemyFighter e1;

HeroAdv2Fighter hAdvF;

ObjFighter(&h1, &e1);

ObjFighter(&hAdvF,  &e1);

system("pause");

}

面向对象三大概念意义

继承:避免重复造轮子,代码复用

封装:防止外界修改类内部属性,安全,突破了面向过程的局限

多态:相同外部接口,不同内部实现,简化代码(配合继承,可以实现调用未来的代码,更适合功能扩展)

间接赋值的三个条件

1 定义两个变量  1个实参1个形参

2 建立关联  实参取地址传给形参

*p (p是实参地址间接修改实参值

动态多态成立三个条件

要有继承

要有重写virtual函数

要有父类指针指向子类

重载(overload)和覆盖(override)都是实现多态的手段其中重载 是静态多态实现,在程序编译时实现覆盖 是动态多态实现,在程序运行时实现。

重载:重载函数定义在全局或某个类中,要求同名但参数个数或类型不同的函数,当在父子关系中出现函数名相同但参数不同的函数,不论是否有virtual,其基类的方法都会被覆盖,调用时需在父类的成员函数前加T::进行区分(T是父类名)

覆盖:1两个函数必须出现在不同域中(如父子类)

      2 函数名 参数 返回值 必须都相同

#include <cstdlib>

#include <iostream>

using namespace std;

//重载只放在在一个类里面 ,在编译期间就确定

class Parent01

{

public:

Parent01()

{

cout<<"Parent01:printf()..do"<<endl;

}

public:

void aaaaa()

{

;

}

void func()

{

cout<<"Parent01:void func()"<<endl;

}

 

 void func(int i)

{

cout<<"Parent:void func(int i)"<<endl;

}

 

 virtual void func(int i, int j)

{

cout<<"Parent:void func(int i, int j)"<<endl;

}

};

//重写 父子类之间,函数三要素(函数名、函数参数、函数返回类型)完全一样 

//重写又分为两种

//如果父类中有virtual关键字,这种父子之间的关系叫做虚函数重写,这种情况下发生多态 (动态链编 迟绑定)

//如果父类中没有virtual关键字,这种父子之间的关系 重定义  (静态链编)

class Child01 : public Parent01

{

 

public:

 

//此处2个参数,和子类func函数是什么关系

void func(int i, int j)

{

cout<<"Child:void func(int i, int j)"<<" "<<i + j<<endl;

}

//我想在子类中重载父类的func函数  ====

//此处3个参数的,和子类func函数是什么关系

void func(int i, int j, int k)

{

cout<<"Child:void func(int i, int j, int k)"<<" "<<i + j + k<<endl;

}

};

 

void run01(Parent01* p)

{

p->func(1, 2);

}

 

int main()

{

Parent01 p;

 

p.func();

p.func(1);

p.func(1, 2);

Child01 c;

//c.func(); //注意1

c.Parent01::func();

//c.aaaaa();

//c.func(1, 2);

run01(&p);

run01(&c);

system("pause");

return 0;

}

//问题1child对象继承父类对象的func,请问这句话能运行吗?why

//c.func(); 

//1子类里面的func无法重载父类里面的func 

//2当父类和子类有相同的函数名、变量名出现,发生名称覆盖

//3//c.Parent::func();

//问题子类的两个func和父类里的三个func函数是什么关系?

类之间的3关系

Has-A包含(组合)A类中有B类作为其数据成员

Use-A:关联,A类部分的使用B类,B类没有做A类的成员,但是做为其方法的形参或者友元

Is-A继承关系具有传递性不具对称性

 

面试题1:请你谈谈对多态的理解?怎么实现的

相同的语句内部实现不同在不同的类中穿梭的时候表现形式不同多态有两种实现形式静态实现(重载)动态实现(重写),多态是函数指针

多态实现原理

1当类中声明虚函数时,编译器会在类中生成一个虚函数表

2虚函数表是一个存储类成员函数指针的数据结构

3虚函数表是由编译器自动生成和维护的

4 Virtual成员函数会被编译器放入虚函数表中

5存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)

如何证明vptr指针存在

每个只带函数不带成员的类sizeof1(函数不占对象内存),给函数加上virtualsizeof变成4,证明存在指针

面试题2:是否可以将类的每个成员函数都声明为虚函数,为什么?

通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多

面试题3:构造函数中能调用虚函数,实现多态吗?why

答:不能,对象在创建的时,由编译器对VPTR指针进行初始化 

只有当对象的构造完全结束后VPTR的指向才最终确定

父类对象的VPTR指向父类虚函数表

子类对象的VPTR指向子类虚函数表

构造函数中调用多态函数,不能实现多态

父子对象指针混搭

将父类或子类放入数组,最好使用数组方法遍历,不要通过指针++来遍历,因为可能步长不一样

#include "iostream"

using namespace std;

//指针也是一种数据类型,指针数据的数据类型是指,它所指的内存空间的数据类型

//最后一点引申 指针的步长内存空间类型来定

class Parent01

{

protected:

int i;

int j;

public:

virtual void f()

{

cout<<"Parent01::f"<<endl;

}

};

class Child01 : public Parent01

{

public:

int k;

public:

Child01(int i, int j)

{

printf("Child01:...do\n");

}

virtual void f()

{

printf("Child01::f()...do\n");

}

};

void howToF(Parent01 *pBase)

{

pBase->f();

}

//指针的步长 在c++领域仍然有效,父类指针的步长和子类指针的步长不一样

//多态是靠迟绑定实现的(vptr+函数指针实现)

int main06()

{

int i = 0;

Parent01* p = NULL;

Child01* c = NULL;

//不要把父类对象还有子类对象同事放在一个数组里面

Child01 ca[3] = {Child01(1, 2), Child01(3, 4), Child01(5, 6)};

//不要用父类指针做赋值指针变量,去遍历一个子类的数组。

p = ca;

c = ca;

p->f();

c->f(); //有多态发生

//  p++;

//  c++;

//  p->f();//有多态发生

//  c->f();

for (i=0; i<3; i++)

{

howToF(&(ca[i]));

}

system("pause");

return 0;

}

为什么需要虚析构函数

问题:当父类析构函数不加virtual,无法表现多态,因此构造时能构造子类和父类,析构时只能析构子类或父类(根据类型判断)

如果你想通过基类析构函数可以释放后面子类的对象南那么要给基类析构函数加上virtual关键字

解决方法给父类析构函数加virtual

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值