-基于VS2017编译器环境下
C ++的三大特性:封装,继承,态多下面我们看多态的英文什么:。多态概念:多态从字面的意思就是“多种状态”,但是在C ++中,多态有着更广泛的含义。
从图中,可看出,多态分为静态多态和动态多态,静态多态中,还分有函数重载和泛型编程动态多态就是有虚函数静态多态:静态多态是编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型的转换),可以推断出要调用那个函数,如果有对应的函数,就调用该函数,如果没有就编译错误。动态多态:动态多态需要虚函数,在基类中有虚函数,在派生类中进行虚函数重写。
动态多态的条件:
1.基类中必须包含虚函数,并且派生类一定要对基类中的虚函数进行重写
2.通过基类对象的指针或者引用调用虚函数
下面通过一段简单的代码了解一下多态调用:
class Base
{
public:
virtual void fun1()//虚函数
{
cout << "Base::fun1" << endl;
}
void fun2()//普通函数
{
cout << "Base::fun2" << endl;
}
virtual void fun3()//虚函数
{
cout << "Base::fun3" << endl;
}
};
class Derived :public Base
{
public:
virtual void fun1()//派生类中进行了fun1()重写
{
cout << "Derived::fun1" << endl;
}
virtual void fun2()//虚函数
{
cout << "Derived::fun2" << endl;
}
void fun3()//普通函数
{
cout << "Derived::fun3" << endl;
}
};
void test(Base &b) //基类的指针或者引用传参
{
b.fun1();
b.fun2();
b.fun3();
}
int main()
{
Base b;
Derived d;
test(b); //参数为基类对象
test(d); //参数为派生类对象
return 0;
}
重写是什么?
下面我们通过一个图来理清,继承体系中同名成员函数的关系:
抽象类:
在成员函数(必须为虚函数)的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的雷叫做抽象类(也叫接口类),抽象类不能实例化对象。纯虚函数在派生类中重定义后,派生类才能实例化对象。
class Base
{
public:
virtual void fun1() = 0;
};
int main()
{
Base b;
return 0;
}
总结
派生类重写基类的虚函数实现多态,要求函数名,参数列表,返回值完全相同(协变)
基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性
只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义成虚函数
如果在类外定义虚函数,只能在声明函数时加上virtual关键字,定义时不用加
构造函数不能定义成虚函数,虽然可以将operator=定义为虚函数,最好不要这样做(容易混)
不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为
最好将基类的析构函数声明为虚函数
虚表是所以类对象实例共用的
理解了多态,下面我们来看看多态的调用原理:
为什么派生类对基类的虚函数进行重写后,通过基类对象的指针或者引用,调用该函数时,就可以实现多态?
class Base
{
public:
virtual void fun1()
{
cout << "Base::fun1()" << endl;
}
virtual void fun2()
{
cout << "Base::fun2()" << endl;
}
int _b;
};
int main()
{
Base b;
b._b = 1;
cout << sizeof(b) << endl;
return 0;
}
我们可以理解为,当我们定义类中有虚函数时,编译器会为对象多开辟4个字节作为对象的起始地址,对象的前4个字节存放着一个虚表,虚表的内容是类中定义的虚函数的地址,使我们调用虚函数时可以找到。
不同继承下带有虚函数的对象模型:
单继承:
class Base
{
public:
virtual void fun1()
{
cout << "Base::fun1()" << endl;
}
virtual void fun2()
{
cout << "Base::fun2()" << endl;
}
/*
virtual void fun3()
{
cout << "Base::fun3()" << endl;
}
*/
int _b;
};
class Derived :public Base
{
public:
virtual void fun1()
{
cout << "Derived::fun1()" << endl;
}
virtual void fun2()
{
cout << "Derived::fun2()" << endl;
}
int _d;
};
int main()
{
Derived d;
d._b = 1;
d._d = 2;
cout << "d的大小为"<<sizeof(d) << endl;
return 0;
}
我们可以看到,对象的前4个字节放的依旧是一个指针,指针指到的地方是一个虚表,由于派生类中的虚函数是对基类中虚函数的重写,所以可以看到虚表中放的是派生类虚函数的地址。即模型为:
多继承: