总结:
1、在父类中申明虚函数时,一般情况下在子类中也申明(便于读代码)
一、赋值兼容
赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。
赋值兼容是一种默认行为,不需要任何的显示的转化步骤。
赋值兼容规则中所指的替代包括以下的情况:
派生类的对象可以赋值给基类对象。
派生类的对象可以初始化基类的引用。
派生类对象的地址可以赋给指向基类的指针。
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
二、多态
C++中所谓的多态(polymorphism)是指,由继承而产生的相关的不同的类,其对象对同一消息会作出不同的响应。
如果父类指针指向的是父类对象则调用父类中定义的函数;
如果父类指针指向的是子类对象则调用子类中定义的重写函数;
1解决方案:
C++中通过virtual关键字对多态进行支持
使用virtual声明的函数被重写后即可展现多态特性
将需要表现多态的父类中的函数声明为虚函数,即在原有函数前加virtual关键字,并在子类函数中对父类虚函数进行重写
2、多态成立的条件
<1>、要有继承
<2>、要有虚函数重写
<3>、要有父类指针(引用)指向子类对象
3、多态的实现原理
虚函数表和vptr指针
(1)当类中声明虚函数时,编译器会在类中生成一个虚函数表,
虚函数表是一个存储类成员函数指针的数据结构;
虚函数表是由编译器自动生成与维护的;
virtual成员函数会被编译器放入虚函数表中;
(2)存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)。
说明:
1. 通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。
2.出于效率考虑,没有必要将所有成员函数都声明为虚函数.
3.C++编译器,执行run函数,不需要区分是子类对象还是父类对象,而是直接通过p的VPTR指针所指向的对象函数执行即可。
4、验证vptr指针的存在(从多个类继承虚函数就有多个vptr指针)
两个类:类A,类B,都只有数据成员int a,类A中有虚函数,类B中没有。计算sizeof(A) sizeof(B)
32位:sizeof(A) =8 sizeof(B)=4
64位:sizeof(A) =16 sizeof(B)=4 (64位一个指针占8字节,类的内存遵从字节对齐。故对齐后为8+8=16)
5、vptr指针初始化
vptr指针时分步骤初始化
1、调用父类构造函数在调用父类构造器时,会将vptr指针当做父类来处理。此时会临时指向父类的虚函数表
2、处理自己构造函数,此时将子类对象的空间变成子类对象处理,vptr指针就从指向父类的表 变成 指向子类的表
注:不要在构造函数中处理业务。调用虚函数
6、父类指针步长和子类指针步长
父类指针步长为 sizeof(父类)
子类指针步长为 sizeof(子类)
注:当父类指针指向子类对象时,指针加加,为父类指针步长,写一个可能并不是子类对象(父类所占空间小于等于子类空间,父类空间++,可能指针指向的并不是子类的开始地址)
验证vptr指针存在代码示例:
#if 1
#include<iostream>
//http://www.mamicode.com/info-detail-1394321.html
using namespace std;
class Parent1 {
public:virtual void func() {cout << "Parent" << endl;}
private:int a;
};
class Parent2 {
public:virtual void func() {cout << "Parent" << endl;}virtual void func(int a) {cout << "Parent" << endl;}
private:int a;
};
class Parent3 {
public:void func() {cout << "Parent" << endl;}
private:int a;
};
class Parent4 {
public:void func() {cout << "Parent" << endl;}
private:int a;
};void test01() {Parent1 p1;Parent2 p2;Parent3 p3;Parent4 p4;cout << "sizeof(parent1)" << sizeof(p1) << endl; //多出来的字节是vptr指针所占用空间cout << "sizeof(parent2)" << sizeof(p2) << endl;cout << "sizeof(parent3)" << sizeof(p3) << endl;cout << "sizeof(parent4)" << sizeof(p4) << endl;}
//字节对齐
/*
sizeof(parent1)16
sizeof(parent2)16
sizeof(parent3)4
sizeof(parent4)4
*/int main() {test01();return 0;
}
#endif
vptr指针初始化代码示例:
#if 1
#include<iostream>
using namespace std;
//vptr指针时分步骤初始化
//1、调用父类构造函数
//2、处理自己构造函数
//不要在构造函数中处理业务。调用虚函数
class Parent {
public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;print(); //调用父类print还是子类print?父类printfunc();}virtual void print() {cout << "Parent::print()...a="<<this->a << endl;}void func() {cout << "Parent:func" << endl;}
private:int a;
};
class Child :public Parent {
public:Child(int a, int b) :Parent(a) //在调用父类构造器时,会将vptr指针当做父类来处理//此时会临时指向父类的虚函数表{//将子类对象的空间变成子类对象处理//vptr指针就从指向父类的表 变成 指向子类的表cout << "Child(int,int)..." << endl;this->b = b;print();func();Parent::func();}virtual void print() {cout << "Child::print()...b=" <<this->b<< endl;}void func() {cout << "Child:func" << endl;}
private:int b;
};
void test01() {Parent *pp = new Child(10, 20);pp->print(); //发生多态delete pp;
}
/*
Parent(int)...
Parent::print()...a=10
Parent:func
Child(int,int)...
Child::print()...b=20
Child:func
Parent:func
Child::print()...b=20
*/
int main() {test01();return 0;
}
#endif
父类指针步长和子类指针步长示例:
#if 1
#include<iostream>
using namespace std;
#if 0
class Parent {
public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;}virtual void print() {cout << "Parent::print()...a=" << this->a << endl;}public:int a;
};
class Child :public Parent {
public:Child(int a) :Parent(a){cout << "Child(int,int)..." << endl;//print();}virtual void print() {cout << "Child::print()...b=" << this->a << endl;}
};
void test01() {Child array[] = { Child(0),Child(1),Child(2) };for (int i = 0; i < 3; i++){array[i].print();}
}
void test02() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];pp->print();Child *cp = &array[0];cp->print();pp++; //pp+sizeof(Parent)cp++; //cp+sizeof(Child)pp->print();cp->print();
}
void test03() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0],i = 0; i < 3; i++, pp++) {pp->print();}
}
void test04() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0], i = 0; i < 3; i++, pp++) {pp->print();}
}
#endif
//在上面的代码基础上,在Child加int 运行报错 当pp++时报错
//因为 pp++; //pp+sizeof(Parent)
// cp++; //cp+sizeof(Child)
//两个步长不一样。
class Parent {
public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;}virtual void print() {cout << "Parent::print()...a=" << this->a << endl;}public:int a;
};
class Child :public Parent {
public:Child(int a) :Parent(a) {cout << "Child(int,int)..." << endl;//print();}virtual void print() {cout << "Child::print()...b=" << this->a << endl;}
public:int b;
};
void test01() {Child array[] = { Child(0),Child(1),Child(2) };for (int i = 0; i < 3; i++){array[i].print();}
}
/*
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
Child::print()...b=1
Child::print()...b=2
*/
void test02() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];pp->print();Child *cp = &array[0];cp->print();pp++; //pp+sizeof(Parent)cp++; //cp+sizeof(Child)pp->print();cp->print();
}
/*
程序崩溃
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
Child::print()...b=0
*/
void test03() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0], i = 0; i < 3; i++, pp++) {pp->print();}
}
/*
程序崩溃
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
*/
void test04() {Child array[] = { Child(0),Child(1),Child(2) };Child *cp = &array[0];int i = 0;for (cp = &array[0], i = 0; i < 3; i++, cp++) {cp->print();}
}
/*
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
Child::print()...b=1
Child::print()...b=2
*/
int main() {//test01();//test02();test03();//test04();return 0;
}
#endif
代码示例:
1、多态
#if 1
#include<iostream>
#include<string>
using namespace std;
#if 0
class Hero {
public:string kongfu;
public:Hero(string kongfu) {this->kongfu = kongfu;}void fight() {cout << "英雄使出了" << this->kongfu << endl;}
};
class Yuebuqun :public Hero {
public:Yuebuqun(string kongfu):Hero(kongfu){}void fight() {cout << "Yuebuqun使出了" << this->kongfu << endl;}
};class Linghuchong :public Yuebuqun
{
public:Linghuchong(string kongfu) :Yuebuqun(kongfu){}void fight(){cout << "Linghuchong使出了" << this->kongfu << endl;}
};
void fight(Hero *hero) {cout << "调用打人的方法" << endl;hero->fight();//希望传递进来的如果是子类,调用子类的fight//如果传递进来的是父类, 调用父类的fight//这种行为就是 多态行为。
}
void test01() {Hero h1("高招");h1.fight();Yuebuqun y1("y高招");y1.fight();Linghuchong l1("l高招");l1.fight();
}
/*
英雄使出了高招
Yuebuqun使出了y高招
Linghuchong使出了l高招
*/
void test02() {Yuebuqun *y1 = new Yuebuqun("y高招");y1->fight();Linghuchong *l1 = new Linghuchong("l高招");l1->fight();delete y1;delete l1;
}
/*
Yuebuqun使出了y高招
Linghuchong使出了l高招
*/
void test03() {Yuebuqun *y1 = new Yuebuqun("y高招");Linghuchong *l1 = new Linghuchong("l高招");fight(y1);fight(l1);delete y1;delete l1;
}
/*
调用打人的方法
英雄使出了y高招
调用打人的方法
英雄使出了l高招
*/int main() {test01();//test02();//test03();return 0;
}
#endif
class Hero {
public:string kongfu;
public:Hero(string kongfu) {this->kongfu = kongfu;}virtual void fight() {cout << "英雄使出了" << this->kongfu << endl;}
};
class Yuebuqun :public Hero {
public:Yuebuqun(string kongfu) :Hero(kongfu) {}void fight() {cout << "Yuebuqun使出了" << this->kongfu << endl;}
};class Linghuchong :public Yuebuqun
{
public:Linghuchong(string kongfu) :Yuebuqun(kongfu) {}void fight() {cout << "Linghuchong使出了" << this->kongfu << endl;}
};
void fight(Hero *hero) {cout << "调用打人的方法" << endl;hero->fight();//当把父类方法声明为virtual时。可以实现传进来谁,调用谁的方法//希望传递进来的如果是子类,调用子类的fight//如果传递进来的是父类, 调用父类的fight//这种行为就是 多态行为。
}void test04() {Yuebuqun *y1 = new Yuebuqun("y高招");Linghuchong *l1 = new Linghuchong("l高招");Hero *h1 = new Hero("h高招");fight(y1); fight(l1);fight(h1);delete y1;delete l1;delete h1;
}
/*
调用打人的方法
Yuebuqun使出了y高招
调用打人的方法
Linghuchong使出了l高招
调用打人的方法
英雄使出了h高招
*/
int main() {test04();
}#endif
2、多态意义
#if 1
#include<iostream>
using namespace std;
//英雄类
class Hero {
public:Hero() {cout << "dd" << endl;}virtual int getAd() {return 10;}
};
class AdvHero:public Hero {
public:virtual int getAd() { //virtual写不写都ok,写上能加强代码的可读性return 1001;}
};
//怪兽类
class Monster {
public:int getAd() {return 1000;}
};//战斗方法
void playerFight(Hero *hp, Monster *mp) {if (hp->getAd() > mp->getAd()) { //hp->getAd()发生了多态cout << "英雄胜利,怪兽被打死了" << endl;}elsecout << "英雄死亡,怪兽赢了" << endl;
}//2020年
//拓展新东西 不改变之前代码
//多态意义 架构意义
class BugHero :public Hero {
public:virtual int getAd() {cout << "调用BugHero方法" << endl;return 666666;}
};
void test01() {Hero h;Monster m;playerFight(&h, &m);AdvHero advH;playerFight(&advH, &m);BugHero b;playerFight(&b, &m);
}int main() {test01();Hero *h1[6]; //不调用析构函数 没有具体指向//int array[][2];cout << "dddddddsfad" << endl;Hero h[5]; //调用5次构造函数
}
/*
dd
英雄死亡,怪兽赢了
dd
英雄胜利,怪兽被打死了
dd
调用BugHero方法
英雄胜利,怪兽被打死了
dddddddsfad
dd
dd
dd
dd
dd
*/
#endif
多态原因:
#if 0
#include<iostream>
using namespace std;class Parent {
public:Parent(int a) {this->a = a;}virtual void func(int a) {cout << "Parent:func(int).." << endl;}void PrintP(){}
private:int a;
};
class Child :public Parent {
public:Child(int a, int b) :Parent(b) {this->b = b;}virtual void func(int b) {cout << "Child::func(int)" << endl;}void func(int a, int b) {cout << "Child:func(int a,int b)" << endl;}void printC(){}
private:int b;
};void myFunc(Parent *p) {p->func(10);
}
void test01() {Parent *pp = new Parent(10);Parent *cp = new Child(100,200); //父类指针指向子类对象myFunc(pp);cout << "------" << endl;myFunc(cp); cp->PrintP(); //如果调用普通函数,编译器根本不会查找虚函数表//只有你调用的是虚函数,才会去查找虚函数表
}
int main() {test01();return 0;
}
#endif