目录
1.多态的概念
多态:多种形态,不同的对象去完成同一件事,结果不同。
2.多态的定义和实现
2.1多态的构成条件
1.必须通过基类的引用或指针调用虚函数
2.被调用的函数必须是虚函数,且派生类必须对基类虚函数完成重写
2.2虚函数
被virtual修饰的类非静态成员函数
2.3重写(覆盖)
//多态:多种形态
//静态的多态:函数重载,看起来调用同一个函数有不同行为。静态:原理是编译时
//动态的多态:一个父类的引用或指针去调用同一个函数,传递不同的对象,会调用不同的函数。动态:原理是运行时实现
//本质:不同人去做同一件事情,结果不同
class Person
{
public:
virtual void BuyTicket()
{
cout << "买票-全价" << endl;
}
};
class Student : public Person
{
public:
//子类中满足三同(函数名、参数、返回值)(协变除外) 、 虚函数, 叫做 重写(覆盖)
virtual void BuyTicket()
{
cout << "买票-半价" << endl;
}
};
//构成多态,跟p的类型没有关系,传的哪个类型的对象,调用的就是这个类型的虚函数 -- 跟对象有关
//不构成多态,调用的就是p类型的函数 -- 跟类型有关
void Func(Person& p)
{
p.BuyTicket(); //多态
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
重写要求返回值相同有一个例外:协变--要求返回值是父子关系的引用或指针
//重写要求返回值相同有一个例外:协变--要求返回值是父子关系的指针或引用
class A{};
class B : public A{};
class Person
{
public:
virtual A* BuyTicket()
{
cout << "买票-全价" << endl;
return nullptr;
}
};
class Student : public Person
{
public:
virtual B* BuyTicket()
{
cout << "买票-半价" << endl;
return nullptr;
}
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
3.派生类的析构函数
动态申请的父子对象,如果给了父类指针管理,那么需要析构函数是虚函数完成重写,构成多态,那么才能正确调用析构函数。析构函数名被特殊处理了,处理成了destructor。
//析构函数名被特殊处理了,处理成了destructor
class Person
{
public:
virtual ~Person()
{
cout << "~Person()" << endl;
}
};
class Student : public Person
{
public:
virtual ~Student()
{
cout << "~Student()" << endl;
}
};
int main()
{
//普通对象,析构函数是否虚函数,是否完成重写,都正确调用了
//Person p;
//Student s;
//动态申请的父子对象,如果给了父类指针管理,那么需要析构函数是虚函数完成重写,构成多态,那么才能正确调用析构函数
Person* p1 = new Person; //operator new + 构造函数
Person* p2 = new Student;
//析构函数 + operator delete
delete p1; //p1->destructor()
delete p2; //p2->destructor()
return 0;
}
虚函数的重写允许两个都是虚函数或者父类是虚函数,再满足三同,就构成重写
//虚函数的重写允许两个都是虚函数或者父类是虚函数,再满足三同,就构成重写
class Person
{
public:
virtual void BuyTicket() { cout << "买票-全价" << endl;}
virtual ~Person() { cout << "~Person()" << endl;}
};
class Student : public Person
{
//虽然子类没写virtual,但是他是先继承了父类的虚函数的属性,再完成重写。也算虚函数
void BuyTicket() { cout << "买票-半价" << endl;}
public:
~Student() { cout << "~Student()" << endl;}
};
void Func(Person& p)
{
p.BuyTicket();
}
4.override和final
设计一个不能被继承的类 (C++98)
//设计一个不能被继承的类
//C++98
class A
{
private:
A(int a = 0)
:_a(a)
{}
public:
static A CreatOBj(int a = 0)
{
//new A;
return A(a);
}
protected:
int _a;
};
//间接限制,子类构成函数无法调用父类构造函数初始化成员,没办法实例化对象
class B : public A
{};
int main()
{
A aa = A::CreatOBj(10);
return 0;
}
final修饰类 该类不能被继承
//C++11 直接限制
class A final
{
protected:
int _a;
};
//class B : public A
//{
//
//};
final修饰虚函数 该虚函数不能被子类虚函数重写
//C++11 final 修饰虚函数,限制他不能被子类中的虚函数重写
class C
{
public:
virtual void f() final
{
cout << "C::f()" << endl;
}
};
class D : public C
{
public:
//virtual void f() final
//{
// cout << "D::f()" << endl;
//}
};
override:放在子类重写的虚函数后面,检查是否完成重写,没有重写就报错
//override放在子类重写的虚函数的后面,检查是否完成重写
//没有重写就报错
class Car
{
public:
void Drive()
{}
};
class Benz :public Car
{
public:
virtual void Drive() override
{
cout << "Benz-舒适" << endl;
}
};
5.重载、覆盖(重写)、隐藏(重定义)的对比
重载 | 1.两个函数在同一作用域 2.函数名相同,参数不同 |
覆盖(重写) | 1.两个函数分别在基类和派生类作用域 2.函数名,参数,返回值都相同(协变例外) 3.两个函数是虚函数 |
隐藏(重定义) | 1.两个函数分别在基类和派生类作用域 2.函数名相同 3.两个基类和派生类同名的函数,不构成重写就是重定义 |
6.抽象类
在虚函数后面加上=0,这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数要求派生类必须重写,更体现出了接口继承。
class Car
{
public:
//纯虚函数一般只声明,不实现
//实现没有价值
//virtual void Drive() = 0;
virtual void Drive() = 0
{
cout << "virtual void Drive() = 0" << endl;
}
void f()
{
cout << "void f()" << endl;
}
};
int main()
{
Car* p = nullptr;
//p->Drive();
return 0;
}
7.接口继承和实现继承
8.多态的原理
8.1虚函数表
//sizeof(Base)是多少?--12
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
virtual void Func2()
{
cout << "Func2()" << endl;
}
private:
int _b = 1;
char _ch = 'A';
};
int main()
{
cout << sizeof(Base) << endl;
Base bb;
return 0;
}
对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)
class Person{
public:
virtual void BuyTicket(){cout << "买票-全价" << endl;}
void f(){cout << "f()" << endl;}
protected:
int _a = 0;
};
class Student : public Person{
public:
virtual void BuyTicket(){cout << "买票-半价" << endl;}
protected:
int _b = 0;
};
void Func(Person& p)
{
//多态调用,在编译时,不能确定调用的是哪个函数
//运行时,去p指向对象的虚表中找到虚函数的地址
p.BuyTicket();
p.f();
}
//普通函数与虚函数存储的位置是否一样?
//一样,都在代码段。虚函数要把地址存到虚表,方便实现多态
int main()
{
Person Mike;
Func(Mike);
Student Johnson;
Func(Johnson);
Person& r1 = Johnson;
Person p = Johnson;
//Person p1 = Mike;
//Person p2 = Johnson;
不是多态,编译时确定地址
//p1.BuyTicket();
//p2.BuyTicket();
return 0;
}
8.2打印虚函数表中内容
typedef void(*VF_PTR)();
//打印虚函数表中内容
void PrintVFTable(VF_PTR* table)
{
for (int i = 0; table[i] != nullptr; ++i)
{
printf("vft[%d]:%p->", i, table[i]);
VF_PTR f = table[i];
f();
}
cout << endl;
}
typedef void(*VF_PTR)();
//打印虚函数表中内容
void PrintVFTable(VF_PTR* table)
{
for (int i = 0; table[i] != nullptr; ++i)
{
printf("vft[%d]:%p->", i, table[i]);
VF_PTR f = table[i];
f();
}
cout << endl;
}
//单继承
class Base
{
public:
Base()
{
a = 0;
}
virtual void func1(){ cout << "Base::func1" << endl; }
virtual void func2(){ cout << "Base::func2" << endl; }
private:
int a = -1;
};
class Derive :public Base
{
public:
virtual void func1(){ cout << "Derive::func1" << endl; }
virtual void func3(){ cout << "Derive::func3" << endl; }
void fun4(){ cout << "Derive::func4" << endl; }
private:
int b;
};
int main()
{
Base b;
//b.func1();
//#ifdef _WIN64
// PrintVFTable((VF_PTR*)(*(long long*)&b));
//#else
// PrintVFTable((VF_PTR*)(*(int*)&b));
//#endif
//
PrintVFTable((VF_PTR*)(*(void**)&b));
return 0;
}