C++ 的基础概念(3)——多态详解。

最近两次面试都问到了多态,我也不得不重视起来了,最近最大的收获就是:基础知识很重要,就算你很会写代码,但是面试官问你基础知识答不上来的话,也很难被人赏识和录用,所以还是要多补补基础概念,这一篇就说多态。

之前第一篇提到过,多态是指同样的消息被不同的对象接受时导致不同的行为。分四类:重载多态,强制多态,包含多态参数多态

多态从实现的角度分为 编译时多态 运行时多态。不同处就是确定操作针对的具体对象的时间是编译的时候还是运行的时候。

重载多态:

我们知道的普通函数及类的成员函数的重载都属于重载多态。

函数的重载因为很常用相信一般都很熟悉了,就是指函数名相同而形参的个数和类型不同,编译器调用的时候根据参数类型个数来判断调用哪一个函数。

运算符的重载也是重载多态,比如我们定义了一个类Counter,希望这个类内部可以实现'+'的运算,比如Counter  a,b,c; 初始化后可以有c=a+b;或类似的运算,那么我们需要对‘+’进行重载,给它以运算规则,来保证这句话能通过编译。

(有5个运算符不可以被重载,分别是: .  .*   ::   sizeof    ?:)

运算符重载分两种:作为成员函数重载  作为友元函数重载

语法形式为:

           作为成员函数重载运算符:                            作为友元函数重载运算符:

函数类型 operator 运算符(形参表){

      ............;

}

  friend  函数类型 operator 运算符(形参表){           

         ............;

 }

 

 

 

 

 

之前也说过,友元函数其实是定义在函数外的,所以跟函数成员的区别就是友元函数需要两个形参作为运算符前后的值。

比如我要重载上面的Counter 类型的+ - 运算: 

Counter operator +(Counter c2){

return Counter(this.number+c2.number);

}

//或者友元重载减号:

friend Counter operator -(Counter c1, Counter c2){

return Counter(c1.number-c2.number);

}
//调用时:

Counter a(5),b(3),c,d;
c=a+b;
d=a-b;
//都是可以通过的。

 

/


除了这种加减等常用的外,还有比如-- ++这种前置或后置运算符,比如我们要定义一个counter++; 那么运算符重载为Counter的成员函数,同时函数要带有一个整数形参(int),它的作用就是区别说明它是后置的++ --。如果是前置,则没有int形参。如下:

//前置++的重载
Counter Counter::operator ++(){
.........;
}

//后置++的重载

Counter Counter::operator ++(int){
...........;
}

//这样,当出现++符号时,系统就可以根据位置知道调用哪个函数了:

Counter a(5);
a++; // 相当于调用了 operator ++(0);
++a; // 相当于调用了 operator ++();

强制多态:

就是指讲一个变元的类型加以变化,符合一个函数或者操作的具体要求。

比如加法符号,在进行整型和浮点型的运算时,会先强制将整型转为浮点型,然后再进行运算。

 

包含多态:

包含多态是研究类族中定义于不同类中的同名函数成员的多态行为,主要通过虚函数来实现.

虚函数必须是非静态成员,经过多次派生之后,族类中可以实现运行过程中的多态。

一般虚函数成员声明语法:

virtual 函数类型 函数名(形参表){

      .......................;

}

  虚函数的声明只能在类定义中的函数原型声明时就写清楚,而不能在写函数体时才声明。运行过程中要满足三个规则:

1. 类之间要满足类型兼容规则。

2. 声明时虚函数。

3*. 要由成员函数来调用,或者是通过指针,引用来访问虚函数。

 理论了解了之后,我们写个小例子就懂了。

 以下是完整代码:

虚函数的使用
#include <iostream>
using namespace std;
class Father{
public:
virtual void outputTest(){
cout<<"Father"<<endl;
}

};
class Son1:public Father{
public:
void outputTest(){
cout<<"Son1"<<endl;
}

};
class Son2:public Son1{
public:
void outputTest(){
cout<<"Son2"<<endl;
}

};
// 调用时
int main(){
Father f,*p;
Son1 s1;
Son2 s2;
p=&f;
p->outputTest();
p=&s1;
p->outputTest();
p=&s2;
p->outputTest();
}

 话不多说,我们截图为证:

  可见,通过使用virtual这个关键词,我们实现了outputTest这个函数的包含多态。这三个类中,outputTest都是虚函数。

