面向对象新需求及C++解决方案
#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()
{
//this->AttackPower()
return 20;
}
protected:
private:
};
//这个类产生的时间
class HeroAdv3Fighter : public HeroFighter
{
public:
int AttackPower()
{
//this->AttackPower()
return 40;
}
protected:
private:
};
//我写了一个框架 这个框架产生的时间
//一个模型 如果你把这个函数做成框架那。。。。。。。
void ObjFighter(HeroFighter *pBase, EnemyFighter *pEnemy)
{
if (pBase->AttackPower() > pEnemy->DestoryPower())
{
printf("主角win\n");
}
else
{
printf("主角挂了\n");
}
}
//面向对象三大概念
//封装 突破了c函数的概念
//继承 可以使用原来的代码 代码复用
//多态 比代码复用更高一个层次(可以调用未来)
//
//间接赋值成立的3个条件
/*
1 定义两个变量 1个实参1个形参
2、建立关联 实参取地址传给 形参
3、*p(实参的地址) 间接修改实参的值
*/
/*
多态成立的三个条件
1 要有继承
2 要有函数重写(虚)
3、父类指针(父类引用)指向子类对象
*/
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;
HeroAdv3Fighter hAdv3F;
ObjFighter(&h1, &e1);
ObjFighter(&hAdvF, &e1);
ObjFighter(&hAdv3F, &e1);
system("pause");
}
知识总结: --------重写 重载
函数重载:
必须在同一个类中
子类无法重载父类的函数,父类同名函数将被名称覆盖
重载是在编译期间根据参数类型和个数决定函数调用
函数重写:
必须发生于子类和父类之间
并且父类与子类中的函数必须有完全相同的原型
使用virtural声明之后能够产生多态(如果不使用virtual,叫重定义)
多态是在运行期间根据具体对象的类型决定函数调用
#include "iostream"
using namespace std;
class parent
{
public:
void func()
{
cout << "parent func()" << endl;
}
void func(int a, int b)
{
cout << "parent func(a,b)" << endl;
}
virtual void func(int a, int b, int c)
{
cout << "parent func(a,b,c)" << endl;
}
protected:
};
<span style="color:#FF0000;">//重写:父类和子类之间,函数三要素(函数名、函数参数、函数返回类型)完全一样
//重写分为两种:
//1. 如果父类有virtual关键字,这种父子之间的关系叫虚函数重写,这种情况发生多态(动态联编 迟绑定)
//2. 如果父类没有virtual关键字,这种父子之间的关系叫重定义(静态联编)</span>
class child1 :public parent
{
public:
void func()
{
cout << "child1 func()" << endl;
}
//不加virtual是重写
void func(int a, int b)
{
cout << "child1 func(a,b)" << endl;
}
//virtual这是多态
virtual void func(int a, int b, int c)
{
cout << "child1 func(a,b,c)" << endl;
}
private:
};
int rundunc(parent *pbase)
{
cout << "pbase: ";
pbase->func(1, 2, 3) ;
return 0;
}
void main()
{
parent p;
//重载:在同一个函数里面,函数名相同,形参不同
p.func();
p.func(1, 2);
p.func(1, 2, 3);
child1 c;
c.func();
c.func(1, 2);
c.func(1, 2, 3);
cout << "*********************" << endl;
//子类和父类都有func(),如何让子类调用父类的func()呢?
child1 c1;
c1.func();
//在函数之前加上限制符
c1.parent::func();
//多态
rundunc(&p);
rundunc(&c1);
system("pause");
}
多态的实现效果
多态:同样的调用语句有多种不同的表现形态;
多态实现的三个条件
有继承、有virtual重写、有父类指针(引用)指向子类对象。
多态的C++实现
virtual关键字,告诉编译器这个函数要支持多态;不要根据指针类型判断如何调用;而是要根据指针所指向的实际对象类型来判断如何调用
多态的理论基础
动态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。
多态的重要意义
设计模式的基础。
实现多态的理论基础
函数指针做函数参数
C++中多态的实现原理
当类中声明虚函数时,编译器会在类中生成一个虚函数表
虚函数表是一个存储类成员函数指针的数据结构
虚函数表是由编译器自动生成与维护的
virtual成员函数会被编译器放入虚函数表中
存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)
vptr指针的初始化过程是分步完成的:
1. 当指向父类的构造函数的时候
C++编译器会初始化子类的vptr指针,让vptr指针指向父类的虚函数表;
如果你在父类的构造函数里面调用虚函数表print();
2. 当父类的构造函数执行完毕以后,再执行子类的构造函数,这个时候,让vptr指针真正的指向子类的虚函数表;
提个问题:构造函数中能调用虚函数,能实现多态吗?为什么?
下面看代码
#include "iostream"
using namespace std;
class parent
{
public:
int i;
int j;
public:
virtual void func()
{
cout << "parent func()" << endl;
}
};
class child1 :public parent
{
public:
//int k; //#1 注意这里!!!
public:
child1(int a, int b)
{
cout << "child1 构造函数" << endl;
}
void func()
{
cout << "child1 func()" << endl;
}
};
void main()
{
int i;
parent *p = NULL;
child1 *c = NULL;
child1 cchar[3] = { child1(1, 2), child1(3, 4), child1(5, 6) };
p = cchar;
c = cchar;
p->func();
c->func();
p++;
c++;
p->func(); //#1 取消注释,这里出错
c->func();
system("pause");
}
为什么#1不注释就不出错呢??????
首先分析以下内存模型图
这回明白了吧!!!如果子类中的成员变量完全是父类中的成员变量,不会出问题,如果子类成员变量中有父类没有的成员变量,那么子类的成员变量就会在原有的内存中加上自有的成员变量,这就导致父类和子类的内存空间发生不一致,当++的时候,父类和子类的步长就会发生异常,这将导致p++的头指针发生错误,如上图所示,所以尽量避免这种步长不一致的情景。
接下来是 另一个重要知识点,虚析构函数的多态。
#include "iostream"
using namespace std;
class parent
{
public:
int i;
int j;
public:
parent(int a=0, int b=0)
{
cout << "parent 构造函数" << endl;
}
virtual void func()
{
cout << "parent func()" << endl;
}
~parent()
{
cout << "parent 析构函数" << endl;
}
};
class child :public parent
{
public:
//int k; //#1 注意这里!!!
public:
child(int a, int b)
{
cout << "child 构造函数" << endl;
}
~child()
{
cout << "child 析构函数" << endl;
}
void func()
{
cout << "child func()" << endl;
}
};
void main()
{
child *c = new child(1, 2);
c->func();
delete c;
system("pause");
}
这时候是没有任何问题的。但是如果改成下面的代码:
#include "iostream"
using namespace std;
class parent
{
public:
int i;
int j;
public:
parent(int a=0, int b=0)
{
cout << "parent 构造函数" << endl;
}
virtual void func()
{
cout << "parent func()" << endl;
}
~parent()
{
cout << "parent 析构函数" << endl;
}
};
class child :public parent
{
public:
//int k; //#1 注意这里!!!
public:
child(int a, int b)
{
cout << "child 构造函数" << endl;
}
~child()
{
cout << "child 析构函数" << endl;
}
void func()
{
cout << "child func()" << endl;
}
};
<span style="color:#FF0000;">void deletefunc(parent *pBase)
{
delete pBase;
}</span>
void main()
{
child *c = new child(1, 2);
c->func();
//delete c;
<span style="color:#FF0000;">deletefunc(c);</span>
system("pause");
}
这时候发现子类的析构函数没有执行,这就需要我们采用多态的形式进行处理:
virtual ~parent()
{
cout << "parent 析构函数" << endl;
}
以上总结:
1. 重载是同一个类中,函数参数和个数不同,是静态联编;
2. 重写是,父类没有virtual,是重定义,是静态链表;
父类有virtual,会发生多态,是动态联编或迟绑定;
3.多态的原理!!!!