C++多态总结:多态原理、虚函数指针、重载重写 ,赋值兼容性原则
来源:互联网 发布:网站域名列表 编辑:程序博客网 时间:2024/06/06 00:32
问题引出:赋值兼容性遇上函数重写
#include "iostream"
using namespace std;
//定义一个子类和一个父类
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;
}
void print()
{
cout<<"子类b:"<<b<<endl;
}
protected:
private:
int b;
};
//面向对象新需求
//如果我传一个父类对象,执行父类的print函数
//如果我传一个子类对象,执行子类的printf函数
//现象产生的原因
//赋值兼容性原则遇上函数重写出现的一个现象
//1没有理由报错
//2对被调用函数来讲,
//3在编译器编译期间,我就确定了,这个函数的参数是p,是Parent类型的。。。
//静态链编
void HowToPrint1(Parent *p)
{
p->print(); //一句话,有多种效果,有多种表现形态把。。。//这个功能的就是多态
}
void HowToPrint2(Parent &myp)
{
myp.print();
}
void main()
{
Parent p1;
Child c1;
Parent *p = NULL;
p = &p1;
p->print(); //调用父类的打印函数
//赋值兼容性原则遇上 同名函数的时候
p = &c1;
p->print(); //
HowToPrint1(&p1);
HowToPrint1(&c1);
HowToPrint2(p1);
HowToPrint2(c1);
system("pause");
}
多态的理解
角度1:
p->print(); //一句话,有多种效果,有多种表现形态把。。。//这个功能的就是多态
角度2:
1、面向对象新需求
编译器的做法不是我们期望的
根据实际的对象类型来判断重写函数的调用
如果父类指针指向的是父类对象则调用父类中定义的函数
如果父类指针指向的是子类对象则调用子类中定义的重写函数
2、C++提供的多态解决方案
C++中的多态支持
C++中通过virtual关键字对多态进行支持
使用virtual声明的函数被重写后即可展现多态特性
3、重载、重写、重定义
函数重载
必须在同一个类中进行
子类无法重载父类的函数,父类同名函数将被名称覆盖
重载是在编译期间根据参数类型和个数决定函数调用
函数重写
必须发生于父类与子类之间
并且父类与子类中的函数必须有完全相同的原型
使用virtual声明之后能够产生多态(如果不使用virtual,那叫重定义)
多态是在运行期间根据具体对象的类型决定函数调用
经典易错:
class Parent01
{
public:
Parent01()
{
cout<<"Parent01:printf()..do"<<endl;
}
public:
void func()
{
cout<<"Parent01:void func()"<<endl;
}
void abcd()
{
cout<<"Parent01:void func()"<<endl;
}
virtual 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:
//原因是发生了名称覆盖,把子类中的没有函数参数的,这个函数abcd名称覆盖了
//在子类中,是不能重载父类的函数的。编译器就是这么做的,顺从。。。。
void abcd(int a, int b)
{
cout<<"Parent01:void func()"<<endl;
}
//此处2个参数,和子类func函数是什么关系
void func(int i, int j)
{
cout<<"Child:void func(int i, int j)"<<" "<<i + j<<endl;
}
//此处3个参数的,和子类func函数是什么关系
void func(int i, int j, int k)
{
cout<<"Child:void func(int i, int j, int k)"<<" "<<i + j + k<<endl;
}
};
int main()
{
Parent01 p;
p.func();
p.func(1);
p.func(1, 2);
Child01 c;
c.Parent01::abcd(); //这个函数是从父类中继承而来可以使用。。。
//子类和父类有相同的名字(变量名字或者是函数名字的时,子类名字覆盖父类名字,如果想使用父类的资源,需要加::)
//c.func(); //问题1这个函数是从父类中继承而来,为什么这个地方不能使用
//c.func(1, 2);
system("pause");
return 0;
}
//问题1:child对象继承父类对象的func,请问这句话能运行吗?why
//c.func();
//1子类里面的func无法重载父类里面的func
//2当父类和子类有相同的函数名、变量名出现,发生名称覆盖
//3//c.Parent::func();
//问题2子类的两个func和父类里的三个func函数是什么关系?
4、多态的实现原理以及多态的理解
多态的实现效果
多态:同样的调用语句有多种不同的表现形态;
多态实现的三个条件
有继承、有virtual重写、有父类指针(引用)指向子类对象。
多态的C++实现
virtual关键字,告诉编译器这个函数要支持多态;不要根据指针类型判断如何调用;而是要根据指针所指向的实际对象类型来判断如何调用
多态的理论基础
动态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。
多态的重要意义
设计模式的基础。
实现多态的理论基础
函数指针做函数参数
C++中多态的实现原理
当类中声明虚函数时,编译器会在类中生成一个虚函数表
虚函数表是一个存储类成员函数指针的数据结构
虚函数表是由编译器自动生成与维护的
virtual成员函数会被编译器放入虚函数表中
存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)
说明1:
通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。
说明2:
出于效率考虑,没有必要将所有成员函数都声明为虚函数
5、多态原理研究(证明VPTR指针的存在)
C++编译器内部实现的时候,通过virtual关键字,内部帮我们在父类子类添加了虚函数指针和虚函数表,以下是证明虚函数指针的存在的方法
#include "iostream"
using namespace std;
class AA
{
public:
virtual void print()
{
printf("dddd\n");
}
protected:
private:
int b;
};
void main()
{
printf("AA%d \n", sizeof(AA)); //8,VPTR4字节,b字节
system("pause");
}
6、虚函数表指针(VPTR)被编译器初始化的过程
对象在创建的时,由编译器对VPTR指针进行初始化
只有当对象的构造完全结束后VPTR的指向才最终确定
父类对象的VPTR指向父类虚函数表
子类对象的VPTR指向子类虚函数表
7、为什么要定义虚析构函数
//在父类中声明虚析构函数的原因
//通过父类指针,把所有的子类析构函数都执行一遍。。。
void howtoDel(Parent *pbase)
{
delete pbase;
}
void mainobj()
{
Parent *p1 = new Parent();
p1->print();
delete p1;
}
void main()
{
Child *pc1 = new Child();
howtoDel(pc1);
//mainobj();
system("pause");
}
8、基类和子类对象指针++混搭风
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();
}
int main()
{
int i = 0;
Parent01* p = NULL;
Child01* c = NULL;
//可以使用赋值兼容性原则,是用在多态的地方
//不要轻易通过父类指针p++,来执行函数操作
//问题的本质子类指针和父类指针步长可能不一样。。。
Child01 ca[3] = {Child01(1, 2), Child01(3, 4), Child01(5, 6)};
p = ca; //第一个子类对象赋值给p,p是基类指针,
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;
}
- C++多态总结:多态原理、虚函数指针、重载重写 ,赋值兼容性原则
- C++多态总结:多态原理、虚函数指针、重载重写 ,赋值兼容性原则
- C++(多态实现原理)函数重写,重载,重定义
- 多态问题抛出(赋值兼容性原则遇上父类与子类同名函数的时候)
- 继承与多态 --- 下 --- 重载与重写、多态实现原理、纯虚函数
- 多态重载重写
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- 继承重载重写多态
- 重载,重写,多态例子
- 重载,重写以及多态
- [Objective-C]多态的方式--重写、重载
- 关于C++函数指针、函数回调的相关总结(重载与多态)
- C 语言实现多态的原理:函数指针
- 赋值兼容性原则
- C-指针赋值总结
- java 中的多态,重载,重写
- 重写重载多态,接口与类
- tomcat容器的EOFException异常
- ArcGIS Desktop软件安装和试用说明
- Warning[Pe061]: integer operation result is out of range
- 更轻量的 View Controllers
- 在Spring3中,配置DataSource的方法有五种。
- C++多态总结:多态原理、虚函数指针、重载重写 ,赋值兼容性原则
- Java文件上传类FileUploadUtil.java代码+注释
- 飞机大战中需要注意的细节
- 【hadoop学习】--(2)安装和配置hadoop伪分布式
- 指针数组与数组指针的区别
- oneNote 2013
- 需要掌握的东西
- 散记 delphi使用VC2010编写的COM组件
- Accelerated C++ 学习笔记及题解----第零章