添一句话:如果子类中的一个函数fun,跟父类中的一个虚函数fun,函数名相同,参数表完全一致,返回值也相同,那么这函数就自动被判定为了虚函数。

虚析构函数:它的存在时为了防止某些情况下的空间泄露。所以虚构造函数是不允许存在的。同时,如果一个父类的析构函数是虚函数,那么子类的析构也同样是虚函数。

下面就说说空间泄露的情况:假如我定义了一个父类Father,里面没有数据成员,然后我定义了一个子类Son公有继承父类,并且有一个私有数据成员*int t; Son的构造函数里有 t=new int(0);,析构函数里会delete t;,但是父类自然没有这一句。这样我们在定义对象的时候如果有这样的情况: Father *f=new Son();  delete *f; 那么,啊~非常不意外的,可怜的t就被这样无情的丢在了角落,不使用也不释放。这样如果在一个较大成程序里发生的话,很可能会出现内存不足的情况。

解决的办法,就是在Father的析构函数前面加virtual,具体形式为: virtual ~Father(); 因为很简单,就再给一次代码好了,三分钟搞定:

虚析构函数代码
#include <iostream>
using namespace std;
class Father{
public:
Father(){};
virtual ~Father(){cout<<"Father delete;"<<endl;};

};
class Son:public Father{
public:
Son(){t=new int(0);};
~Son(){delete t; cout<<"Son delete;"<<endl;};
private:
int *t;

};
// 调用时
int main(){
Father *f=new Son();
delete f;
}

运行结果:

可见,子类的析构函数被调用了,这样就避免了内存泄露带来的危害。所以虚析构还是很有用的~!

除了上述两种虚函数之外,我们还应该知道一种类 叫做:抽象类

抽象类处于类的上一层,它本身无法被实例化,只能通过继承机制,由抽象类生成非抽象的派生类,再对之实例化

注意:带有纯虚函数的类就是抽象类。但是什么是纯虚函数呢?

纯虚函数就是在一个基类中声明的虚函数,它在这个基类里面没有具体要操作的内容,它存在的意义就是为了让子类根据自己的需求对这个函数实现不同的接口。

纯虚类的定义语法是: virtual 函数返回类型 函数名(参数表)=0;  其实就是有了一个‘=0’。这样就不需要给它定义函数体了,所以纯虚函数是没有函数体的。

 

参数多态:

参数多态,就是将程序所处理对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。

参数的多态,可以通过函数模板类模板实现。

所谓函数模板,我们可以想一个例子:我们通常使用math.h里的很多函数比如绝对值函数abs()的时候,参数可以有int, double等不用类型的参数,那么如果为了每一种类型建立一个重载函数的话,就太麻烦了,因为函数体本身是相同的,所以我们就用一个模板来代替之,如下:

template <typename T>
T abs(T x){
return x<0?-x:x;
}

这样,我们在调用的时候,如果给的参数是int,那么编译器就会把typename里的T变为int,这个函数就是个返回int,参数为int的函数了,double等类型也一样。

函数模板的语法定义:

template <typename T> 或 template <class T>    //表示T是一个类型名或者类名,可以更换为int 、 double、 某个类 等等

返回类型  函数名(参数表){       

       .........;

}(注意返回类型不一定为T的~。)

 

 

 

 

 

 

 

类模板:使用类模板可以为类声明一种模式,是的类中某些数据成员、某些成员函数的参数、某些成员函数的返回值能取任意类型。

类模板的语法定义: 在类的外部定义成员函数的函数体时:

 template <模板参数表>

class 类名{

      ...........;

}

 template <模板参数表>

返回类型 类名<模板参数表成员>::函数名(参数表){

       ........;

}

 

 

 

 

 

 

同样,我们用一个小例子,一秒搞懂类模板:

#include <iostream>
using namespace std;

template<class Input, class Output>
class Test{
public:
Test(Input in){input=in;}
Output fun();

private:
Input input;
Output output;
};
// 类之外定义函数时的格式:
template<class Input, class Output>
Output Test<Input,Output>::fun(){
return input>0?input:-input;
}



