沧州青县网站建设,自己怎么做点击量好的网站,精品资料网官方网站,厦门无忧网站建设有限公司本篇要分享的内容是多态#xff0c;以下为本篇目录。
目录
1.多态的概念
2. 多态的定义及实现
3.虚函数
4.C11 override和final
4.1final关键字
4.2override关键字 5.抽象类
5.1抽象类的概念
5.2接口继承和实现继承 1.多态的概念
通俗来说#xff0c;就是多种形态…本篇要分享的内容是多态以下为本篇目录。
目录
1.多态的概念
2. 多态的定义及实现
3.虚函数
4.C11 override和final
4.1final关键字
4.2override关键字 5.抽象类
5.1抽象类的概念
5.2接口继承和实现继承 1.多态的概念
通俗来说就是多种形态具体点就是去完成某个行为当不同的对象去完成时会 产生出不同的状态。
比如旅行景点的成人票是全票儿童半票军人优先购票
又比如拼多多的红包新用户就会获得很多福利而老用户的红包福利只有一点点。
像这样不同的身份可以产生不同的行为和结果也是一种多态行为。
2. 多态的定义及实现
用一段简单的代码来认识多态
#includeiostream
using namespace std;
class Person
{
public:virtual void BuyTicket() { cout 买票-全价 endl;}
};
class Student : public Person
{
public:virtual void BuyTicket(){cout 买票-半价 endl;}
};void Func(Person p)
{p.BuyTicket();
}
int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}
可以看到在多态中引入了一个新的概念 虚函数即在函数前加上virtual。
并且在函数测试中使用父类创建了对象并且调用函数这时要注意我们在main函数中分别用父类和子类创建了对象并调用了Func函数。
在之前的继承中我们知道父类的引用即可传父类对象也可以传子类对象 这时运行代码观察结果 可以看到这里调用了两个函数 普通人买票全价学生买票半价
但是当我们将切片改为传值传参而不是传引用传参时结果就会调用同一个函数 可以看到这里去掉了符号
运行结果如下 可以看到调用到的时一个函数
那这就证明出形成多态的条件
①.虚函数重写
②.父类的指针或者函数去调用虚函数
以上条件只要有一个不满足就会变成普通调用就会去看对象的类型
3.虚函数
在之前的继承中我们学到过虚继承的关键字也是virtual但是和我们今天在多态中所学的虚函数是没有关系的如同取地址符号和引用符号虽然符号相同但是是有区别的。 要构建多态的第一个条件就是构成虚函数重写那么构成虚函数重写也是有条件的
继承关系中两个父子类关系的虚函数函数名、参数、返回值都要相同才能完成虚函数的重写。
当然virtual只能修饰成员函数它的作用是修饰成员函数来构成多态在类外使用当然是会直接报错的。 但是上述的三同函数名、参数、返回值相同又有一个例外,称之为协变返回值可以不同但是必须是父子类关系的指针或引用
如图 可以看到我们修改了函数的返回值是父子关系类的指针结果也会构成多态。
但是在如上的这中情况下子类又可以不加virtual 为什么要这样设计呢
这里的函数需要重写重写就是重复实现也可以认为在父类中的虚函数子类与父类同名的虚函数virtual也会继承下来。
以上的两种特殊用法是需要记忆的是可以使用但是在我们自己编写代码中最好还是使用三同来完成多态。
接下来则是析构函数有如下场景
class Person {
public:~Person() { cout ~Person() endl; }
};
class Student : public Person {
public:~Student() { cout ~Student() endl; }
};
int main()
{Person* p1 new Person;delete p1;Person* p2 new Student;delete p2;return 0;
}
这里的分别在两个类中定义了两个析构函数为方便观察我们输出他们。
再在main函数中分别定义两个指针并且分别指向父类和子类并析构
运行结果如下 可以看到创建的两个指针指向的类不同但是却同时调用了父类的析构函数原因是在没有使用virtual修饰函数的情况下所有的析构函数都被编译器命名为destroy(),所以在两个不同的类中相当与重写了析构函数。
因为子类不能正常调用析构函数所以有可能会造成内存泄漏。 所以我们需要使用虚函数就可以使他们分别调用自己类中的析构函数 子类调用析构函数是先子后父。
4.C11 override和final
在C11中更新了两个关键字final和override
4.1final关键字
1.final修饰类时这个类不能被继承
2.final修饰虚函数时这个虚函数不能被重写
用法如下
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() { cout Benz-舒适 endl; }
};
此处实在Car类中的虚函数后加上final {} 可以看到使用final之后这个虚函数就不能被重写了
并且这个函数必须是虚函数 这里报错很明显final不能修饰非虚函数。 在Car类后加上final之后这个类就不能被继承了 。
4.2override关键字
override用来修饰派生类的虚函数用来检测是否完成重写。
以下是override的使用位置
class Car
{public:virtual void Drive(){}
};class Benz :public Car
{public:virtual void Drive() override {cout Benz-舒适 endl;}
};
如果没有完成重写就会报错 虚函数一定是要重写的否则虚函数是没有意义的。 5.抽象类
5.1抽象类的概念
上面我们提到过虚函数。在虚函数后面加上0则这个函数为纯虚函数
那么此时包含整个纯虚函数的类叫做抽象类也叫做接口类抽象类不能实例化对象。 可以看到上图的Car中有纯虚函数Car就变成了抽象类此时Car也就不能定义对象但是可以定义指针。 上图中我们定义了新的类并且继承了抽象类Car可以看到此时新的类也不能实例化对象了
但是我们可以将纯虚函数重写这样就可以实例化对象了 所以有以下使用场景
class Car
{
public:virtual void Drive() 0;
};class Benz :public Car
{
public:void Drive(){cout Benz endl;}
};class BMW :public Car
{
public:virtual void Drive(){cout BMW-操控 endl;}
};void func(Car* c)
{c-Drive();
}
int main()
{func(new Benz);func(new BMW);
/*Car* pBenz new Benz;
pBenz-Drive();
Car* pBMW new BMW;
pBMW-Drive();*/
}在以上代码中我们发现在func函数中的参数定义了抽象类的指针对象c,利用c去调用类中重写的函数。
同时在main函数中调用函数不难看出指向哪个子类就调用的是哪个子类重写的函数。
所以我们可以得出的结论是抽象类强制了子类去重写。
5.2接口继承和实现继承
普通函数的继承是一种实现继承
虚函数的继承是一种接口继承
用代码说明 首先是实现继承我们在父类中简单写了一个输出函数他会直接继承到子类中也就是说子类可以直接对func函数直接进行调用可以理解为一种函数的复用。
而虚函数的接口继承相当于需要在子类中重写函数的实现但是调用的参数或者说是接口还是父类的接口所以我们才需要重写虚函数。
最后用一道小题来了解接口继承
class A{public:virtual void func(int val 1){ std::coutA- val std::endl;}virtual void test(){ func();}};class B : public A{public:void func(int val0){ std::coutB- val std::endl; }};int main(int argc ,char* argv[]){B*p new B;p-test();return 0;}A: A-0 B: B-1 C: A-1 D: B-0 E: 编译出错 F: 以上都不正确
首先观察main函数中使用B类创建了指针p
使用p去调用testtest在父类中所以test中的this指针是指向A类
在test中又调用了func函数
因为func在A类中被定义为虚函数并且在B类中重写在加上虚函数为接口继承
虚函数只重写函数体内容而接口还是父类的接口
所以答案为B-1
以上就是本篇要分享的关于多态的概念和简单实用本人水平有限尽管不遗余力但本篇的内容仍有不足还请读者指正感谢您的阅读。