-
2017-09-30 00:26:08
面向对象思想————不看后悔! ————转载自:https://zhidao.baidu.com/question/751474259711412524.html
前言:
整理这份资料的目的是为了帮助朋友们能够更直观的理解面向对象的编程。让后来者能够少走一些弯路。但其中不免有许多漏洞及错误,也还请前辈提出宝贵的更改意见,毕竟交流会让我们不断的进步。
技术是日新月异的,他不会等待你的成长。技术要拿出来于别人交流,自己学是自己主观意识上的理解,有对有错!交流会让进步变得更快。我认为如果计算机 的体系结构不发生革命性的变化,我们现在所应用的程序语言也就百变不离奇踪了!学编程学的是什么?思想!精通一门编程语言(最好是面向对象的语言)后再去 搞其他的编程语言,你会发现过程是如此的行云流水!为什么?你已经把编程的思想掌握了,再去学其他的,无非是学习一种新的语法格式了。
我在这里并不是和你讨论怎么去用C++或JAVA,也不是和你讨论怎么去学他们,我要和你讨论的是怎么去理解面向对象。其中主要会涉及到“类、对象、 继承、属性、方法、静态、重载、隐藏、重构、声明、定义、初始化、赋值等”其中有许多相关技术我只会一代而过,让你有一种到此一游的意味我就达到目的了, 而更详细的技术内幕,就请参考其他相关书籍而深入研究吧!因为我只是在和你探讨如何去更好的理解面向对象!
如何去提高效率?重复使用资源,把别人的东西拿来就用。这是很不错的主意!而对于你来说,最大的资源就是信心以及积极性!好,打起精神来,让我们一同到面向对象的编程中去寻幽访胜吧!
注:文章中所有程序实例我都使用JAVA写的,当然在C++中也就大同小异了了,不同的地方我会指出!
注:文章中的正文文字用黑色,说明文字用蓝色,强调文字用橙色,批改文字用红色!
正文:
1.基本概念:
1.1 类与对象的初探
要我说,无论是面向过程的语言也好,面向对象的语言也罢,我首先要给他讲的都是类和对象!--------“这个世界是由什么组成的?”这个问题如果 让不同的人来回答会得到不同的答案。如果是一个化学家,他也许会告诉你“还用问嘛?这个世界是由分子、原子、离子等等的化学物质组成的”。如果是一个画家 呢?他也许会告诉你,“这个世界是由不同的颜色所组成的”。……呵呵,众说纷纭吧!但如果让一个分类学家来考虑问题就有趣的多了,他会告诉你“这个世界是 由不同类型的物与事所构成的”好!作为面向对象的程序员来说,我们要站在分类学家的角度去考虑问题!是的,这个世界是由动物、植物等组成的。动物又分为单 细胞动物、多细胞动物、哺乳动物等等,哺乳动物又分为人、大象、老虎……就这样的分下去了!
现在,站在抽象的角度,我们给“类”下个定义吧!我的意思是,站在抽象的角度,你回答我“什么是人类?”首先让我们来看看人类所具有的一些特征,这个 特征包括属性(一些参数,数值)以及方法(一些行为,他能干什么!)。每个人都有身高、体重、年龄、血型等等一些属性。人会劳动、人都会直立行走、人都会 用自己的头脑去创造工具等等这些方法!人之所以能区别于其它类型的动物,是因为每个人都具有人这个群体的属性与方法。“人类”只是一个抽象的概念,它仅仅 是一个概念,它是不存在的实体!但是所有具备“人类”这个群体的属性与方法的对象都叫人!这个对象“人”是实际存在的实体!每个人都是人这个群体的一个对 象。老虎为什么不是人?因为它不具备人这个群体的属性与方法,老虎不会直立行走,不会使用工具等等!所以说老虎不是人!
由此可见-------类描述了一组有相同特性(属性)和相同行为(方法)的对象。在程序中,类实际上就是数据类型!例如:整数,小数等等。整数也有 一组特性和行为。面向过程的语言与面相对象的语言的区别就在于,面向过程的语言不允许程序员自己定义数据类型,而只能使用程序中内置的数据类型!而为了模 拟真实世界,为了更好的解决问题,往往我们需要创建解决问题所必需的数据类型!面向对象编程为我们提供了解决方案。
1.2 内置数据类型与函数:
计算机程序在存储数据时必须跟踪3个基本属性为:
1. 信息存储在何处;
2. 存储的值是多少;
3. 存储的信息是什么类型的;
让我们来看看编程语言的内置数据类型都有哪些!(呵呵,这个不大好说,因为每门语言都有自己独特的数据类型,但这毕竟是少数,比如在JAVA中有 byte类型的数据,而在C++中就没有,希望你能举一反三!)比如整数”int ”,浮点类型的数据”float”!字符串”String”,以及数组还有结构体等等。然而在写程序的时候,根据需要我们会创建一个类型的变量或常量,例 如:由于我们需要创建一个整形的变量i为5,我们就可以这样做,int i = 5;而根据需要我很有可能改变i的值,也就是从新给它赋值,比如让它等与6,就可以在所需的地方改成i = 6;由此我们知道,在“值”上可以发生变化的量就叫变量。不会发生变化的量就叫做常量了,在C++中用count关键字来声明,而在JAVA中则使用 final关键字来声明。由于不同语言的声明格式不一样,这里就不做一一介绍了,详细的内容清查阅相关书籍!
在这里我们主要讨论一下函数,我们可以把函数想象成一个“实现某种特定功能的黑匣子”-------这个功能是由你来设定的,举个例子来说:现在我问 你“2+3等于多少”?我相信你能很快的回答我等于5。让我们来分析分析这句话包含什么信息!首先我要把你的大脑想象成是一个黑匣子,我并不知道也没有必 要知道你的大脑是如何工作的(也就是怎么运算的),我关心的只是我传给你的是什么信息?你对信息做了哪些处理? 以及你返回给我的是什么信息?需要提醒你一下的是每个方法都会返回一个信息给调用者的,除了构造函数外(稍候我会作详细的介绍)。我现在需要把自己当作是 一名程序员,而你呢?当然就是计算机了!计算即可没有人那么聪明,它只会按事先约好的特定的格式运行,我想让它具有如上所述的功能,我就要先定义这个黑匣 子!首先我要告诉这个黑匣子会有两个整数值给你(这就是所谓的参数,是程序员需要给黑匣子的信息),然后就要定义这个黑匣子内部实现这两个整数相加的运算 (这就是黑匣子对数据所做的加工,根据需要,你可以做任何的加工。)。最后再标注它返回给我一个同样是整型的数值(这是黑匣子返回给程序员的信息)。一个 函数就这样定义完了,让我们来看看书写格式:
int addnum(int x,int y){
return x+y;
}
具体的含义是这样的:
int /*返回值类型*/ addnum /*方法(黑匣子)名称*/ (int x,int y/*传入的参数*/){
return x+y; /*内部是想方法(实现相加运算,)并用return返回给调用者结果*/
}
首先请注意上明的“return”语句!return 关键字的含义是向调用者返回紧跟在它后面的信息!就像上面一样,因为我问你,你才会回答我,如果我不问你,你就不用回答我的!在计算机中也一样,定义好这 个函数在哪里调用呢?我只能告诉你,哪里需要就在哪里调用!当然,你可以根据需要去更改参数、返回值以及内部实现,具体到如何定义如何调用你只好去参考相 关的资料了!在这里我只是给你一个思想!
有时你会遇到这样的问题,我让你记住,我的年龄是20岁!从字面上理解,你并没有给我返回信息!然而事实上,你确实给我返回了信息,信息的内容是“无信息,也就是无返回值类型void”。具体的程序如下:
int myAge = 0;
int a=20;
void remAge(int a){
myAge=a;
}
具体的函数说明如下:
int myAge =0; //定义并初始化我的年龄为0;
int a=20; /*定义变量a等于20*/
void /*返回值类型为无返回值类型*/ remAge /*函数名称*/(int a /*传入的参数*/){
myAge=a; //内部实现方法,注意,没有return返回!!!
}
关于函数的话题还有很多很多,这里就不一一介绍了,我的目的是让你知道函数是怎么一会事儿!为下面的讨论作铺垫!
1.3 指针以及引用:
指针及引用是在C++中有的,JAVA中没有。JAVA中取消了对内存的操作,随之而来的事也取消了操作符重载的操作。不过在稍候我还是会介绍一些操 作符重载的功能等。引用主要还是用在函数参数的传递上。所以我在这里就不做过多的介绍了。他们很实用,有兴趣的同学可以参阅C++相关书籍。
1.4 运算符及控制语句:
还是自己看看相关书籍吧,这里就不再熬述了!
2.深入探讨面向对象:
2.1“类型”的内部细节:
有了上面的知识,我们现在就可以深入的挖掘类的内部实现了。所有的知识点我都会围绕着类与对象展开,在此之前,我希望你能够确信对以上所介绍的基本内容已完全掌握了!
是的,面向对象的编程语言最大的特色就是可以编写自己所需的数据类型,以更好的解决问题。我想我必须要帮你搞清楚“类,对象,属性,方法它们之间的关 系”!就像我前面所说的,人这个“类”是什么也做不了的,因为“人类”只是一个抽象的概念,它不是实实在在的“东西”,而这个“东西”就是所谓的对象。只 有人这个“对象”才能去工作。而类呢?类是对象的描述!对象从类中产生出来!此时,对象具有类所描述的所有的属性以及方法。-------一定要理解这句 话!!!
也许你已经有些不知所措了,没关系!好好的回味一下,我再举个例子!例如电视机,电视机都有工作原理图,那么什么叫电视机呢?只要它能够实现工作原理图的 所有功能的物体,我们都叫它电视机。你想想是不是这么一回事儿?可是,电视机原理图是不能工作的,也就是这个原理图不能收看节目,只有电视机这个“实体 ——即所谓的对象”才能收看节目,也就是说,从类生成出对象之后才算得上是真正的有意义!才能开始工作。此时,电视机拥有电视原理图所描述的所有的属性及 方法!明白了吧,呵呵!
我先前介绍过,类是属性与方法的集合。而这些属性与方法可以被声明为私有的(private),公共的(public)或是受保护(protected)的,他们描述了对类成员的访问控制。下面我分别做一下介绍:
1. 公共的(public):把变量声明为公共类型的之后,那么就可以通过对象来直接访问,一切都是暴露无遗的!也就是说,你的信用卡密码别人也能够直接得到。
2. 私有的(private):如果把变量声明为私有的情况就好多了,想要得到我的信用卡密码,对象必须要调用专用的方法才能够得到。
3. 受保护的(protected):介绍继承时再讨论。
4. 默认控制访问符(friendly)://JAVA中有而C++中没有。
为了实现数据的封装,提高数据的安全性,我们一般会把类的属性声明为私有的,而把类的方法声明为公共的。这样,对象能够直接调用类中定义的所有方法,当对 象想要修改或得到自己的属性的时候就必须要调用以定义好的专用的方法才能够实现。你想想,你会把你的信用卡密码公布出来嘛?呵呵!所以,我们提倡的是: “对象调方法,方法改属性”;
2.2通过实例看内存分配:
说了这么多,让我们来看一个实例吧!比如:现在我们要编写某家公司员工管理系统,你认为最合适的数据类型是什么?我认为是员工个人!但是在面向过程的 语言中,这样做是不允许的,因为它只能使用语言中的内部数据类型!而员工不在这个内部数据类型之内!也许有人会说可以用C语言中的struct,好注意! 毕竟它是类的基础!如果你以前是一名面C或B的程序员,请你忘掉这些,让我们一起看看如何用类来实现这一切吧!
某家公司的员工是人类的一个特殊群体,它除了具备人类的所有特性与方法外,它还有额外的特性与方法,比如她有她的工资、信用卡密码、作息时间等等,这 些特性以及工作内容,工作量等等这些方法。而在计算机中我们该如何定义这个类呢?下面我将写出它的格式,让你看看在计算机中它是张什么样子的!
/*在此我需要再次声明的是,我用的是JAVA格式,在语法格式上它与C++大不相同!许多细节以及内部操作都有诸多区别,而在思想上确实大同小异的*/
//employee.java
public class employee{
private String name; //员工姓名
private int age; //员工年龄
private char sex; //员工性别
private float emolument; //员工薪水
private boolean lunch; //员工午餐
//……等等
public void heater(){ //这个方法是用来加工员工的午餐
lunch = true;
}
public void setName(String a){ //这个方法是修改员工的姓名
name= a;
}
public String getName(){ //这个方法是得到员工的姓名
return name;
}
//……等等
}
这样我们就定义完了我们所需要的数据类型。现在,让我们来看看它能够干什么以及怎么工作!
我想要做的是,工作室里有一个光杆司令叫“jingwei”,我修改它的名字后对对它进行输出,看看我是怎么做的吧!
注意:请仔细观察对象是如何调用方法的,它使用了“.”操作符!事实上是这样的,对象调用公共的属性或方法时就会使用“.”操作符。
然而在C++中,如果定义一个同类型的指针,该指针调用此对象的方法时,就会使用“->”操作符。更详细的内容清参阅相关书籍了!
//workstation.java
import java.awt.Graphics;
import java.applet.Applet;
public class workstation extends Applet{
private employee jingwei ; //对象的声明,此时并不分配内存!
public void init(){
jingwei = new employee(); /*此时创建对象会调用构造函数,稍候介绍*/
jingwei.setName(“jw”); //设置我的名字
}
public void paint(Graphics g){
g.drawString("my age is "+jingwei.getName(),10,10);//显示我的年龄
}
}
输出结果是:
my name is jw
这串字符串是在输出窗口的x坐标轴为10 px , y坐标轴为10 px的位置。
我现在要做的是,把上面的程序做个大解剖,让你能够看清楚它到底是怎么一回事儿!(我可不时带你去看里面的汇编,呵呵,那个我也不会:)
首先还是来看看我们自定义的数据类型employee,在应用的时候它和int类型的数据没什么两样,一样的需要创建变量(对象),只不过前者是咱自 己定义的,而后这是它内置的。Employee这个类中有许多属性,也有许多方法。而此时,我们不能直接用我们所创建出来的对象调用它的属性进行修改。因 为它是private受保护类型的!我要想修改我的姓名我就要用对象调用setName()这个方法,而我想得到我的姓名就要调用getName()这个 方法。我们完全是按照航线来行走的,这个航线就是“对象调方法,方法改属性”
好的,我真的相信你已经明白了这是怎么一回事儿了!呵呵!仰起航帆,继续前行!
现在让我们一起来看看workstation这个类。这是个主类,和C++中的main()函数的味道差不多。其中,在JAVA中,一个文件只允许有而且必须有一个主类,这个主类用public来声明!他就跟C++中必须要有一个main()函数是一样的。
让我们来看看这个类中的第一条语句!private employee jingwei ;这条语句的作用是声明一个employee的对象jingwei(在C++中就不用声明了)。我想要和你说的是“声明”与“定义”之间的区别。声明只是 告诉计算机将要有这样的一个变量(对象),在内存中它并不为这个变量(对象)分配内存!而只有在定义的时候才会给这个变量(对象)分配内存。(需要说明一 下的是init()方法是完成初始化操作的,在此处定义对象,为对象分配内存。start()方法用来启动浏览器的主线程,paint()方法来显示 Apple的界面。这些是Applet程序所需的,至于Application程序就不需要了,当然了,C++中也不需要他们。关于他们的详细内容清参阅 相关书籍)
紧接着就开始定一个对象了,对jingwei这个对象进行操作才会有实际的意义。千万不要有这种想法:“试图对类进行操作!”就像前面我说的,电视机 原理不能看电视一样!这是毫无意义的!看这条语句jingwei = new employee();它的意思就是定义一个employee类型的对象jingwei。此时,我想告诉你的是:“jingwei这个对想拥有了些什 么”。它拥有了类所描述的所有的属性及方法。下面我一一给你列出来:
/*所有的employee对象都拥有这些属性。每创建一个对象就会从新分配一块内存来存放相应对象的这些属性。我的意思是每个对象都有自己“独特”的一份*/
private String name; //员工姓名
private int age; //员工年龄
private char sex; //员工性别
private float emolument; //员工薪水
private boolean lunch; //员工午餐
/*所有的employee对象都拥有这些方法。但在内存中只有一份*/
public void heater(){ //这个方法是用来加工员工的午餐
lunch = true;
}
public void setName(String a){ //这个方法是修改员工的姓名
name= a;
}
public String getName(){ //这个方法是得到员工的姓名
return name;
}
/*但是,实际上在创建jingwei这个对象时计算机只给这个对象的所有的属性分配了内存,而并没有给方法分配内存。方法只有一个,是属于所有的对象的,所以无论创建了多少个对象,计算机只会为一个方法分配一块内存。*/
我想我还是举个例子吧,不然你非晕倒不可。呵呵!
看我这条语句“private boolean lunch;”公司不管午餐,每个员工都需要带饭。我们现在这样想,公司的空间是所有的内存容量,你的办公桌就是计算机中的内存中的一部分(每个员工都有 一份,是在创建对象时分配的)。你把午饭带到了公司,放在了你的办公桌上。“午饭”占据了你的办公桌的一角(占了你自己“对象”的一块内存容量)。这份午 饭只属于你自己,同样别人的也只属于她自己!所以每个员工(对象)都需要一快空间(内存)来存放自己的午餐(属性)。在计算机中也是这样的,每创建一个对 象,就会在内存中从新分配一块内存来放“午餐——lunch”这个属性(对象所拥有的所有的属性)。
计算机只会为对象的属性分配内存。因为每个对象的都不一样!就像你往公司带的午饭和我往公司带的午饭不一样是一个道理!但方法就不同了。早晨带的饭中 午就凉了,你需要用微波炉来加热。微波炉可不用你带,公司就有(只占公司的一块空间),它放在了午餐桌上。你想想,微波炉属于谁的?它属于所有员工的!因 为每个员工都可以用它。而不必每个员工都带一份。由此可见,每个员工(对象)都有一份午饭(属性),但所有的员工(对象)只一个微波炉(方法)。所有的员 工(对象)都可以通过这个微波炉(方法)来改变自己午餐(属性)的冷热状态。殊途同归!在计算机中也就是这样,方法只有一份,供所有的对象使用!而属性是 每个对象一份,因为每个对象的都不一样。别和我说你还不明白,不然我会撞墙的,呵呵:)
2.3深入探讨函数:
2.3.1构造函数、默认构造函数、缺省构造函数
对于上面的实例,它已经能完成绝大部分工作了,但它还是不完善的,还有许许多多的细节等到我们去完善!也许有的同学已经注意到了,当我创建完 “jingwei”这个对象时,这个对象的所有的属性都是空的,也就是说:这个对象的姓名是未定的、年龄是未定的、性别是未定的、薪水是未定的、午餐也是 未定的。而我们想把这些属性都添加上去,就还要用对象调用相应的方法,去一个个修改!天啊,这简直是太麻烦了!有没有什么好方法能够在我们创建对象的同时 就完成了对属性赋值的操作呢?哦不,应该说是对属性的初始化呢?当然没问题了,这就需要所谓的构造函数!
构造函数是类中最特殊的函数,它与析构函数的功能正好相反!
从特征上来说:1.它是编程语言中唯一没有返回值类型的函数。
2.它的名称与类的名称必须要完全相同。
3.它必须被声明为公共(public)的类型
4,可以对构造函数进行重载。
5.它在创建对象是自动被调用。
从功能上来说:1.它是对类中的属性进行初始化。
其实,对于上面的程序来说我们没有自己定义构造函数。但是,在这种情况下,系统会自动为我们定义一个“默认构造函数”。他会把数值变量自动赋值为0, 把布尔行变量赋值为false等等(但在C++中,默认构造函数不初始化其成员)。如果程序员定义了构造函数,那么系统就不会再为你的程序添加一个缺默认 造函数了。(在这里,我们提倡的是自己定义构造函数,而不是用系统的默认构造函数)
还是看个实例吧!这样比较清楚一些!
//employee.java
public class employee{
private String name; //员工姓名
private int age; //员工年龄
private char sex; //员工性别
private float emolument; //员工薪水
private boolean lunch; //员工午餐
//……等等
public employee(){ //这个就是“默认”构造函数
name = “jw”; //设置员工姓名
age = 20; //设置员工年龄
sex = “M”; //设置员工性别
emolument = 100; //设置员工薪水
lunch = false; //设置员工午餐
}
public void heater(){ //这个方法是用来加工员工的午餐
lunch = true;
}
//……等等
};
这样,在我们创建“jingwei”这个对象的同时,它的所有的属性也被初始化了!显然,这大大的提高了工作效率,但是,它还是不符合要求。想想看, 如果我们现在创建这个类型的第二个对象的时候会发生什么事情?告诉你,除了对象的“名”(这个名称不在是对象属性中的名称,而是对象本身的名称)不一样 外,其所有的“属性值”都一样!比如:现在我们创建第二个对象flashmagic,然而我会发现这个对象的所有的属性和jingwei这个对象的所有的 属性完全相同。而我们只能在用对象的方法去改变着写属性了!很显然,这种方法不大好!我们需要一种方法在创建对象的时候为对象的属性赋予“我们想要的 值”。
相信你也看到了,默认构造函数就显得无能为力了。我们需要的是带参数的构造函数,在创建对象时,我们把参数传给构造函数,这样就能完成了上述的功能!口说无凭,还是来看个实例吧:
//employee.java
public class employee{
private String name; //员工姓名
private int age; //员工年龄
private char sex; //员工性别
private float emolument; //员工薪水
private boolean lunch; //员工午餐
//……等等
public employee(String n,int a,char s,float e,boolean l){ //看这个构造函数
name = n; //设置员工姓名
age = a; //设置员工年龄
sex = s; //设置员工性别
emolument = e; //设置员工薪水
lunch =l; //设置员工午餐
}
public void heater(){ //这个方法是用来加工员工的午餐
lunch = true;
}
//……等等
};
这样一来,在创建对象的同时我们就可以给他赋予我们想要的值,很显然,这可就方便多了。哦,对了!还没有告诉你怎么创建呢!哈哈,往前翻几页你会看到这句话:
jingwei = new employee();这是创建一个对象,而我们把它改成
jingwei = new employee("jingwei",20,'M',100,false);这样一来,所有的工作都完成了,呵呵!(在创建对象的同时赋予了我们想要的“初值”)
2.3.2重载构造函数:
我还是先把概念给你吧,让你有个认识,随后我们在进行论述。
在JAVA中:
1. 函数重载是一个类中声明了多个同名的方法,但有不同的参数个数和参数类型。
2. 函数重构是指在子类中声明与父类同名的方法,从而覆盖了父类的方法。重构解决了子类与父类的差异问题。(在讨论到继承时我会详细说明)
在C++中:
1. 数重载的概念一样。
2. 重构的概念可就不一样了,C++中功能更为庞大的虚函数。更详细内容这里就不错过多介绍了!
其实关于重载的概念你并不陌生,在编程中相信你也接触过。呵呵!让我们来举个操作符重载的例子你就会明白了,(JAVA中不支持这个功能)我们定义三个整数变量:
int i1=2, i2=3,i3=0;
i3 = i1 + i2;
此时i3=5;加号实现了两个数相加的运算功能。然而我们现在要定义三个字符串变量:
String str1=”jing”, str2=”wei”,str3=””;
str3 = str1 + str2;
此时str3 = “jingwei”;加号实现了两个字符串相加的运算功能。同样是加号,既可以把两个整型的变量加在一起,也可以把两个字符串类型的变量加在一起。同一个 操作符实现了不同的功能------这就是所谓的操作符重载(嘿嘿,我说你一定见过吧:)!不就好像是汉语中的一词多意一样!我需要说明一下的是,C++ 中的操作符重载可没有这么简单。比如,我们可以对两个自定义类型的对象进行相加的运算,进行赋值的运算。这样书写简洁明了,而且非常实用。当然,关于操作 符重载的话题太多了,有兴趣再看看书吧!
我们把操作符的话题在转到函数上来,我们一直强调的是“对象调方法”------对象其实调的是方法的“名称”。而我们现在要对方法进想重载,也就是 定义多个相同名称的函数,这样计算机在调用的时候不会混淆嘛?我想应该不会的,呵呵,因为仅仅是函数名称相同,而我们在调用函数时会把参数传递给他的。既 是没有参数也是一种参数传递参数的信息(信息为无参数)!然而由于参数类型、参数数量、返回值类型不同我们就可以对相同名称的函数进行区分更多相关内容 -
Python零基础入门篇 - 33 - 你了解编程范式吗?面向过程编程与面向对象编程的区别又是什么?
2022-03-07 22:02:57编程范式是计算机语言编程的一种需要编程者遵循的基本风格与规范模式。如果说每一个编程者进行的编程是在创造一个依托于计算机而存在的虚拟世界,那么编程范式就是这个虚拟世界所采用的世界观与创造世界的方法论。前言:
✌ 作者简介:渴望力量的哈士奇,大家可以叫我 🐶哈士奇🐶 。(我真的养了一只哈士奇)
📑 个人主页:渴望力量的哈士奇主页
📫 如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥 如果感觉博主的文章还不错的话,还请不吝👍关注、点赞、收藏三连支持👍一下博主哦
💬 人生格言:优于别人,并不高贵,真正的高贵应该是优于过去的自己。💬📕系列专栏::
👍 Python全栈系列 - [更新中] ➡️➡️➡️ 【 本文在该系列】
👋 网安之路系列
🍋 网安之路踩坑篇
🍋 网安知识扫盲篇
🍋 Vulhub 漏洞复现篇
🍋 Shell脚本编程篇
🍋 Web攻防篇 ➡️➡️➡️ 2021年9月3日停止更新,转战先知等安全社区
🍋 渗透工具使用集锦 ➡️➡️➡️ 2021年9月3日停止更新,转战先知等安全社区
⭐️ 点点点工程师系列
🍹 测试神器 - Charles 篇
🍹 测试神器 - Fiddler 篇
🍹 测试神器 - Jmeter 篇
🍹 自动化 - RobotFrameWork 系列
🍹 自动化 - 基于 JAVA 实现的WEB端UI自动化
🍹 自动化 - 基于 MonkeyRunner 实现的APP端UI自动化
🤗 2019年之前学习Python留下的乱七八糟系列🎉🎉欢迎持续关注🎉🎉 文末有彩蛋
文末有彩蛋!!!!
文末有彩蛋!!!!
文末有彩蛋!!!!
重要的事 情说三遍编程范式科普
什么是编程范式?
编程范式是计算机语言编程的一种需要编程者遵循的基本风格与规范模式。如果说每一个编程者进行的编程是在创造一个依托于计算机而存在的虚拟世界,那么编程范式就是这个虚拟世界所采用的世界观与创造世界的方法论。
常见的编程范式:
面向过程编程
与面向对象编程
编程范型提供了程序员对程序执行的看法:
- 在面向过程编程中,程序员认为程序是一系列相互调用的过程或者函数;
- 在面向对象编程中,程序员认为程序是一系列相互作用的对象;而在函数式编程中一个程序会被看作是一个无状态的函数计算的序列。
在不同的编程语言中,提倡的是不同的编程范式。在 C语言 中,使用的是面向过程编程范式,在 JAVA 语言中支持的是面向对象编程。而 Python 编程语言支持多种编程范式,所以在不同的应用场景下,程序员应选择合适的编程范式。
面向过程编程
面向过程编程是一种以过程为中心的编程思想,程序由一系列相互调用的过程组成。面向过程编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。
面向过程编程的核心是过程,就是分析出实现需求所需要的步骤,通过函数一步一步实现这些步骤,接着依次调用即可,再简单理解就是程序从上到下一步步执行,从头到尾的解决问题。
在这类问题中,解决问题的途径由多个步骤构成,使用函数描述每个步骤,因此使用函数对问题建模非常合适。
面向过程编程不足之处就是它不适合某些种类问题的解决,例如图形化编程,在图形化编程中,客观世界由具体的对象(窗口、标签、按钮等)组成,无法自然的将函数与图形对象一一对应,因此面向过程编程不适合用于图形化编程的领域。
举个数学计算的例子:
A地点 到 B地点之间的距离是 180KM ,开车走高速的速度是每小时 60KM ,计算所花费的时间。
面向过程实现的代码示例如下:
speed = 60.0 mileage = 180.0 time = mileage / speed print('共计需要花费的时间是:', time, '小时') # >>> 执行结果如下 # >>> 共计需要花费的时间是: 3.0 小时
面向对象编程
区别于
面向过程
编程 ,面向对象
编程是一种以对象为中心的编程思想,程序由一系列相互作用的对象组成。面向对象编程中,程序包含各种独立而又互相调用的对象,而在面向过程编程中,将程序看作一系列函数的集合。面向对象编程是把构成事物的整个需求按照特点、功能划分,将这些存在共性的部分封装成对象,创建对象的目的不是为了完成某一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
面向对象程序设计方法是尽可能模拟人类的思维方式,使得软件的开发方法与过程尽可能接近人类认识世界、解决现实问题的方法和过程,也即使得描述问题的问题空间与问题的解决方案空间在结构上尽可能一致,把客观世界中的实体抽象为问题域中的对象。
Python中一切皆对象,比如之前学过的变量其实也都是对象,包括整形(int)、浮点型(float)、字符串(str)、列表(list)、元组(tuple)、字典(dict)和集合(set)。
基本概念
面向对象编程包含通过类、实例、属性和方法等核心概念:
- 类: 可以理解是一个模板,通过它可以创建出无数个具体实例。 比如,定义一个Person类,通过它可以创建出无数个实例来代表各种不同特征的人。
- 实例(对象): 类并不能直接使用,通过类创建出的实例(又称对象)才能使用,比如以上案例创建类对象Person。
- 属性: 类中的所有变量称为属性。比如以上案例中类的变量有 姓名name、年龄age。
- 方法,方法是指对象的行为。例如,存在一个对象 person,对象 person 的包括一个方法 show,通过调用方法 show 可以输出对象 person 的相关信息。
下面的代码演示了以上 4 个基本概念:
class Person: def __init__(self, name, age): self.name = name self.age = age def show(self): print('My name is %s, I am %d years old' % (self.name, self.age)) tom = Person('tom', 10) jerry = Person('jerry', 12) tom.show() jerry.show()
- 在第 1 行,定义了类 Person
- 在第 2 行,定义了类 Person 的方法 init
- 方法 init 设置类 Person 的属性 name 和 age
- 在第 6 行,定义了类 Person 的方法 show,
- 方法 show 输出类 Person 的属性 name 和 age
- 在第 9 行,通过类 Person 创建一个实例 tom
- 实例 tom 的属性 name 是 tom,age 是 10
- 在第 10 行,通过类 Person 创建一个实例 jerry
- 实例 jerry 的属性 name 是 jerry,age 是 12
- 在第 11 行,调用类 tom 的方法 show
- 在第 12 行,调用类 jerry 的方法 show
面向对象最重要的概念就是类和实例,要牢记类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法。
同样是上面的数学计算的例子,如何利用 面向对象编程 的编程范式来实现:
class Car(object): speed = 0 def take_time(self, mileage): time = mileage / self.speed print('共计需要花费的时间是:', time, '小时') car = Car() car.speed = 60.0 car.take_time(180.0) # >>> 执行结果如下 # >>> 共计需要花费的时间是: 3.0 小时
面向过程与面向对象的区别
编程思想的不同
面向过程:是一种以过程为中心的编程思想。
面向对象:是一类以对象为核心的编程思想。
特点不同
面向过程:主要分析出解决问题所需要的步骤,用函数把这些步骤一步一步实现,使用的时候一个一个依次调用即可。
面向对象:主要找出问题中的共性问题,作为对象进行操作。
话说现如今,那个年轻人不是在用命去打拼呢?遍地戾气如斯,与君更勉之。
为了碎银几两,为了三餐有汤。为了车呀为了房,你为的是哪位姑娘…
-
PHP面向对象之领域模型+数据映射器实例(分析)
2020-12-20 00:35:19因原书中领域模型+数据映射器的示例代码是连贯在一起的 所以这里就整理在一起了。 简单介绍一下我的看法,从数据库操作的角度看领域模型主要是操作数据表中的单条记录的而数据映射器是操作整个数据表的数据的。 按... -
coq-dpdgraph:在COQ对象之间建立依赖关系图
2021-05-05 01:56:15里面有什么? 首先,它是一个小型工具(一个Coq插件),用于提取Coq对象之间的依赖关系,并生成包含此信息的文件(建议使用后缀.dpd)。 这个想法是,然后可以开发其他小的工具来处理.dpd文件。 目前,有: dpd2... -
什么是面向对象编程
2020-01-12 18:57:20另一方面,在学了一些编程的相关知识以后,一个最明显的感觉就是:这东西有什么用?为什么我明明听懂了却还是解决不了实际的问题,甚至于完成不了相应的练习?这其中最主要的原因是什么呢?除了埋怨出的题目跨度太大...写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域,如今终有小成,愿将昔日所获与大家交流一二,希望对学习路上的你有所助益。同时,博主也想通过此次尝试打造一个完善的技术图书馆,任何与文章技术点有关的异常、错误、注意事项均会在末尾列出,欢迎大家通过各种方式提供素材。
- 对于文章中出现的任何错误请大家批评指出,一定及时修改。
- 有任何想要讨论和学习的问题可联系我:zhuyc@vip.163.com。
- 发布文章的风格因专栏而异,均自成体系,不足之处请大家指正。
什么是面向对象编程
本文关键字:面向对象、面向过程、面向过程编程语言、面向对象编程语言
一、浅说编程
说到编程,对于初学者来讲,可能第一想到的就是敲键盘,写代码,做游戏,甚至于会联想到软件破解、网络攻防。另一方面,在学了一些编程的相关知识以后,一个最明显的感觉就是:这东西有什么用?为什么我明明听懂了却还是解决不了实际的问题,甚至于完成不了相应的练习?这其中最主要的原因是什么呢?除了埋怨出的题目跨度太大以外,我们也应该好好的反思一下。
对于以上的问题,最主要的原因就是对于编程了解的不够深入。这个深入指的不是我们要去了解他的发展历史以及底层原理,而指的是我们要对编程有一个明确的定位,我们现在学的内容是什么,又能用来做什么?
- 编程与软件开发
很多同学在刚开始都会把和计算机相关的所有内容都和编程,敲代码,程序员联想到一起,这其中甚至包括:做系统、配路由、修电脑、做病毒等等脑洞大开的想法。这些想法很多是行外人的看法,但是也有一少部分发生在学习者身上,等开始学了才发现和预想的完全不一样。
通常来说学习编程所对应的岗位就是开发工程师,开发工程师根据工作内容的不同又分为很多种:前端、后端、移动端、嵌入式、游戏开发、大数据等等。相同点在于都是完成一个应用产品,也就是在电脑上运行的软件、通过浏览器访问的应用、手机端运行的各种APP、各种硬件设备的监控、数据分析产品等等。
由于软件编程是信息技术的一部分,因此,在学习时通常会用到网络通信、操作系统、数据结构等等方面的知识,这时候往往就会一个头两个大了,因为这些学科可能不会去系统的学习,在接触的时候会感觉整个体系十分庞杂,难以掌握,学到的东西又不知道什么时候才会派上用场。- 如何学习编程
对于以上的情况,首先我们需要做的就是先沉下心,越难掌握的东西往往会让自身的价值更高。这也是为什么程序开发者相对高薪,无论你接受与否,这都是一条充满挑战且激动人心的学习之旅。那么,编程到底如何学呢?熟练的掌握基础语法?背下每一段常用的代码?当然不是。首先我们要把编程语言当做是工具,做它的主人。
我们现在所学的东西无非是那些伟大的人已经创造出来的东西,有一部分是固定的需要记忆下来的,就比如基础语法,一些基本的操作步骤。另外一部分就是比较难掌握,也是最充满魅力的部分,就是用已有的知识去解决一个实际的问题,而且通常这个问题都是自己之前没有实现过的。归结起来就是编程思想,对于初学者来说,就是解决一个问题的思路。吴文俊院士曾经提出过“数学机械化”的概念,很多问题在前进一步之后都有一个确定的、必须选择的下一步,这样就可以沿着一条有规律的、刻板的道路,一直达到结论。
在编程和方法设计时我们也可以借鉴这样一个思路。通常我们按照三个步骤来思考就好,What I want?What I need?How to do?当然这只是本人总结的,如果大家有更清晰的步骤可以分享。- 第一步(What I want)
一定不要着急写程序,特别是大脑一片空白不知道如何下手的时候。首先我们最应该做的是明确我们自己要做什么,把这个问题转换为一个有步骤的逻辑问题,是一个简单的顺序结构,还是一个需要根据不同情况来进行选择和循环,功能和功能之间存在哪些联系,这是我们需要理清的。
- 第二步(What I need)
在完成了第一步后,我们要在我们所学的知识当中来搜索,应该用到哪些基本结构或者已有的工具类和方法。如果还是没有任何思路,那只能说学的还不够扎实,或者又一次接触到了知识的盲区。这个时候并不需要暴躁,去查就可以了,并且幸运的是,在完成了第一步之后我们在搜索时我们的问题会很有针对性。搜索的问题不再是:XX系统怎么做?XX功能怎么实现?而是如何处理XX(步骤),XX技术点(使用方法)。碰到没有接触过的东西是一件很正常的事情,你目前所遇到的问题一定是先前别人已经遇到的。即使你遇到的是一个稀有级或者史诗级的BUG,那么通过搜索后得到的信息也一定会对解决问题有所帮助。
- 第三步(How to do)
完成了前两步之后,最后一步其实就很简单了。明确了自己要什么,用什么来做,剩下的就是怎么做了,把思路和工具结合,就是你的结果!所以大家需要明确的是,真正能写出程序,让他工作的是我们,不要把计算机想象的太过智能,每一个功能,包括细小的逻辑判断都是从我们的需要出发来实现的。所以编程才会那么灵活多变,能开发出各种各样的系统,做各种各样的应用,这也就是最具魅力的地方。想一想,把自己的一个想法完整的实现出来,是一种什么样的感觉?
二、面向过程
首先我们来说一说面向过程的编程语言,这是一种编程思维,是以过程为中心的编程。主要的特点是先分析出解决问题需要的步骤,然后把每一步定义成一个一个方法,通过逐个调用的方式来完成整个功能。这样的好处是十分直接,要解决什么问题就写什么代码,但是复用性会比较差。
- 举例说明:假如我需要制作一套家具
如果使用面向过程的语言来完成,那么我将化身一个全能木匠,从准备原材料开始到家具成品,中间的所有步骤都要自己完成。首先是确定原材料、准备原材料、然后是制作形状、上漆等步骤。在编程上体现为:主要通过方法调用来完成,一个程序解决一个特定的问题。
三、面向对象
那么什么是面向对象编程呢?由于对象这个词本身就很抽象,只要你不是按照下面这张图片理解的,那么你还是可以学好面向对象编程的。
面向对象是被目前绝大多数主流编程语言采用的编程思想和软件开发方法,并且由于这种概念的普适性,也已经在数据库系统、应用平台、分布式系统、人工智能等领域普遍使用。那么到底什么是面向对象编程呢?其实很简单,我们只需要按照我们正常的思维方式去思考就行了,比如还是制作一个木制家具,那么在实际生活中是怎样办到的呢?
原材料准备,这应该是采购商的工作,其他的环节他无需插手;家具设计图,这是设计公司的工作;找到合适的木匠,这是用人单位应该做的。谁来做家具呢?木匠。于是我们把这些人组合到一起就可以完成这样一个工作,编程者更像是一个项目的负责人,需要知道完成这项工作需要用到哪些人,这些人应该能够完成哪部分的工作,每个人各司其职。- 举例说明:面向过程与面向对象的比较
从直观来看,面向对象编程都有哪些特点呢?开销大,这是肯定的,原来一个经验丰富的老师傅能完成的工作现在要这么多人来完成,要创建这么多对象,内存的占用当然也是较多的。那有什么好处呢?我们把问题看的长远一点,如果我们要求家具用不同的材质来做,采用更多的式样来做,不单单是上漆,还要更多的装饰式样,这个时候会怎么样呢?如果使用面向过程的语言,我们需要写出很多很多个和要求匹配的程序,非常不利于维护。如果换成面向对象的语言呢?我们只需要告诉采购商:我要什么原材料;告诉设计公司:我要什么样的家具;告诉用人单位:我要什么样的木匠;告诉木匠:你该怎么做。虽然在前期要编写很多代码,但是在完成工作时可能只需要改变调用的参数就能解决很多问题。
总体来说,如果我们追求效率和资源,要以最小的开销做出符合要求的产品,那拉上一帮人只为做那么几件东西就显得有些得不偿失了,这个时候我们选择使用面向过程语言比较合适。如果我们要做的是一个国际公司,可以适应不同国家地区不同人的要求,那么这个时候我们使用面向对象思维是很必要的,你总不能为每个需求都量身定做写一套程序。四、代表语言
- 面向过程:
C语言、Fortran等
- 面向对象:
C++、C#、Java、Python等
扫描下方二维码,加入官方粉丝微信群,可以与我直接交流,还有更多福利哦~
-
c++类和类的封装,对象线程封装
2018-09-05 17:50:421. C++面向对象编程介绍 面向对象编程(Object Oriented Programming),简称OOP。 在传统的面向过程编程中...OOP提供了设计对象的功能,对象包括特性和行为,两者都囊括在一起,共同构成对象实体(即类实体); 对...1. C++面向对象编程介绍
面向对象编程(Object Oriented Programming),简称OOP。
在传统的面向过程编程中,数据以及数据的相关操作函数都是分离的独立个体;
对象,如周围的一切其实都是对象;就编程角度,对象包括A)一系列属性(数据);B)一系列操作(函数)。
OOP提供了设计对象的功能,对象包括特性和行为,两者都囊括在一起,共同构成对象实体(即类实体);
对象实体,使程序更模块化,更易读易写,提升了代码重用到一个更高的层次;
对象提供了对数据操作的直接方法,定义了如何与对象进行交互,以及对象之间的交互;
更重要的是,OOP提供了更实用的概念:封装、继承、多态和抽象。
这篇主要讲述封装,即对象将数据及其操作函数封装到一个类实体中。
2. 类和类成员
C++提供了如char、int、long、float、double等基本数据类型,足够用来解决大多数哦相对简单的问题,但对于复杂的问题就比较困难了。
C++的一个实用特性是自定义数据类型;如之前的枚举和结构体:
枚举和结构体代表了传统的面向过程编程,它们只包含数据,如果需要访问此类型变量,必须提供自定义函数,并以该类型变量为参数:
在OOP世界中,我们希望自己的类型不仅包括数据,也包括操作数据的函数;C++中,通过class关键字来声明一个类。
2)类类似结构体,但它更多功能和更灵活:
类似结构体,类的声明没有分配内存,只是说明了类的结构;类的声明以分号结尾;
为了使用类,可以声明一个该类型的变量:
声明一个类的变量,也叫实例化一个类;该变量称作为类的一个实例,或者对象。
3)成员函数:
类不仅仅包含数据(成员变量),亦可以包含函数,称作成员函数:
成员函数可以类似访问成员变量,使用'.'来使用:
通常,类的成员变量,加一个前缀'm_'来区分:
3. 公有和私有
1)访问标识符:公有-public关键字,私有-private关键字
在上面的DateStruct的结构体,它的成员可以在main函数中访问,因为结构体的所有成员默认是公有的,即public;
公有成员在结构体和类中,程序的任何函数都可以访问;
但如果Date的成员没有public标识符,则会出现错误:
这个说明,类成员的默认是私有的,即private的;
私有的成员只能在类的定义之内的函数可以访问;
可以通过public关键字使成员变为公有的,即可访问了:
类和结构体的本质区别之一是类的成员可以使用标识符来控制它们的可访问性;
C++提供了3种不同的访问标识符:public、private、protect,分别是公有的、私有的和保护的:
4. 访问函数和封装
1)访问函数,也叫做读写函数;是读取和写入私有成员变量的值。
例如:
GetLength函数就是个访问函数。
访问函数有2种,即getter和setter:
将数据成员私有化,提供Getter和Setter来访问,即所谓的"封装" ;
2)封装
封装的思想就是将实现的细节隐藏,而暴露公有接口;
C++中的访问标识符,可以实现在类中的封装;通常是将所有的成员变量私有化;
尽管看起来访问成员变量的不直接,但使程序更有可重用性和可维护性;
A)封装实现,无论类的实现如何改变,只要对外的接口不发生变化即可。
如图上例,如果m_Value被重命名了,那么main函数中访问就会出错;
如果提供了m_Value的访问函数:
B)隐藏了类的实现,类的使用者只需知道公共的接口,就可以使用该类;
C)封装帮助防止意外的改变和误用;
D)对程序调试有很大的帮助,因为改变类的成员变量只用通过公共接口。
5. 构造函数Ⅰ
1)构造函数:是类的一种特殊的成员函数,当类被实例化时执行;通常用以初始化成员变量。
构造函数有明确的命名规则:A)函数名必须和类名一样;B)无返回类型(包括void)。
无参构造函数-不带参数的构造函数,是类的默认构造函数:
通常,类都包含一个默认的构造函数,可以初始化成员变量。
含参构造函数-含有参数的构造函数,可以对成员变量赋予指定的值;
以上的两个构造函数,类似重载函数;构造函数必须有唯一的前面(参数个数和参数类型)。
类亦可以只提供含参构造函数,没有默认构造函数:
6. 析构函数
析构函数是类的另一种特殊的函数,当类的对象销毁时调用;它和构造函数是成对出现的。
普通的简单类,一般不需要析构函数;因为C++会自动回收垃圾;
如果类中执行了某些动态内存分配,则需要显式定义析构函数,并释放回收垃圾;
析构函数的明确命名规则:A)函数名和类名一样,并前缀'~';B)不能带参数(即意味着只有一个析构函数);C)没有返回类型。
注意动态分配,必须提供析构函数,来回收分配的空间。
2)构造函数和析构函数的时序:
如上图所示,输出的依次是:simple的构造函数,pSimple的构造函数,最后是pSimple的析构函数。
构造函数和析构函数的时序是:Constructor First, Destructor Last。
7. 隐藏的'this'指针
如之前的例子中的this,是每个类的成员函数中隐藏的指针,它指向了类成员函数打交道的类的对象。
实用性:
1)当构造函数或成员函数中的参数名和成员变量名相同时,可以使用this来访问本类的成员变量;
2)可以使用this返回类的对象引用:
8. 构造函数Ⅱ
1)私有构造函数-如果不想类以外使用指定的构造函数,我们可以将它私有化。
类只能被实例化一次,称作为单一性;通常使用私有/保护构造函数进行的。
2)构造函数链和初始化
有时,一个构造函数所做的工作和另外的构造函数一样,只是增加了一些;
这样这个构造函数可以调用另外的构造函数,称作为构造函数链。如C#就支持这种格式,但C++不支持。
但构造函数可以调用类中的非构造函数,只是要注意这些非构造函数调用的成员,必须已经初始化。
通常的做法就是,定义一个公共的非构造函数,构造函数都调用它来初始化共同的;例如:
9. 类代码和头文件
1)在类的定义之外定义成员函数。
如之前的类定义,都是在类的定义中定义成员函数:
当类的定义越来越长和越来越复杂时,就显得臃肿,难以维护和操作;
幸运的是,C++提供了一种分离类的定义及其应用定义的方法,将类的成员函数在类的外面定义;格式是:类名::函数名
2)将类的定义放在头文件中:
头文件的使用可达到重用的效果;所以将类的定义放在头文件中,而成员函数放在.cpp中定义;而cpp的名字需和类的名字相同。
Date.h:
Date.cpp:
推荐分离类的定义中的成员函数到类外定义。
10. 常量类对象和成员函数
函数的参数可以为常量对象,如内置的基本数据类型一样,类对象也可以声明为常量,所有常量对象的变量必须在创建时初始化,其后不能修改。
上图3个错误,因为程序试图修改常量类对象的变量;
因为常量类对象不能调用非常量成员函数;
常量成员函数-保证不修改任何类变量或调用任何非常量函数。
为了使GetValue常量化,可以在其原型加个const关键字:
注意:
A) 常量成员函数在类外定义时,也必须加const关键字;
B) 任何常量成员函数试图修改类成员变量,或者调用非常量成员函数都是非法的,会产生编译错误。
C)构造函数不能常量化;
重载函数使用const和非const,是当返回类型不一样的时候。
11. 静态成员变量
在之前的程序中,静态表示变量的值在运行期间保持最新的值;
1)静态成员变量
在实例化两个对象,其包含的相同的成员变量;
静态成员变量是属于类的本身,是所有对象的共享变量;它的值是保持修改的最新值;
使用格式:类名::静态成员。
2)静态成员变量的初始化
初始化必须在类的代码文件中进行。
12. 静态成员函数
如静态成员变量一样,静态成员函数是属于类的本身,不属于任何类的对象;
如静态成员变量访问一样,可以通过:类名::静态成员函数来访问类的静态成员函数。
注意:静态成员函数,没有this指针;
例如:
13. 友元类和友元函数
多数时候,类和函数需要运行很紧密;但需使用显示函数来打印相关信息,这并不显得很隐藏类的细节;
这时,友元类和友元函数就可以很好的访问私有细节;
1)友元函数
友元函数访问类的私有成员,就如其是类的一个成员函数。
一个友元函数可以是,也可以不是其他类的成员函数;使用关键字friend。
例如:
一个友元函数可以是多个类的友元函数;
2)友元类
友元类是,声明在其他类中,可以访问其他类的私有变量;
在使用友元函数和友元类时,请务必谨慎。
14. 匿名变量和对象
所谓匿名,就是可以不通过命名变量来访问,减少临时变量。
【免责特此声明:
1)本内容可能是来自互联网的,或经过本人整理的,仅仅代表了互联网和个人的意见和看法!
2)本内容仅仅提供参考,任何参考该内容造成任何的后果,均与原创作者和本博客作者无关!】C++学习:类,对象,封装
- 类
- 对象
-
封装
C++是一门集面向过程,面向对象以及泛型编程于一体的强大的编程语言,在这里面最重要的要属面向对象了吧???什么是面向对象?面向对象的思想是什么???总结下来就一句话:万物皆对象.在面向对象的世界里,一切都是可以用对象来解释的.这也是面向对象思想的精髓部分.
万物皆对象
1:类
举个例子:在超时里面进行商品购物的时候,它所有的商品都是按类目来划分的.生活用品,食品等.这就是按类别来分.而在自然界中也是.如:动物昆虫等.这就是类.将一类相似的的东西抽象成一类东西.
结构化程序设计
程序 = 算法 +数据结构
面向对象设计OOP
-
OBJECT ORIENRED PROGRAMING
-
Object oritened programing
-
程序 = 对象 +对象 +....
-
关键:让每个对象都负责执行一组相关的任务
面向对象开发范式的特性:
-
万物皆是对象
-
程序是一组对象彼此之间在发送消息
-
每个对象都有自己的内存占用,可以组装成更大的对象
-
每个对象都有类型,特定类型的对象都可以接收相同的消息
概念:
-
类:类是创建对象的模板和蓝图,类是一组相似对象的共同抽象定义
-
对象:对象是类的实例化结果,对象是实实在在的存在,代表现实世界的某一事物
对象的三大特性:
-
行为:对象能干什么
-
状态:对象的属性,行为的结果
-
标识:对象的唯一身份;
类和对象的区别:
-
类是静态定义
-
对象是动态实例
-
建立模型得到的是类而非对象
联系:
-
类是对象的定义
-
对象的产生离不开类这个模板
-
类存在的摸底是实例化得到对象
世界是由对象组成的
定义一个类的步骤:
1:定义类名
2:编写类的数据成员代表属性
3:编写类的方法代表行为类的建模是一个抽象和封装的过程:
抽象:去掉不关注的,次要的信息而保留重要的信息
封装:信息打包
具体一点:将数据和行为结合在一个包中,对对象的使用者隐藏数据的具体实现方式
实现封装的关键:不能让类中的方法直接访问其他类的内部数据,只能通过公开行为方法间接访问:例子: class ClassName{ field1; field2; .....; constructor; ..... method1; method2; }
对象的两种类的形式:
1:结构体形式: struct Saving{ unsigned accountNumber; float balance; }; 缺点:安全性不好,任何人都是可以进行访问的 2:class形式: class Savings{ public: float deposit(float amount){ balance +=amount; return balance; } private: unsigned accountNumber; float balance; }; 优点:类不仅可以保护数据,而且可以提供成员函数来操作
C++用类来定义抽象数据类型
C++早期的版本被成为带类的C
-
class 类名称{
-
public:
-
//共有函数
-
protected:
-
//保护成员
-
private:
-
//私有函数
-
//私有成员
-
int val;
-
};
类中定义成员函数:
-
class Tdate{
-
public:
-
void set(int m = 1,int d = 2,int y = 3){
-
month = m;
-
day = d;
-
year = y;
-
}
-
bool isLeepYear(){
-
return (year%4 ==0 &&year%100!=0) || (year %400==0);
-
}
-
void print(){
-
cout<<year<<"/"<<month<<"/"<<day<<endl;
-
}
-
private:
-
int month,day,year;
-
}
-
调用:
-
int main(){
-
Tdate d;
-
d.set(2,4,1998);
-
d.print();
-
}
在类中定义成员函数:
类中定义的成员函数一般都为内联函数,即使没有明确用inline标示
在C++中,类定义通常在头文件中,因此这些成员函数定义也伴随这进入头文件在类之后定义成员函数:
C++允许在其他地方定义成员函数;
将类定义和其成员函数定义分开,是目前开发程序的通常做法
我们把类定义看成是类的外部接口,类的成员函数定义看成是类的内部实现看一个最简单的实例:在main.cpp中去定义一个类.并且在此类中直接实现成员函数.(也可以通过Car.h和Car.cpp的形式去实现)
-
/*
-
* ===========================================================================
-
*
-
* Filename: main.cpp
-
* Description:
-
* Version: 1.0
-
* Created: 2017年05月26日 22时24分05秒
-
* Revision: none
-
* Compiler: gcc
-
* Author: (),
-
* Company:
-
*
-
* ===========================================================================
-
*/
-
#include<iostream>
-
#include<stdio.h>
-
#include<stdlib.h>
-
using namespace::std;
-
class Car{
-
public:
-
void run(){
-
cout <<"car run"<<endl;
-
}
-
void shut(){
-
cout<<"car shutdown"<<endl;
-
}
-
void setProperty(int price,int carNum){
-
this->mPrice = price;
-
this->mCarNum = carNum;
-
}
-
private:
-
int mPrice;
-
int mCarNum;
-
};
-
int main(int argc,char *argv[]){
-
Car mCar;
-
/* *类的成员函数是不会占用内存的 */
-
cout <<"sizeof Car" <<sizeof(mCar)<<endl;
-
cout << &mCar <<endl;
-
mCar.setProperty(10000,1000000);
-
mCar.run();
-
mCar.shut();
-
return 0;
-
}
通过Car.cpp及Car.h来实现一个简单类的封装
-
/*
-
* ===========================================================================
-
*
-
* Filename: Car.h
-
* Description:
-
* Version: 1.0
-
* Created: 2017年05月26日 22时43分53秒
-
* Revision: none
-
* Compiler: gcc
-
* Author: (),
-
* Company:
-
*
-
* ===========================================================================
-
*/
-
#ifndef __CAR_H__
-
#define __CAR_H__
-
#ifdef __cplusplus
-
extern "C"{
-
#endif
-
/* *
-
* 在头文件中去定义一个Car类型
-
* */
-
class Car{
-
private:
-
int mCarPrice;
-
int mCarNum;
-
int mCarType;
-
public:
-
void run();
-
void shut();
-
void setProperty(int price,int carNum,int carType);
-
void print();
-
};
-
#ifdef __cplusplus
-
}
-
#endif
-
#endif
-
/*
-
* ===========================================================================
-
*
-
* Filename: Car.cpp
-
*
-
* Version: 1.0
-
* Created: 2017年05月26日 22时48分41秒
-
* Revision: none
-
* Compiler: gcc
-
* Author: (),
-
* Company:
-
*
-
* ===========================================================================
-
*/
-
#include<iostream>
-
using namespace::std;
-
/**
-
* 包含Car头文件
-
*/
-
#include<Car.h>
-
/* *
-
*类的行为的实现,定义
-
* */
-
void Car::run(){
-
cout << "car run" << endl;
-
}
-
void Car::shut(){
-
cout << "car shut" << endl;
-
}
-
void Car::setProperty(int price,int carnum,int cartype){
-
mCarPrice = price;
-
mCarNum = carnum;
-
mCarType = cartype;
-
if(mCarType == 1){
-
cout << "car type is one" <<endl;
-
}else if(mCarType == 2){
-
cout << "car type is two" <<endl;
-
}else if(mCarType == 3){
-
cout << "car type is three" <<endl;
-
}
-
}
-
void Car::print(){
-
cout <<"price:"<<mCarPrice<<"\n";
-
cout <<"carnum:"<<mCarNum<<"\n";
-
cout <<"carType"<<mCarType<<endl;
-
}
-
/*
-
* ===========================================================================
-
*
-
* Filename: CarTest.cpp
-
* Description:
-
* Version: 1.0
-
* Created: 2017年05月26日 23时18分01秒
-
* Revision: none
-
* Compiler: gcc
-
* Author: (),
-
* Company:
-
*
-
* ===========================================================================
-
*/
-
#include<iostream>
-
using namespace::std;
-
#include<Car.h>
-
/* *
-
*使用指针的方式来调用成员函数,类似结构体指针调用成员变量
-
* */
-
void usePoint(Car *mCar){
-
cout<<"==============="<<endl;
-
mCar ->setProperty(20000,20002,3);
-
mCar ->run();
-
mCar ->shut();
-
mCar ->print();
-
}
-
/* *
-
*使用引用的形式来调用成员函数,与对象的调用一致
-
* */
-
void userReference(Car &mCar){
-
cout << "==============="<<endl;
-
mCar.setProperty(10000,100001,2);
-
mCar.run();
-
mCar.shut();
-
mCar.print();
-
}
-
int main(int argc,char *argv[]){
-
Car mCar;
-
cout << "address:"<< &mCar <<endl;
-
cout <<"size of mCar"<< sizeof(mCar) <<endl;
-
mCar.setProperty(80001,88888,1);
-
mCar.run();
-
mCar.shut();
-
usePoint(&mCar);
-
userReference(mCar);
-
return 0;
-
}
以上是简单的关于类和对象以及一个简单的封装的国政
在成员函数中访问成员:
-
成员函数必须用对象来调用
-
Car mCar;
-
mCar.run();
-
在成员函数内部,访问数据成员或成员函数无需如此
-
void setProperty(int price,int carNum){
-
this->mPrice = price;
-
this->mCarNum = carNum;
-
}
this指针代表当前对象占用内存空间的地址:
-
void Tdate::set(int m ,int d,int y){
-
this->month = m;
-
this->day = d;
-
this->year = y;
-
}
通过指针来调用成员函数:
-
例子:
-
#include"tdate.h"
-
#include<iostream>
-
void func(Tdate *pDate){
-
pDate->print();
-
if(pDate->isLeepYear()){
-
cout<<"leepyear"<<endl;
-
}else{
-
cout<<"not leepyear"<<endl;
-
}
-
}
通过引用来调用成员函数:
-
#include"tdate.h"
-
#include<iostream>
-
void func(Tdate &pDate){
-
pDate.print();
-
if(pDate.isLeepYear()){
-
cout<<"leepyear"<<endl;
-
}else{
-
cout<<"not leepyear"<<endl;
-
}
-
}
类的成员函数的重载:
类的成员函数可以像普通函数一样进行重载
但是不同的类即使有相同的函数名也不算重载类的成员函数可以默认设置成员参数:
在类之外去定义这样一个类的行为OOP三大特性(面向对象的三个特性):
继承(inheritance):
多态(polymorphism):
封装(encapsulation):类背后隐藏的思想是数据抽象和封装
信息隐藏,隐藏对象的实现细节,不让外部直接访问
将数据成员和成员函数一起包装到 一个单元中 ,单元以类的形式实现
将数据成员和成员函数包装进类中,加上具体实现的隐藏,共同被称作封装,其结果是一个同时带有特征和行为的数据类型
封装类:定义类,定义其成员函数的过程称为封装类细节:
-
除非必须公开底层的实现细节,否则应该将所有字段指定为private
-
使数据成员私有,控制数据访问限制.增强了类的可维护性
-
隐藏方法的具体实现.向外部提供公开的接口.以供安全调用
信息隐藏是OOP最重要的特性之一,也是可以使用访问修饰符的原因
访问修饰符号:-
public
-
protected
-
private
信息隐藏的原因:
对模块的任何实现细节所做的更改不会影响使用该模块的代码
防止用户意外的修改数据
使模块易于使用和维护C++类(Class)总结
一、C++类的定义
C++中使用关键字 class 来定义类, 其基本形式如下:
class 类名
{public:
//行为或属性
protected:
//行为或属性
private:
//行为或属性
};
示例:
定义一个点(Point)类, 具有以下属性和方法:
■ 属性: x坐标, y坐标
■ 方法: 1.设置x,y的坐标值; 2.输出坐标的信息。
实现代码:class Point
{
public:
void setPoint(int x, int y);
void printPoint();
private:
int xPos;
int yPos;
};
代码说明:
上段代码中定义了一个名为 Point 的类, 具有两个私密属性, int型的xPos和yPos, 分别用来表示x点和y点。在方法上, setPoint 用来设置属性, 也就是 xPos 和 yPos 的值; printPoint 用来输出点的信息。
1 数据抽象和封装抽象是通过特定的实例抽取共同特征以后形成概念的过程。一个对象是现实世界中一个实体的抽象,一个类是一组对象的抽象。
封装是将相关的概念组成一个单元,然后通过一个名称来引用它。面向对象封装是将数据和基于数据的操作封装成一个整体对象,对数据的访问或修改只能通过对象对外提供的接口进行。2 类定义
几个重要名词:
(1) 类名
遵循一般的命名规则; 字母,数字和下划线组合,不要以数字开头。
(2) 类成员
类可以没有成员,也可以定义多个成员。成员可以是数据、函数或类型别名。所有的成员都必须在类的内部声明。
没有成员的类是空类,空类也占用空间。class People
{
};
sizeof(People) = 1;(3) 构造函数
构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。
(4) 成员函数
成员函数必须在类内部声明,可以在类内部定义,也可以在类外部定义。如果在类内部定义,就默认是内联函数。
3 类定义补充
3.1 可使用类型别名来简化类
除了定义数据和函数成员之外,类还可以定义自己的局部类型名字。
使用类型别名有很多好处,它让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。
class People
{
public:
typedef std::string phonenum; //电话号码类型
phonenum phonePub; //公开号码
private:
phonenum phonePri;//私人号码
};
3.2 成员函数可被重载
可以有多个重载成员函数,个数不限。
3.3 内联函数
有三种:
(1)直接在类内部定义。
(2)在类内部声明,加上inline关键字,在类外部定义。
(3)在类内部声明,在类外部定义,同时加上inline关键字。注意:此种情况下,内联函数的定义通常应该放在类定义的同一头文件中,而不是在源文件中。这是为了保证内联函数的定义在调用该函数的每个源文件中是可见的。3.4 访问限制
public,private,protected 为属性/方法限制的关键字。
3.5 类的数据成员中不能使用 auto、extern和register等进行修饰, 也不能在定义时进行初始化
如 int xPos = 0; //错;
例外:
静态常量整型(包括char,bool)数据成员可以直接在类的定义体中进行初始化,例如:static const int ia= 30;
4 类声明与类定义
4.1 类声明(declare)
class Screen;在声明之后,定义之前,只知道Screen是一个类名,但不知道包含哪些成员。只能以有限方式使用它,不能定义该类型的对象,只能用于定义指向该类型的指针或引用,声明(不是定义)使用该类型作为形参类型或返回类型的函数。
void Test1(Screen& a){};void Test1(Screen* a){};
4.2 类定义(define)
在创建类的对象之前,必须完整的定义该类,而不只是声明类。所以,类不能具有自身类型的数据成员,但可以包含指向本类的指针或引用。
class LinkScreen
{public:
Screen window;
LinkScreen* next;
LinkScreen* prev;
}; //注意,分号不能丢因为在类定义之后可以接一个对象定义列表,可类比内置类型,定义必须以分号结束:
class LinkScreen{ /* ... */ };class LinkScreen{ /* ... */ } scr1,scr2;
5 类对象
定义类对象时,将为其分配存储空间。
Sales_item item; //编译器分配了足以容纳一个 Sales_item 对象的存储空间。item 指的就是那个存储空间。
6 隐含的 this 指针
成员函数具有一个附加的隐含形参,即 this指针,它由编译器隐含地定义。成员函数的函数体可以显式使用 this 指针。
6.1 何时使用 this 指针
当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。最常见的情况是在这样的函数中使用 this:该函数返回对调用该函数的对象的引用。
class Screen
{
...
public:
Screen& set(char);
};Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
7 类作用域每个类都定义了自己的作用域和唯一的类型。
类的作用域包括:类的内部(花括号之内), 定义在类外部的成员函数的参数表(小括号之内)和函数体(花括号之内)。
class Screen
{
//类的内部
...
};
//类的外部
char Screen::get(index r, index c) const
{
index row = r * width; // compute the row location
return contents[row + c]; // offset by c to fetch specified character
}
注意:成员函数的返回类型不一定在类作用域中。可通过 类名::来判断是否是类的作用域,::之前不属于类的作用域,::之后属于类的作用域。例如Screen:: 之前的返回类型就不在类的作用域,Screen:: 之后的函数名开始到函数体都是类的作用域。
class Screen
{
public:
typedef std::string::size_type index;
index get_cursor() const;
};
Screen::index Screen::get_cursor() const //注意:index前面的Screen不能少
{
return cursor;
}
该函数的返回类型是 index,这是在 Screen 类内部定义的一个类型名。在类作用域之外使用,必须用完全限定的类型名 Screen::index 来指定所需要的 index 是在类 Screen 中定义的名字。
二 构造函数
构造函数是特殊的成员函数,用来保证每个对象的数据成员具有合适的初始值。
构造函数名字与类名相同,不能指定返回类型(也不能定义返回类型为void),可以有0-n个形参。
在创建类的对象时,编译器就运行一个构造函数。1 构造函数可以重载
可以为一个类声明的构造函数的数量没有限制,只要每个构造函数的形参表是唯一的。
class Sales_item;
{
public:
Sales_item(const std::string&);
Sales_item(std::istream&);
Sales_item(); //默认构造函数
};
2 构造函数自动执行
只要创建该类型的一个对象,编译器就运行一个构造函数:
Sales_item item1("0-201-54848-8");
Sales_item *p = new Sales_item();
第一种情况下,运行接受一个 string 实参的构造函数,来初始化变量item1。第二种情况下,动态分配一个新的 Sales_item 对象,通过运行默认构造函数初始化该对象。
3 构造函数初始化式
与其他函数一样,构造函数具有名字、形参表和函数体。与其他函数不同的是,构造函数可以包含一个构造函数初始化列表:
Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0)
{ }
构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
构造函数可以定义在类的内部或外部。构造函数初始化只在构造函数的定义中指定。
构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。初始化列表属于初始化阶段(1),构造函数函数体中的所有语句属于计算阶段(2)。
初始化列表比构造函数体先执行。不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。3.1 哪种类需要初始化式
const 对象或引用类型的对象,可以初始化,但不能对它们赋值,而且在开始执行构造函数的函数体之前要完成初始化。
初始化 const 或引用类型数据成员的唯一机会是构造函数初始化列表中,在构造函数函数体中对它们赋值不起作用。
没有默认构造函数的类类型的成员,以及 const 或引用类型的成员,必须在初始化列表中完成初始化。class ConstRef
{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
ConstRef::ConstRef(int ii)
{
i = ii; // ok
ci = ii; // error
ri = i; //
}
应该这么初始化:
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }
3.2 成员初始化的次序
每个成员在构造函数初始化列表中只能指定一次。重复初始化,编译器一般会有提示。
成员被初始化的次序就是定义成员的次序,跟初始化列表中的顺序无关。
3.3 初始化式表达式初始化式可以是任意表达式
Sales_item(const std::string &book, int cnt, double price): isbn(book), units_sold(cnt), revenue(cnt * price) { }
3.4 类类型的数据成员的初始化式
初始化类类型的成员时,要指定实参并传递给成员类型的一个构造函数,可以使用该类型的任意构造函数。
Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}3.5 类对象的数据成员的初始化
在类A的构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化。类类型的数据成员,运行该类型的默认构造函数来初始化。
内置或复合类型的成员的初始值依赖于该类对象的作用域:在局部作用域中不被初始化,在全局作用域中被初始化为0。假设有一个类A,
class A
{
public:
int ia;
B b;
};
A类对象A a;不管a在局部作用域还是全局作用域,b使用B类的默认构造函数来初始化,ia的初始化取决于a的作用域,a在局部作用域,ia不被初始化,a在全局作用域,ia初始化0。
4 默认构造函数
不含形参的构造函数就是默认构造函数。
只要定义一个对象时没有提供初始化式,就使用默认构造函数。如: A a;
为所有形参提供默认实参的构造函数也定义了默认构造函数。例如:class A
{
public:A(int a=1,char c =''){}
private:
int ia;
char c1;
};4.1 合成的默认构造函数
只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
一个类只要定义了一个构造函数,编译器也不会再生成默认构造函数。
建议:
如果定义了其他构造函数,也提供一个默认构造函数。
如果类包含内置或复合类型(如 int& 或 string*)的成员,它应该定义自己的构造函数来初始化这些成员。每个构造函数应该为每个内置或复合类型的成员提供初始化。
5 隐式类类型转换
5.1 只含单个形参的构造函数能够实现从形参类型到该类类型的一个隐式转换class A
{
public:
A(int a)
{
ia =a;
}
bool EqualTo(const A& a)
{
return ia == a.ia;
}
private:
int ia;
};
A a(1);
bool bEq = false;
bEq = a.EqualTo(1);//参数为1,实现从int型到A的隐式转换
5.2抑制由构造函数定义的隐式转换
通过将构造函数声明为 explicit,来防止在需要隐式转换的上下文中使用构造函数:class A
{
public:
explicit A(int a )
{
ia =a;
}
bool EqualTo(const A& a)
{
return ia == a.ia;
}
private:
int ia;
};
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为 explicit。将构造函数设置为 explicit 可以避免错误。
三 复制控制
1 复制构造函数
1.1 几个要点(1) 复制构造函数
复制构造函数是一种特殊构造函数,只有1个形参,该形参(常用 const &修饰)是对该类类型的引用。
class Peopel
{
public:
Peopel();//默认构造函数
Peopel(const Peopel&);//复制构造函数
~Peopel();//析构函数
};
当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。
Peopel a1; Peopel a2 = a1;
当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数。
Peopel Func(Peopel b){...}
(2)析构函数
析构函数是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。
析构函数可用于释放构造对象时或在对象的生命期中所获取的资源。
不管类是否定义了自己的析构函数,编译器都自动执行类中非 static 数据成员的析构函数。
(3) 复制控制复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。
(4) 两种初始化形式
C++ 支持两种初始化形式:直接初始化和复制初始化。直接初始化将初始化式放在圆括号中,复制初始化使用 = 符号。
对于内置类型,例如int, double等,直接初始化和复制初始化没有区别。
对于类类型:直接初始化直接调用与实参匹配的构造函数;复制初始化先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。直接初始化比复制初始化更快。
(5)形参和返回值当形参或返回值为类类型时,由该类的复制构造函数进行复制。
(6)初始化容器元素
复制构造函数可用于初始化顺序容器中的元素。例如:
vector<string> svec(5);
编译器首先使用 string 默认构造函数创建一个临时值,然后使用复制构造函数将临时值复制到 svec 的每个元素。
(7)构造函数与数组元素如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。
如果使用常规的花括号括住的数组初始化列表来提供显式元素初始化式,则使用复制初始化来初始化每个元素。根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素:
Sales_item primer_eds[] = { string("0-201-16487-6"),
string("0-201-54848-8"),
string("0-201-82470-1"),
Sales_item()
};
1.2 合成的复制构造函数
(1)合成的复制构造函数
如果没有定义复制构造函数,编译器就会为我们合成一个。
合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
逐个成员初始化:合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。
例外:如果一个类具有数组成员,则合成复制构造函数将复制数组。复制数组时合成复制构造函数将复制数组的每一个元素。
1.3 定义自己的复制构造函数
(1) 只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数,也可以复制。
class Peopel
{
public:
std::string name;
unsigned int id;
unsigned int age;
std::string address;
};
(2) 有些类必须对复制对象时发生的事情加以控制。
例如,类有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义自己的复制构造函数。
最好显式或隐式定义默认构造函数和复制构造函数。如果定义了复制构造函数,必须定义默认构造函数。1.4 禁止复制
有些类需要完全禁止复制。例如,iostream 类就不允许复制。延伸:容器内元素不能为iostream
为了防止复制,类必须显式声明其复制构造函数为 private。
2 赋值操作符
与复制构造函数一样,如果类没有定义自己的赋值操作符,则编译器会合成一个。(1)重载赋值操作符
Sales_item& operator=(const Sales_item &);
(2)合成赋值操作符合成赋值操作符会逐个成员赋值:右操作数对象的每个成员赋值给左操作数对象的对应成员。除数组之外,每个成员用所属类型的常规方式进行赋值。对于数组,给每个数组元素赋值。
(3)复制和赋值常一起使用
一般而言,如果类需要复制构造函数,它也会需要赋值操作符。
3 析构函数
构造函数的用途之一是自动获取资源;与之相对的是,析构函数的用途之一是回收资源。除此之外,析构函数可以执行任意类设计者希望在该类对象的使用完毕之后执行的操作。
(1) 何时调用析构函数- 撤销(销毁)类对象时会自动调用析构函数。
- 变量(类对象)在超出作用域时应该自动撤销(销毁)。
- 动态分配的对象(new A)只有在指向该对象的指针被删除时才撤销(销毁)。
- 撤销(销毁)一个容器(不管是标准库容器还是内置数组)时,也会运行容器中的类类型元素的析构函数(容器中的元素总是从后往前撤销)。
(2)何时编写显式析构函数
如果类需要定义析构函数,则它也需要定义赋值操作符和复制构造函数,这个规则常称为三法则:如果类需要析构函数,则需要所有这三个复制控制成员。
(3)合成析构函数合成析构函数按对象创建时的逆序撤销每个非 static 成员,因此,它按成员在类中声明次序的逆序撤销成员。
对于每个类类型的成员,合成析构函数调用该成员的析构函数来撤销对象。
合成析构函数并不删除指针成员所指向的对象。 所以,如果有指针成员,一定要定义自己的析构函数来删除指针。
析构函数与复制构造函数或赋值操作符之间的一个重要区别:即使我们编写了自己的析构函数,合成析构函数仍然运行。
四 友元
友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。
友元可以出现在类定义的内部的任何地方。
友元不是授予友元关系的那个类的成员,所以它们不受声明出现部分的访问控制影响。建议:将友元声明成组地放在类定义的开始或结尾。
1 友元类
class Husband
{
public:
friend class Wife;
private:
double money;//钱是老公私有的,别人不能动,但老婆除外
};
class Wife
{
public:
void Consume(Husband& h)
{
h.money -= 10000;//老婆可以花老公的钱
}
};
Husband h;
Wife w;
w.Consume(h);
2 使其他类的成员函数成为友元
class Husband; //1.声明Husband
class Wife //2.定义Wife类
{
public:
void Consume(Husband& h);
};
class Husband //3.定义Husband类
{
public:
friend void Wife::Consume(Husband& h);//声明Consume函数。
private:
double money;//钱是老公私有的,别人不能动,但老婆除外
};
void Wife::Consume(Husband& h) //4.定义Consume函数。
{
h.money -= 10000;//老婆可以花老公的钱
}
注意类和函数的声明和定义的顺序:
(1)声明类Husband
(2)定义类Wife,声明Consume函数
(3)定义类Husband
(4)定义Consume函数。
五 static 类成员
static 成员,有全局对象的作用,但又不破坏封装。
1 static 成员变量
static 数据成员是与类关联的对象,并不与该类的对象相关联。
static 成员遵循正常的公有/私有访问规则。
2 使用 static 成员而不是全局对象有三个优点。
(1) static 成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突。
(2) 可以实施封装。static 成员可以是私有成员,而全局对象不可以。
(3) 通过阅读程序容易看出 static 成员是与特定类关联的,这种可见性可清晰地显示程序员的意图。
3 static 成员函数
在类的内部声明函数时需要添加static关键字,但是在类外部定义函数时就不需要了。
因为static 成员是类的组成部分但不是任何对象的组成部分,所以有以下几个特点:
1) static 函数没有 this 指针
2) static 成员函数不能被声明为 const (将成员函数声明为 const 就是承诺不会修改该函数所属的对象)3) static 成员函数也不能被声明为虚函数
4 static 数据成员
static 数据成员可以声明为任意类型,可以是常量、引用、数组、类类型,等等。
static 数据成员必须在类定义体的外部定义(正好一次),并且应该在定义时进行初始化。建议:定义在类的源文件中名,即与类的非内联函数的定义同一个文件中。注意,定义时也要带上类类型+"::"
double Account::interestRate = 0.035;
5 特殊的静态常量整型成员
静态常量整型数据成员可以直接在类的定义体中进行初始化,例如:
static const int period = 30;当然char 可以转换成整形,也是可以的, static const char bkground = '#';
6 其他
(1)static 数据成员的类型可以是该成员所属的类类型。非 static 成员只能是自身类对象的指针或引用
class Screen{
public:
// ...
private:
static Screen src1; // okScreen *src2; // ok
Screen src3; // error
};
(2)非 static 数据成员不能用作默认实参,static 数据成员可用作默认实参class Screen
{
public:
Screen& clear(char = bkground);
private:
static const char bkground = '#';//static const整形变量可以在类内部初始化。
};这段时间看了不少C++代码,也写了一个小项目,这篇文章来说一下我见到过的比较通用的两种多线程封装方式,实现平台为Linux
首先说说地一种线程封装方式,也是我们平常见得最多的一种封装方式,是用面向对象中的继承,多态来实现的,下面来看具体的代码,这些代码使我随手写的,主要是为了说明思想,如果要用到项目中还需要完善。[cpp] view plain copy
- /*************************************************************************
- > File Name: Thread.cpp
- > Author: KevinFu
- > Mail: kevinfu1985@gmail.com
- > Created Time: 2014年05月26日 星期一 21时27分51秒
- ************************************************************************/
- #include<iostream>
- #include <pthread.h>
- using namespace std;
- class Thread
- {
- public:
- Thread(string name = "Unknown")
- {
- }
- virtual ~Thread()
- {
- }
- void Start()
- {
- pthread_create(&m_ThreadID, NULL, ThreadFunc, this);
- }
- static void* ThreadFunc(void* pth)
- {
- Thread* p = static_cast<Thread*>(pth);
- p->Run();
- }
- virtual void Run() = 0;
- private:
- pthread_t m_ThreadID;
- };
- class Test:public Thread
- {
- public:
- virtual void Run()
- {
- while(1)
- {
- cout<<"In Test::Run()"<<endl;
- }
- }
- };
- int main()
- {
- Thread* thread1 = new Test();
- thread1->Start();
- sleep(1);
- }
这里我们用Thread这个类来实现线程的封装,用户自己要启用的线程类都要继承这个Thread类,在Thread类的Start函数里,调用了pthread_create创建了一个线程,并将ThreadFunc设置为线程函数,把线程的this指针传递给了这个函数,在这个线程函数里调用了
虚函数Run,这个Run函数最终会利用多态调用到用户线程类的Run函数,这就是上面代码的基本原理,比较简单,这也是面向对象用的比较多的地方,也可以说是他的强大的地方,不过用起来比较别扭,我们公司的关于线程的封装就是用的这种方法,个人感觉很麻烦,不好用,用户要想启用一个线程,必须要继承Thread这个类,而且要覆盖Run这个虚函数,很麻烦。
下面这种线程封装方式是用基于对象的封装方式,上面的封装方式是面向对象的,这种方式我们用到了boost库中的神器boost::function, boost::bind,这个神器原理就是帮定一个函数对象,函数对象可以包含参数,我们可以利用这个神器调用任意全局函数,甚至类的成员函数,
而不需要继承自任何类,下面来看具体代码[cpp] view plain copy
- /*************************************************************************
- > File Name: Thread.cpp
- > Author: KevinFu
- > Mail: kevinfu1985@gmail.com
- > Created Time: 2014年05月26日 星期一 20时41分07秒
- ************************************************************************/
- #include<iostream>
- #include <pthread.h>
- #include <boost/function.hpp>
- #include <boost/bind.hpp>
- using namespace std;
- class Thread
- {
- typedef boost::function<void(void)> ThreadFunctionCallBack;
- public:
- Thread(ThreadFunctionCallBack cb, string name = "Unknow")
- :m_cb(cb)
- {
- }
- ~Thread()
- {
- }
- void Start(void)
- {
- pthread_create(&m_ThreadID, NULL, ThreadFunction, this);
- }
- static void* ThreadFunction(void* obj)
- {
- Thread* thread = static_cast<Thread*>(obj);
- thread->m_cb();
- }
- private:
- ThreadFunctionCallBack m_cb;
- pthread_t m_ThreadID;
- };
- class Test
- {
- public:
- void run(void)
- {
- cout<<"In test::run()"<<endl;
- }
- };
- int main()
- {
- Test t;
- Thread thread1(boost::bind(&Test::run, &t));
- thread1.Start();
- usleep(100);
- }
我们可以看到Test类不需要继承自Thread类,我们直接可以将boost::bind帮定的函数对象传递给Thread的构造函数,这里为了简单我们把this指针传递给了ThreadFunc,我们完全可以传递任何参数给ThreadFunc, 因为我们不需要使用多态,这就是基于对象的思想,比面向对象似乎更直接,使用起来更方便,更简单。
C++11中的tr1中已经包含了boost这两大功能,boost确实是代表着最高水平的C++库,其中有很多东西值得学习,也有很多东西即将加入到标准C++库中。 -
c# -- 对象销毁和垃圾回收
2018-08-09 15:15:45有些对象需要显示地销毁代码来释放资源,比如打开的文件资源,锁,操作系统句柄和非托管对象。在.NET中,这就是所谓的对象销毁,它通过IDisposal接口来实现。不再使用的对象所占用的内存管理,必须在某 -
对C++面向对象的编程的理解
2019-01-16 18:05:06面向对象方法历经了30多年的研究和...长期以来,人与计算机之间仍存在着较大的隔阂,人认识问题时的认识空间和计算机处理问题时的方法空间的不一致,而面向对象技术有助于减小这一隔阂、并使这两个空间尽量趋于一致... -
谈谈我对面向对象编程的经验看法
2017-03-14 19:08:16关于面向对象编程方式,如何使用才是较为简洁高效的方式,如何编程才能编写出可维护性高的程序? -
java中直接打印对象
2021-02-28 15:02:28java中直接打印对象,会调用对象.toString()方法。如果没有重写toString()方法会输出"类名+@+hasCode"值,hasCode是一个十六进制数//没有重写toString()方法的类class Person{private String name;//构造方法public ... -
[Python图像识别] 四十五.目标检测入门普及和ImageAI“傻瓜式”对象检测案例详解 (1)
2021-07-26 13:49:17该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门、OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子、图像增强技术、图像分割等,...希望文章对您有所帮助,如果有不足之处,还请海涵~ -
每日一面 - java中,描述一下什么情况下,对象会从年轻代进入老年代?
2021-01-03 09:04:05启用分代 GC 的,在发生 Young GC,更准确地说是在 Survivor 区复制的时候,存活的对象的分代年龄会加1。 当分代年龄 = -XX:MaxTenuringThreshold 指定的大小时,对象进入老年代 还有动态晋升到老年代的机制,首先... -
js 对象解构_JS对象:解构
2020-06-28 11:14:44自一开始以来,JavaScript就一直困扰着其“原型继承”系统,人们对其产生了误解和尴尬,这主要是因为“继承”根本不是JS的工作原理,而试图做到这一点只会导致我们陷入困境和困惑。必须铺平用户土地的帮助库。 相反... -
String对象和数组的异同
2018-07-23 17:54:231.创建字符串和数组的方法 1.1 创建字符串的方法 A....字符串对象创建: new String(""); 1.2 创建数组的方法 A. var arr = [ element.....]; B. var arr = ne... -
C++中面向对象的理解
2018-09-29 14:44:191、对于OO(面向对象)的含义,并非每一个人的看法都是同样的。 即使在如今。假设问十个人,可能会得到15种不同的答案。差点儿全部的人都会允许继承和多态是OO中的概念。大多数人还会再加上封装。 另一些人会加上... -
面向过程和面向对象
2016-08-30 17:25:441、引言 机算机科学是一门应用科学...但是,会编程和编好程绝对是两码事,同样的程序员,有的人几年之后成为了架构师,有的人却还在不停地coding,只不过ctrl-c、ctrl-v用得更加纯熟了。在中国,编程人员最终的归途 -
Python爬虫库BeautifulSoup获取对象(标签)名,属性,内容,注释
2020-12-06 14:39:48如何利用Python爬虫库BeautifulSoup获取对象(标签)名,属性,内容,注释等操作下面就为大家介绍一下一、Tag(标签)对象1.Tag对象与XML或HTML原生文档中的tag相同。from bs4 import BeautifulSoupsoup = BeautifulSoup('... -
面向过程编程与面向对象编程的区别是什么?
2019-04-28 11:09:43面向过程编程就是一步一步的按照过程来进行,面向流程的;简单来说就是先分析出解决问题所需要的步骤,然后用函数一步步的调用实现。 例如:你需要网上购物,那么你首先需要进入到这个...面向对象编程是面向问题中的... -
第十三天学Python:类(1):什么是面向对象,什么是类
2020-04-29 14:07:05什么是类?什么是面向对象?面向过程和面向对象的区别是什么? -
十分钟搞懂JSON(JSON对象---JSON字符串---对象 之间的区别)
2020-08-31 10:34:49好记性不如烂笔头,相信我,看了之后你会彻底搞懂JSON ...我们传输信息在JAVA中都是对象传输,你单纯的传一堆String过去又有什么意义,如果你想传输一堆String,你也是通过对象来传输。所以总结来就是 Lis. -
C语言--面向对象编程之封装
2022-03-14 20:54:45文章目录前言一、面向对象基本概念什么是对象?对象与类面向对象的编程方式二、C语言实现...此对象非彼对象,虽然有时候此对象又可以是你脑袋中的对象,那让我们从我们误解的对象开始了解吧,虽然我没有,但是用一下自 -
面向对象分析和设计(第3版)读书笔记 - 认识 OO
2016-03-07 16:20:59面向对象分析和设计(第3版)读书笔记 - 认识 OO -
基于Java基础-面向对象实现植物大战僵尸简易版
2019-03-31 15:15:44从零开始学习的Java的第一个月,在面向对象(OOP)的学习中,跟着讲师完成了飞机大战的游戏编码。第二个月开始接触API,心血来潮便结合API中的集合、多线程开始植物大战僵尸的编码,从游戏模式的设计到游戏内容的实现... -
JS创建类和对象
2018-07-08 17:31:34JavaScript 创建类/对象的几种方式在JS中,创建对象(Create Object)并不完全是我们时常说的创建类对象,JS中的对象强调的是一种复合类型,JS中创建对象及对对象的访问是极其灵活的。JS对象是一种复合类型,它允许... -
NFT和元宇宙之间有什么联系
2022-03-29 16:58:17元宇宙的技术和投资目标是将物理现实与虚拟现实联合起来,创建一个数字空间,在此空间,我们可以聚集在一起,并执行影响我们化身的虚拟世界以及现实和物理世界的行动。 在元宇宙中,我们会发现一个新的生态系统,... -
C语言和C++有什么区别?
2020-12-25 11:42:48还有,到1983年后期,贝尔实验室里负责开发和支持UNIX、生产AT&T的3B系列计算机的分支机构开始对C++ 感兴趣,它已经希望为C++ 工具的开发投入一些资源。不幸的是对于使C++ 的发展由一个人独舞转变为一个公司的... -
unity开发排坑指南(关于面向对象)
2020-02-19 17:11:31今天写这篇的原因呢,也是在最近自己在游戏开发的过程中,发现了开发的一些固有观念和思维定势给我们的开发埋了很大的坑,所以想分享一下,希望大家在开发的过程中避免这样的问题,因为优化实在是太难了,还是找时间... -
超硬核!十万字c++题,让你秒杀老师和面试官(上)
2021-04-21 10:46:15const Stock & Stock::topval (②const Stock & s) ③const ①处const:确保返回的Stock对象在以后的使用中不能被修改 ②处const:确保此方法不修改传递的参数 S ③处const:保证此方法不修改调用它的对象,const... -
块存储、文件存储、对象存储及内容分发CDN
2018-11-16 10:49:12在实际搭建私有云网络中常用NAS结构,而部署传统RAC集群的时候也需要配SAN网络,对这几种存储方式有直观的了解,却没能理论化系统化的梳理,今天看到一篇讲这方面的文章,我也就搞了个拿来主义,收录到自己的BLOG下...