许昌网站建设,江苏省网站建设与管理历年自考试题,什么是网络营销?它的内涵包括哪几个层次?,怎么注册网站多态 前言多态的概念多态的定义及实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外协变(基类与派生类虚函数返回值类型不同)析构函数的重写 override和final 虚函数的默认参数 抽象基类 前言
在买火车票的时候#xff0c;如果你是学生#xff0c;是买半价票#… 多态 前言多态的概念多态的定义及实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外协变(基类与派生类虚函数返回值类型不同)析构函数的重写 override和final 虚函数的默认参数 抽象基类 前言
在买火车票的时候如果你是学生是买半价票普通人是全家买票军人买票是优先买票。
多态的概念 多态多种形式具体点就是去完成某个行为当不同的对象去完成时会产生出不同的形态。 多态的定义及实现
多态的构成条件
多态是在不同继承关系的类对象去调用同一函数产生了不同的行为。比如Student继承了Person。Person对象买票全家Student对象买票半价。 必须通过基类的指针或者引用调用虚函数 被调用的函数必须是虚函数切派生类必须对基类的虚函数进行重写 虚函数
继承中提到过虚继承关键字是 virtual这里的虚函数是指被 virtual修饰的类成员函数称为虚函数。
class Person
{
public:virtual void BuyTicket(){cout 买票---全价 endl;}
};虚函数的重写 虚函数的重写(覆盖) 派生类中有一个根据类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型函数名字参数列表完全相同)称子类的虚函数重写了基类的虚函数 class Person
{
public:virtual void BuyTicket() const{cout 买票---全价 endl;}
};class Student : public Person
{
public:virtual void BuyTicket() const{cout 买票---半价 endl;}
};
/*注意在重写基类虚函数时派生类的虚函数在不加virtual关键字时虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范不建议这样使用*/void func(const Person p)
{p.BuyTicket();
}int main()
{func(Student());return 0;
}当虚函数通过指针或者引用调用时我们并不知道该函数真正作用的对象是什么类型因为他可能是一个基类的对象也可能是一个派生类的对象直到运行时才会决定到底执行哪个版本判断依据是引用或指针所绑定的对象的真是类型。 当且仅当对通过指针或引用调用虚函数时才会在运行时解析该调用也只有在这种情况下对象的动态类型才有可能与静态类型不同。 所以上面的结果是 半价 虚函数重写的两个例外
协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时与积累虚函数返回值类型不同。即基类虚函数返回基类本身的指针或引用派生类虚函数返回派生类对象的指针或者引用时称为协变。 我将上面的代码Student中的函数返回值改为int就会产生协变。
如果我将基类的函数和派生类的函数都改成指针类型。 代码就能正常运行。
析构函数的重写
如果基类的析构函数为选好念书此时派生类析构函数只要定义无论是否加 virtual关键字都与基类的析构函数构成重写虽然基类与派生类析构函数名字不同。虽然函数名不相同看起来违背了重写的规则其实不是的这里可以理解为编译器对析构函数的名称做了特殊处理编译后析构函数的名称同一处理称destructor。
class Person
{
public:~Person(){cout ~Person endl;}};class Student : public Person
{
public:~Student(){cout ~Student endl;}};int main()
{Person* p new Person;delete p;p new Student;delete p;return 0;
}执行了两次基类的析构函数因为p的类型是基类的指针类型所以在不重写的情况下只能调用基类的析构函数如果这个时候派生类里面涉及到动态内存等申请资源的函数且最后并没有释放资源就会造成内存泄漏。
override和final
如果把某个函数指定为 final则之后任何尝试覆盖该函数的操作都将引发错误。
class Person
{
public:virtual void BuyTicket() const final{cout 买票---全价 endl;}
};使用 override标记了某个函数但该函数并没有覆盖已存在的虚函数此时编译器将报错。
class B
{
public:virtual void f1(int) const;virtual void f2();void f3();
}class D : B
{
public:void f1(int) const override; 正确f1跟基类中的f1匹配void f2(int) override; 错误没有匹配参数void f3() override; 错误f3不是虚函数void f4() override; 错误B中没有名为f4的函数
}虚函数的默认参数
和其他函数一样虚函数也可以拥有默认参数。如果我们通过基类的引用或指针调用函数则使用基类中定义的默认实参即使实际运行的派生类中的函数版本也是如此。此时传入派生类函数的将是基类函数定义的默认参数。如果派生类函数依赖不同的实参则程序结果将于我们的预期不同。 如果虚函数使用默认参数则基类和派生类中定义的默认实参最好一致。 class A
{
public:virtual void f(int a 1){cout A::f() a endl;}
};class B : public A
{
public:virtual void f(int a 2){cout B::f() a endl;}
};int main()
{B b;A* a b;a-f();return 0;
}输出结果B::f()1
抽象基类
在虚函数的后面写上 0则这个函数为纯虚函数。这样做可以告诉用户当前这个纯虚函数没有实际意思。一个纯虚函数无需定义在函数体的位置写上 0即可。这个 0只能出现在类内部虚函数声明处。
class A
{
public:virtual void f() 0;
};含有纯虚函数的类是抽象基类。抽象基类负责定义接口而后续的其他类可以覆盖该接口。抽象基类不能实例化出对象。派生类继承后也不能实例化出对象只有重写虚函数派生类才能实例化出对象。