多态性:基类指针在运行期间确定类型,并且必须指定基类成员函数为虚函数,然后子类对该成员函数进行重写。
重写:子类的函数名称和其他函数签名都和父类相同,要求函数参数也相同。
注意区分重载:是函数名相同,函数参数不同
override:可以在子类需要重写的成员函数添加override来限定它是重写。
final:禁止这个成员函数被它的子类重写
还有一个很重要的点:无论重写函数的访问修饰符是什么,都由基类的虚函数的访问修饰符决定
虚函数在基类声明时带有默认实参值,通过基类指针访问,总是接收基类版本的实参值。
#include <iostream>
using namespace std;
class Cat
{
public:
Cat() = default;
Cat(int sizeValue) :
size{ sizeValue }
{}
virtual int getSize() {
return size;
}
void showSize() {
cout << getSize() << endl;
}
protected:
int size;
};
class SmallCat :public Cat
{
public:
SmallCat(int sizeValue) :
Cat{ sizeValue }
{}
int getSize() override final{
return 0.5 * size;
}
};
int main()
{
Cat cat1{ 2 };
Cat* p1{&cat1};
SmallCat cat2{ 4 };
Cat* p2{ &cat2 };
p1->showSize(); // 2
p2->showSize(); // 2
return 0;
}
多态集合:
如果不存储为指针或引用,不会存储完整的派生对象。
#include <iostream>
#include <vector>
using namespace std;
class Cat
{
public:
Cat() = default;
Cat(int sizeValue) :
size{ sizeValue }
{}
virtual int getSize() const {
cout << "1111" << endl;
return size;
}
void showSize() const{
cout << getSize() << endl;
}
protected:
int size;
};
class SmallCat :public Cat
{
public:
SmallCat(int sizeValue) :
Cat{ sizeValue }
{}
private:
int getSize() const override final{
cout << "2222" << endl;
return 2 * size;
}
};
int main()
{
cout << "======1=======" << endl;
vector<Cat> cats;
cats.push_back(Cat(1));
cats.push_back(SmallCat(2));
cats.push_back(SmallCat(4));
for (const auto& p : cats) {
p.showSize();
}
cout << "======2=======" << endl;
vector<Cat*> cats2;
Cat cat1 = Cat(1);
SmallCat cat2 = SmallCat(2);
SmallCat cat3 = SmallCat(4);
Cat* p1 = &cat1;
Cat* p2 = &cat2;
Cat* p3 = &cat3;
p1->showSize(); // 2
p2->showSize(); //
p3->showSize();
cats2.push_back(p1);
cats2.push_back(p2);
cats2.push_back(p3);
for (const auto& p : cats2) {
p->showSize();
}
}
指针和类对象之间的转换
可以把派生类对象指针隐式转换成基类指针,但是不能反过来转换。
动态强制转换使用dynamic_cast<>(),目标类型放在<>中
转换失败返回nullptr
Cat* p1 = &cat1;
Cat* p2 = &cat2;
Cat* p3 = &cat3;
SmallCat* p4 = dynamic_cast<SmallCat*>(p1);
if (p4 == nullptr) {
cout << "======null=======" << endl;
}
多态会需要更多的内存
多态会创建一个特殊的指针,指向一个为类创建的函数指针表vtable
可以用typeid打印出动态类型
typeid(类对象).name()
纯虚函数
在声明函数后=0
包含至少一个纯虚函数的类叫抽象类。不能创建实例,只能创建派生类的对象。
接口:只包含纯虚函数,没有成员变量或其他函数的抽象类可以称为接口