int main(){
Test<int,int> test(-30);
cout<<test.fun()<<endl;
}

查看输出:

 成功~! 呵呵 虽然没有含金量,但是该用到的格式这个小程序都用到了,不错吧~



转载于:https://www.cnblogs.com/jiaozihardworking/archive/2012/02/18/2356590.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/260970.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET Forms身份验证

.NET表单身份验证 ASP.NET Forms 身份验证的简单实现&#xff1a;1&#xff09;在Web.config文件中配置应用程序使用 Forms 身份验证&#xff1b;2&#xff09;创建登陆页面&#xff0c;将用户身份验证票证添加到Cookie集合。1.配置文件中设置为Forms验证<authentication mo…

计算机对口升学可以报考的学校,对口升学可以报考的学校都在这里,赶快来收藏吧...

对口升学指对口高考&#xff0c;在平常又叫对口单招&#xff0c;对口升学&#xff0c;是从中等职业学校毕业生招生&#xff0c;强调中等职业学校毕业生对口升高职的专业技能考试&#xff0c;以专业技能成绩为主要录取依据的招生办法。报名条件具有正式学籍的中等职业学校毕业生…

Visual Studio 常用快捷键 (二)

想不到上一篇 【Visual Studio 常用快捷键】 受这么多人的欢迎。看来大家对Visual Studio的用法非常感兴趣。 接下来我准备写一个 “Visual Studio使用技巧 ” 一个系列的博客。 希望对大家有所帮助 本篇继续介绍几个常用的快捷键 阅读目录 按两下Tab键回退到光标的上一次位置…

企业邮箱收发信息服务器怎么设置,网易闪电邮企业邮箱收发设置教程(IMAP)...

网易闪电邮企业邮箱收发设置教程(IMAP)发表时间 2019-05-30人气 73(1)启动闪电邮后&#xff0c;点击“邮箱---新建邮箱账户”菜单&#xff0c;如下图&#xff1a;(2)输入“邮箱地址和密码”&#xff0c;点击下拉箭头继续设置&#xff0c;设置完成后点击下一步&#xff0c;如下图…

订餐系统之Excel批量导入

批量导入现在基本已经成为各类系统的标配了&#xff0c;当前&#xff0c;我们订餐系统也不例外&#xff0c;什么商家呀、商品呀、优惠码之类的&#xff0c;都少不了。毕竟嘛&#xff0c;对非开发人员来说&#xff0c;看到Excel肯定比看到很多管理系统还是要亲切很多的。这里&am…

oracle数据库之数据导入问题

2019独角兽企业重金招聘Python工程师标准>>> 在oracle数据库中建立好数据库以后&#xff0c;需要使用PLSQL进行用户创建&#xff0c;打开PLSQL&#xff0c;使用时需要使用最高权限进入PLSQL。如下图&#xff1a; 不需要使用用户名和密码&#xff0c;进入数据库操作。…

win服务器创建文件夹命令,Win10系统如利用命令提示符或WSL创建任意大小空白文件...

要测试网盘或服务器的上传&#xff0c;下载速度&#xff0c;需要指定大小的文件用来测试。创建的空白文件虽然没有任何内容&#xff0c;但是有大小&#xff0c;可以用来测试实际传输速度、覆盖已删除数据等用途。这篇文章是本站教大家在Win10中用命令或WSL创建任意大小空白文件…

计算UILabel带行间距的行高

为什么80%的码农都做不了架构师&#xff1f;>>> //设置lab的行间距 NSMutableAttributedString *attributedString [[NSMutableAttributedString alloc] initWithString:_doubletStr]; NSMutableParagraphStyle *paragraphStyle [[NSMutableParagraphStyle alloc…

关于farpoint公司的控件:SPREAD for .NET Windows Forms Ed.的一些简单方法.

