目录
1、多态的概念
多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一个消息作出不同的响应。在C++中,多态性通过虚函数和指针/引用实现。
在C++中,实现多态性的关键是使用虚函数。当基类中的成员函数被声明为虚函数时,派生类可以重写(override)这些虚函数,从而实现多态性。通过基类指针或引用指向派生类对象,可以根据实际对象类型调用相应的虚函数。
2、多态如何实现
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
继承构成多态有两个条件:
1. 必须通过基类的指针或者引用调用虚函数。2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
虚函数
class A
{
public:
virtual void test()
{
cout << " a";
}
};
虚函数的重写
class A
{
public:
virtual void test()
{
cout << " a";
}
};
class B:public A
{
public:
virtual void test()
{
cout << " b";
}
};
void Fun(A& a)
{
a.test();
}
int main()
{
A a;
B b;
Fun(a);
Fun(b);
return 0;
}
虚函数重写的两个例外:
class A {};
class B : public A {};
class Person {
public:
virtual A* f() { return new A; }
};
class Student : public Person {
public:
virtual B* f() { return new B; }
};
#include <iostream>
class Base {
public:
virtual ~Base() {
std::cout << "Base class destructor" << std::endl;
}
};
class Derived : public Base {
public:
~Derived() {
std::cout << "Derived class destructor" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
delete ptr; // 输出 "Base class destructor"
return 0;
}
在上面的示例中,Base
类和Derived
类的析构函数名字相同,因此Derived
类的析构函数重写了Base
类的析构函数。在main
函数中,通过Base
类指针指向Derived
类对象,当调用delete
时,会先调用Derived
类的析构函数,然后调用Base
类的析构函数。
c++11中 override与final
C++ 对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug 会得不偿失,因此: C++11 提供了 override 和 final 两个关键字,可以帮助用户检测是否重写。
1、final:修饰虚函数,表示该虚函数不能再被重写
class A
{
public:
virtual void test() final
{
cout << " a";
}
};
class B:public A
{
public:
virtual void test()
{
cout << " b";
}
};
class A
{
public:
virtual void test()
{
cout << " a";
}
};
class B:public A
{
public:
virtual void test(int a) override
{
cout << " b";
}
};
3、重载 、重写、重定义的区别。
1、重载
两个函数在同一作用域
函数名和函数参数都相同
2、重写
两个函数分别在基类和派生类的作用域
函数名、参数、返回值都必须相同(协变例外)
两个函数必须是虚函数
3、重定义
两个函数分别在基类和派生类的作用域
函数名相同
两个基类和派生类的同名函数不构成重写就是重定义
接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。 虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。
class A
{
public:
virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
virtual void test() { func(); }
};
class B : public A
{
public:
void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
B* p = new B;
p->test();
return 0;
}
这段代码的输出结果是
子类(
B
)继承了父类(A
)的接口(即虚函数func
和test
),并且子类可以覆盖父类的虚函数func
来提供自己的实现。在这里,子类B
重写了父类A
中的func
函数,因此调用p->test()
时会输出"B->1"子类B中的func函数重写了父类A中的func函数,但并没有改变函数的参数列表和返回类型,因此这是对函数体的重写。在C++中,子类可以重写父类的虚函数,只需要保持函数参数列表和返回类型一致即可。
4、动态绑定与静态绑定
静态绑定又称为前期绑定 ( 早绑定 ) , 在程序编译期间确定了程序的行为 , 也称为静态多态 , 比如:函数重载动态绑定又称后期绑定 ( 晚绑定 ) ,是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数, 也称为动态多态 。
5、虚函数表
1、单继承中虚函数表
class Base {
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
private:
int a;
};
class Derive :public Base {
public:
virtual void func1() { cout << "Derive::func1" << endl; }
virtual void func3() { cout << "Derive::func3" << endl; }
virtual void func4() { cout << "Derive::func4" << endl; }
private:
int b;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
cout << " 虚表地址>" << vTable << endl;
for (int i = 0; vTable[i] != nullptr; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
VFPTR f = vTable[i];
(*f)();
}
cout << endl;
}
int main()
{
Base b;
Derive d;
VFPTR* vTableb = (VFPTR*)(*(int*)&b);
PrintVTable(vTableb);
VFPTR* vTabled = (VFPTR*)(*(int*)&d);
PrintVTable(vTabled);
return 0;
}
Derive类中fun1构成重写(覆盖)。
2、多继承中的虚函数表
class Base1 {
public:
virtual void func1() { cout << "Base1::func1" << endl; }
virtual void func2() { cout << "Base1::func2" << endl; }
private:
int b1;
};
class Base2 {
public:
virtual void func1() { cout << "Base2::func1" << endl; }
virtual void func2() { cout << "Base2::func2" << endl; }
private:
int b2;
};
class Derive : public Base1, public Base2 {
public:
virtual void func1() { cout << "Derive::func1" << endl; }
virtual void func3() { cout << "Derive::func3" << endl; }
private:
int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
cout << " 虚表地址>" << vTable << endl;
for (int i = 0; vTable[i] != nullptr; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
VFPTR f = vTable[i];
f();
}
cout << endl;
}
int main()
{
Derive d;
VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
PrintVTable(vTableb1);
VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
PrintVTable(vTableb2);
return 0;
}
Derive类中fun1构成重写(覆盖)。多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中