一:错误理解下的多态
#include <iostream>
using namespace std;
class Parent
{
public:
Parent()
{
cout << "Parent construct" << endl;
}
void overrideFunc()
{
cout << "Parent override func" << endl;
}
};
class Child:public Parent //1.有继承
{
public:
Child()
{
cout << "Child construct" << endl;
}
void overrideFunc() //2.有函数重写
{
cout << "Child override func" << endl;
}
};
void main()
{
Parent* p = new Child(); //3.父类指针指向子类对象
p->overrideFunc(); //4.进行多态调用
system("pause");
}
这里是根据指针类型来判断调用那个函数执行(这是不对的)--->虽然也叫作多态
二:错误讨论 :使用函数来讨论到底出错在哪里?
(一)再次回顾对象的产生(重点)(对于一个含有无参构造方法的类的对象的生成)
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
Test(int a)
{
cout << "Test(int a)" << endl;
}
void getInfo()
{
cout << "use getinfo" << endl;
}
};
void main()
{
Test t; //会调用无参构造函数产生一个对象,若是不存在无参构造函数,则这里编译报错
Test t2(5); //会调用有参构造函数进行产生对象
t.getInfo();
t2.getInfo();
system("pause");
}
重点:Test t3();是错误的!!!--->虽然声明不会报错,使用的时候会出错
原因://warning C4930: “Test t3(void)”: 未调用原型函数(是否是有意用变量定义的?)
会认为该语句表示声明一个名为a的函数,返回类型是Test。
(二)详细错误案例指出《重点了解》
class Parent
{
public:
Parent()
{
cout << "Parent construct" << endl;
}
void overrideFunc()
{
cout << "Parent override func" << endl;
}
};
class Child01:public Parent
{
public:
Child01()
{
cout << "Child01 construct" << endl;
}
void overrideFunc()
{
cout << "Child01 override func" << endl;
}
};
class Child02 :public Parent
{
public:
Child02()
{
cout << "Child02 construct" << endl;
}
void overrideFunc()
{
cout << "Child02 override func" << endl;
}
};
void testMulStatus(Parent* p) //想通过一个父类指针实现对子类对象方法的动态调用
{
p->overrideFunc(); //想实现对象方法的动态调用
}
void main()
{
Child01 c1;
Child02 c2;
testMulStatus(&c1);
testMulStatus(&c2);
system("pause");
}
本意是通过同一个父类指针,实现对不同的子类的方法的调用
三:真正的多态实现《重点》
(一)多态原则
1.要有继承
2.要有virtual函数重写
3.要有父类指针指向子类对象
(二)代码测试
class Parent
{
public:
Parent()
{
cout << "Parent construct" << endl;
}
virtual void overrideFunc()
{
cout << "Parent override func" << endl;
}
};
class Child01:public Parent
{
public:
Child01()
{
cout << "Child01 construct" << endl;
}
virtual void overrideFunc()
{
cout << "Child01 override func" << endl;
}
};
class Child02 :public Parent
{
public:
Child02()
{
cout << "Child02 construct" << endl;
}
virtual void overrideFunc()
{
cout << "Child02 override func" << endl;
}
};
void testMulStatus(Parent* p) //想通过一个父类指针实现对子类对象方法的动态调用
{
p->overrideFunc(); //想实现对象方法的动态调用
}
void main()
{
Child01 c1;
Child02 c2;
testMulStatus(&c1);
testMulStatus(&c2);
system("pause");
}
四:原理探析(虚函数表指针VPtr)
(一)多态中的虚函数表
当类中声明虚函数时,编译器就会在类中生成一个虚函数表
虚函数表是一个存储类成员虚函数指针的数据结构
虚函数表由编译器自动生成和维护
virtual成员函数会被编译器放入虚函数表中
存在虚函数时,每个类中都会有一个指向虚函数表的指针(vptr指针)
注意:虽然定义为虚函数,但是占用空间和原来成员函数大小是一样的,所以在内存上面唯一的区别在于多了一个虚函数指针(大小4字节)
(二)证明虚函数指针的存在
class Test
{
public:
int a;
int b;
public:
Test()
{
}
void getInfo()
{
cout << "getInfo" << endl;
}
void setInfo()
{
cout << "setInfo" << endl;
}
};
class Test02
{
public:
int a;
int b;
public:
Test02()
{
}
virtual void getInfo()
{
cout << "getInfo" << endl;
}
virtual void setInfo()
{
cout << "setInfo" << endl;
}
};
void main()
{
cout << sizeof(Test) << endl;
cout << sizeof(Test02) << endl;
system("pause");
}
无论是一个虚函数,还是两个或者多个虚函数,最终内存大小相差4字节。所以一个成员函数--->虚成员函数是不增加空间大小的。多的那4字节是由于虚函数表指针的增加导致的
(三)多态上虚函数调用步骤
每个对象会含有一个虚函数指针vptr,指向虚函数表,当父类指针调用重写虚函数时:首先会去找vptr,根据vptr去找对应的子类虚函数表,从而找到对应对象的方法进行调用
(四)虚函数和普通函数调用的区别
.若是普通成员函数,编译器可以直接确定被调用成员函数(静态编链)
.若是虚函数:编译器要根据对象的vptr指针,找到虚函数表,找到对应的虚函数进行调用(动态编链)
.所以虚函数的调用效率要低于普通成员函数,所以出于效率,没有必要全部定义为虚函数