View Code using System;using FarPoint.Win.Spread ;using System.Drawing;using System.Windows.Forms;namespace DFO010{/// <summary>/// Fpspread的相关函数.2006/4/12 制作:Chouka/// </summary> public class Classspd {public Classspd() { }/// <su…

串行总线协议笔记

I2C --INTER-IC串行总线的缩写&#xff0c;是PHILIPS公司推出的芯片间串行传输总线。它以1根串行数据线&#xff08;SDA&#xff09;和1根串行时钟线&#xff08;SCL&#xff09;实 现了双工的同步数据传输。具有接口线少&#xff0c;控制方式简化&#xff0c;器件封装形式小&a…

[置顶] 程序员编程生产力相差10倍意味着什么?

在软件工程研究中&#xff0c;被验证得最多的结论就是对于同等经验的两个不同程序员&#xff0c;在效率和质量上可能会有10倍的差距。研究人员还发现&#xff0c;这种差距也适用于团队级别上&#xff0c;也就是说在同一行业内的不同的团队也是如此。 软件开发中的个人效率的变化…

生成随机长度字符串,比如密码等

2019独角兽企业重金招聘Python工程师标准>>> public function createstr( $length ) {$chars "abcdefghijklmnopqrstuvwxyz0123456789"; $str "";for ( $i 0; $i < $length; $i ) { $str. substr($chars, mt_rand(0, strlen($chars)-1), 1…

css margin属性,css margin属性怎么用?css margin属性用法教程

在css中&#xff0c;有一个重要的属性margin&#xff0c;很多人都不知道css margin属性是什么&#xff1f;怎么用&#xff0c;下面为您总结一下css margin属性用法教程。margin是css用于在一个声明中&#xff0c;对所有css margin属性的简写&#xff0c;正因为margin来控制css中…

gaia引擎分析(二)场景管理

只是粗略的分析原理&#xff0c;大虾轻喷~~ Gaia引擎中没有场景管理器&#xff08;scenemanager&#xff09;这种东西&#xff0c;但是并不是没有场景管理&#xff0c;而是在cGameHost类中有一课场景树进行场景组织、一棵四叉树用来进行剪裁。 class cGameHost class cGameHos…

C++.Templates学习总结归纳1

函数模板 首先我们来看看函数模板&#xff0c;一个函数模板&#xff08;function template&#xff09;代表一族函数&#xff0c;其表现和一般的函数一样&#xff0c;只是其中的某些元素在编写的时候还不知道&#xff0c;也就是说这些还不知道的元素&#xff0c;我们将其参数化…

Xml Tips

Xml Tips//z 2012-3-7 16:43:47 PM IS2120CSDN1. xml 中的注释<!-- 这是注释 -->并非用于 XML 分析器的内容&#xff08;例如与文档结构或编辑有关的说明&#xff09;可以包含在注释中。注释以 <!-- 开头&#xff0c;以 --> 结尾&#xff0c;例如<!--catalog la…

Window7+vs2008+QT环境搭建

记录下自己是如何搭建QT开发环境的&#xff0c;备忘吧。操作系统&#xff1a;win7&#xff0c;其实winXP&#xff0c;win7都没有关系&#xff1b;我使用的机器安装的操作系统是win7&#xff1b;开发环境是VS&#xff0c;使用2005,2008,2010或者即将发布的2011都行&#xff1b;因…

Javascript创建对象的几种方式?

javascript 中常见的创建对象的几种方式&#xff1a; 1. 使用Object构造函数创建&#xff1b; 使用Object构造函数来创建一个对象&#xff0c;下面代码创建了一个person对象&#xff0c;并用两种方式打印出了Name的属性值。 var person new Object(); person.name"kevin&…

使用 RMAN 同步数据库

使用 RMAN 同步数据库使用 RMAN 同步数据库一&#xff0e;概述二 操作步骤(一)&#xff0e;把生产库置为归档模式(二).启动rman做数据库0级备份(三)&#xff0e;修改生产库数据库到未归档(四)&#xff0e;拷贝备份集到测试库(五).在测试库上的操作一&#xff0e;概述 因项目组遇…

js实现图片上传预览及进度条

js实现图片上传预览及进度条 原文js实现图片上传预览及进度条 最近在做图片上传的时候&#xff0c;由于产品设计的比较fashion&#xff0c;上网找了比较久还没有现成的&#xff0c;因此自己做了一个&#xff0c;实现的功能如下&#xff1a; 1&#xff1a;去除浏览器<input …