目录
概念:
特例:
协变:
final:
override:
抽象类、纯虚函数:
查表:
动态绑定:
概念:
就是多个不同的对象,在完成某种相同动作时,会产生多种不同的状态。
就比如:一个音乐播放软件,会员和普通用户 能听的歌不一样,有些歌只有会员可以听,会员也可以选择听更好的音质。
class A
{
public:
virtual void dunc()
{
cout << "Hello A" << endl;
}
int a;
};
class B : public A
{
public:
virtual void dunc()
{
cout << "Hello B" << endl;
}
int b;
};
class C : public A
{
public:
virtual void dunc()
{
cout << "Hello c" << endl;
}
int c;
};
void func(A* ptr)
{
ptr->dunc();
}
int main()
{
A a;
B b;
C c;
func(&a);
func(&b);
func(&c);
return 0;
}
多态有两个要求:
子类虚函数重写父类的虚函数(重写:函数名+返回值+参数相同、是虚函数)
由父类的指针或者引用去调用虚函数
特例:
协变:
class S
{};
class W :public S
{};
class A
{
public:
virtual S* dunc()
{
S s;
return &s;
}
int a;
};
class B : public A
{
public:
W* dunc()
{
W w;
return &w;
}
int b;
};
只要父子关系的指针和引用做返回值都算 协变
子类虚函数没有写 virtual 父类虚函数写了是没有问题的
class A
{
public:
~A()
{
cout << "delete A" << endl;
}
int a;
};
class B : public A
{
public:
~B()
{
cout << "delete B" << endl;
}
int b;
};
int main()
{
A* x = new A;
delete x;
A* y = new B;
delete y;
return 0;
}
大家觉得这段代码的运行结果是什么
普通调用就是正常的:
x->destructor() + operator delete(x)
y->destructor() + operator delete(y)
你这个变量是什么类型,就调用谁的析构函数。那如果加上virtual呢
class A
{
public:
virtual ~A()
{
cout << "delete A" << endl;
}
int a;
};
class B : public A
{
public:
~B()
{
cout << "delete B" << endl;
}
int b;
};
int main()
{
A* x = new A;
delete x;
A* y = new B;
delete y;
return 0;
}
很明显这里就是一个多态调用了,先delete A 然后在子类的析构函数里面,先析构子类再析构父类 。
经过这也就得出一个结论:如果设计一个类,它可能作为基类的话,就需要给基类的析构函数添加上virtual 这样可以防止子类里面的资源泄露
final:
如果有不想要被继承或者被重写的类或者函数,就可以在类名 和 函数后面加上final
void func() final {}
class A final
{
};
override:
这个是用来放在子类里面去使用的,用于检查重写(函数名 + 参数 + 返回值)的完整性
抽象类、纯虚函数:
纯虚函数:在虚函数后面写上 =0 ,则这个函数为纯虚函数
抽象类:包含纯虚函数的类叫做抽象类
抽象类不能实例出对象。被继承后派生类也不能实例出对象,只有派生类重写虚函数,才能实例化出对象,纯虚函数也表明派生类必须重写,也体现出了接口继承
查表:
一个指针是不知道自己指向的是 派生类切割过来的 还是 指向自己类型 。所以在调用虚函数的时候会去查对应的虚函数表
class A
{
public:
virtual void dunc()
{
cout << "Hello A" << endl;
}
virtual void dunc1()
{
cout << "dunc1" << endl;
}
int a;
};
class B : public A
{
public:
virtual void dunc()
{
cout << "Hello B" << endl;
}
void dunc2()
{
cout << "dunc2" << endl;
}
int b;
};
int main()
{
A X;
B y;
return 0;
}
大家仔细地看这段代码,和这两个变量的内容:
首先,这两个虚函数表的地址是不同的;
A类里面的dunc1 是虚函数但是并没有在B类中重写,依然也是在A类的虚函数表里面的;
B类里面的虚函数表里面的第一个函数已经展现不清楚了,但我们依然知道它是重写后的dunc;
动态绑定:
静态绑定又叫前期绑定,在程序编译期间就确定了程序的行为:静态多态,比如:函数重载
动态绑定又叫后期绑定,在程序运行期间,根据具体拿到的类型确定程序的具体行为:动态多态