虚函数,虚表,虚表指针 (great explanation!!!)
基类指针指向基类对象
#include<iostream>
using namespace std;
class A{
public:
void f1(){
cout<<"A f1()"<<endl;
}
virtual void f2(){
cout<<"A f2()"<<endl;
}
virtual void f3(){
cout<<"A f3()"<<endl;
}
virtual void f4(){
cout<<"A f4()"<<endl;
}
};
class B:public A{
public:
void f1(){
cout<<"B f1()"<<endl;
}
void f2(){
cout<<"B f2()"<<endl;
}
void f4(int x){
cout<<"B f4()"<<endl;
}
};
int main(){
A *p, o1;
p=&o1;
p->f1(); //A f1()
p->f2(); //A f2()
p->f3(); //A f3()
p->f4(); //A f4()
// p->f4(5); // 报错,因为o1对象没有形如f4(int)的函数。no matching function for call to 'A::f4(int)'
}
基类指针指向派生类对象
知识点:静态绑定,动态绑定(下的多态特性)
#include<iostream>
using namespace std;
class A{
public:
void f1(){
cout<<"A f1()"<<endl;
}
virtual void f2(){
cout<<"A f2()"<<endl;
}
virtual void f3(){
cout<<"A f3()"<<endl;
}
virtual void f4(){
cout<<"A f4()"<<endl;
}
};
class B:public A{
public:
void f1(){
cout<<"B f1()"<<endl;
}
void f2(){
cout<<"B f2()"<<endl;
}
void f4(int x){
cout<<"B f4()"<<endl;
}
};
int main(){
A *p;
B o2;
p=&o2;
p->f1(); //A f1() 静态绑定,访问的是基类
p->f2(); //B f2() 动态绑定(即:多态特性),访问的是派生类
p->f3(); //A f3() B类中没有f3()的实现,所以只能访问从基类继承而来的 A f3()
p->f4(); //A f4() 同理,B类中没有形如 f4()的函数 (注意:f4()和f4(int)是不同的),所以只能访问从基类继承而来的 A f4()
// p->f4(5); // 报错,因为虽然o2对象有形如f4(int)的函数,但是是基类指针p指向o2,p并没有访问函数f4(int)的权限。no matching function for call to 'A::f4(int)'
}
派生类指针指向派生类对象
知识点:隐藏。在隐藏中,类的函数特性只对函数的名称敏感
#include<iostream>
using namespace std;
class A{
public:
void f1(){
cout<<"A f1()"<<endl;
}
virtual void f2(){
cout<<"A f2()"<<endl;
}
virtual void f3(){
cout<<"A f3()"<<endl;
}
virtual void f4(){
cout<<"A f4()"<<endl;
}
};
class B:public A{
public:
void f1(){
cout<<"B f1()"<<endl;
}
void f2(){
cout<<"B f2()"<<endl;
}
// using A::f4; // 或者添加这句
void f4(int x){
cout<<"B f4()"<<endl;
}
};
int main(){
B *p;
B o2;
p=&o2;
p->f1(); //B f1() 隐藏 基类的f1函数
p->f2(); //B f2() 隐藏 基类的f2函数
p->f3(); //A f3() B类中没有f3()的实现,所以只能访问从基类继承而来的 A f3()
p->A::f4(); //A f4() B类中有2个f4函数(继承的和自己的)。若要调用继承的,则需要指定为A::f4(),或者在类B中添加 using A::f4;
p->f4(5); //B f4(5) 调用自己的f4(int)函数
}
c++父类指针指向子类对象
示例1:
#include <memory>
#include <iostream>
using namespace std;
struct A {
void foo(){
cout<<"foo"<<endl;
}
virtual void bar(){
cout<<"bar"<<endl;
}
A (){
bar();
}
};
struct B:A{
void foo(){
cout<<"b_foo"<<endl;
}
void bar(){
cout<<"b_bar"<<endl;
}
};
int main() {
A* p=new B; // 调用A的构造器, 输出bar
p->foo(); // 静态绑定,输出foo
p->bar(); // 多态特性的动态绑定,输出b_bar
}
示例2:
#include <iostream>
using namespace std;
class A
{
public:
virtual void foo()
{
cout << "A's foo()" << endl;
bar();
}
virtual void bar()
{
cout << "A's bar()" << endl;
}
};
class B: public A
{
public:
void foo()
{
cout << "B's foo()" << endl;
A::foo();
}
void bar()
{
cout << "B's bar()" << endl;
}
};
int main()
{
B bobj;
A *aptr = &bobj;
aptr->foo();
cout<<"********"<<endl;
A aobj = *aptr; //转化为A类对象
aobj.foo();
}
//多态,调用B的foo
B's foo()
//调用A::foo();
A's foo()
//虽然是A::foo()中的bar(); 但传给指针的地址还是bobj的地址,且bar()也用virtual进行多态了。所以是B的bar()
//如果A中的bar函数不是虚函数,这里就调用的是A的bar函数
B's bar()
********
//转化为A的对象后调用A的相关函数
A's foo()
A's bar()
基类函数声明为虚函数
- 当基类指针指向派生类对象时,为了实现完整删除,需要把基类的析构函数设为virtual
- 当派生类指针指向派生类对象时,无需将基类的析构函数设为virtual,也可以实现完整删除
#include<iostream>
using namespace std;
class Base
{
public:
Base(){cout<<"create Base"<<endl;}
~Base(){cout<<"delete Base"<<endl;}
};
class Der : public Base
{
public:
Der(){cout<<"create Der"<<endl;}
~Der(){cout<<"Delete Der"<<endl;}
};
int main(int argc, char const* argv[]) {
Der *b = new Der;
delete b;
return 0;
}
//create Base
//create Der
//Delete Der
//delete Base
重载,隐藏,重写
- 重载:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载。(注意:只需关注同一作用域中函数名称相同,形参不同即可)
void funcA();
和void funcA() const;
也属于重载。- 原因是:在类中,由于隐含的this形参的存在,const版本的function函数使得作为形参的this指针的类型变为指向const对象的指针,而非const版本的使得作为形参的this指针就是正常版本的指针。此处是发生重载的本质
- 调用规则:const对象默认调用const成员函数,非const对象默认调用非const成员函数
void funcB(int a);
和void funcB(const int a);
是非法的,不能通过编译- 这属于非引用传参,形参是否const是等价的
- 当使用引用或指针传参时,有无const是不同的。指向const对象的指针(或引用)和指向非const对象的指针(或引用)做形参的函数是不同的
- 隐藏:不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。(注意:只需关注函数名相同即可)
- 重写:派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。(注意:函数签名必须一致,基类函数是virtual的,基类指针指向派生类对象,才会有多态)
- 重写的特殊情况:协变返回类型
#include<iostream>
using namespace std;
void func() {
cout << "global function"<< endl;
}
class A {
public:
void func() {
cout << "A function" << endl;
}
virtual void polyFunc(){
cout <<"A Polymorphism_多态" <<endl;
}
};
class B : public A {
public:
void func() {
cout << "B function" << endl;
}
void polyFunc(){
cout <<"B Polymorphism_多态" <<endl;
}
};
int main() {
A *pa=new B;
B *pb=new B;
// 隐藏了全局函数func,调用A的func函数
pa->func(); // 结果为:A function
// 隐藏了全局函数func 和 A的func函数,调用B的func函数
pb->func(); // 结果为:B function
// 类A中的polyFunc()是虚函数,可以实现多态,因此动态绑定后调用类B的polyFunc()函数
pa->polyFunc(); // 结果为B Polymorphism_多态
}