精华内容
下载资源
问答
  • 和对象单独在一起说什么
    千次阅读 多人点赞
    2021-07-22 22:46:33

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦
    Python是一门面向对象的语言,所以,本文将详细介绍面向对象的思想介绍类,对象以及方法。
    干货满满,建议收藏,需要用到时常看看。 小伙伴们如有问题及需要,欢迎踊跃留言哦~ ~ ~。

    前言

    说点题外话:这两天看到郑州市区洪灾的视频真的很让人心痛,洪水真的太可怕了。不过&

    更多相关内容
  • Java类和对象 详解(一)

    万次阅读 多人点赞 2016-10-06 20:48:02
    一、面向对象简述面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计...

    一、面向对象简述

    面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,在70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计思路,才形成C++,而由C++产生了Java这门面向对象的编程语言。

    但是在面向对象设计之前,广泛采用的是面向过程,面向过程只是针对于自己来解决问题。面向过程的操作是以程序的基本功能实现为主,实现之后就完成了,也不考虑修改的可能性,面向对象,更多的是要进行子模块化的设计,每一个模块都需要单独存在,并且可以被重复利用,所以,面向对象的开发更像是一个具备标准的开发模式。

    在面向对象定义之中,也规定了一些基本的特征:
    (1)封装:保护内部的操作不被破坏;
    (2)继承:在原本的基础之上继续进行扩充;
    (3)多态:在一个指定的范围之内进行概念的转换。

    对于面向对象的开发来讲也分为三个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。

    二、类与对象的基本概念

    类与对象时整个面向对象中最基础的组成单元。

    :是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
    对象:对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。

    可以一句话来总结出类和对象的区别:类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。

    三、类与对象的定义和使用

    在Java中定义类,使用关键字class完成。语法如下:

    class 类名称 {
             属性 (变量) ;
             行为 (方法) ;
    }

    范例:定义一个Person类

    class Person {     // 类名称首字母大写
        String name ;
        int age ;
        public void tell() {        // 没有static
              System.out.println("姓名:" + name + ",年龄:" + age) ;
             }
    }

    类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下:

    (1)格式一:声明并实例化对象

    类名称 对象名称 = new 类名称 () ;

    (2)格式二:先声明对象,然后实例化对象:

    类名称 对象名称 = null ;
    对象名称 = new 类名称 () ;

    引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。

    当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
    对象.属性:表示调用类之中的属性;
    对象.方法():表示调用类之中的方法。

    范例:使用对象操作类

    package com.wz.classandobj;
    
    class Person { 
        String name ;
        int age ;
        public void get() {
            System.out.println("姓名:" + name + ",年龄:" + age);
        }
    }
    
    public class TestDemo {
            public static void main(String args[]) {
                Person per = new Person() ;// 声明并实例化对象
                per.name = "张三" ;//操作属性内容
                per.age = 30 ;//操作属性内容
                per.get() ;//调用类中的get()方法
            }
    }

    运行结果:

    姓名:张三,年龄:30

    以上完成了一个类和对象的操作关系,下面换另外一个操作来观察一下:

    package com.wz.classandobj;
    
    class Person { 
        String name ;
        int age ;
        public void get() {
            System.out.println("姓名:" + name + ",年龄:" + age);
        }
    }
    
    public class TestDemo {
            public static void main(String args[]) {
                Person per = null;//声明对象
                per = new Person() ;//实例化对象
                per.name = "张三" ;//操作属性内容
                per.age = 30 ;//操作属性内容
                per.get() ;//调用类中的get()方法
            }
    }

    运行结果:

    姓名:张三,年龄:30

    那么,问题来了,以上两种不同的实例化方式有什么区别呢?
    我们从内存的角度分析。首先,给出两种内存空间的概念:
    (1)堆内存:保存对象的属性内容。堆内存需要用new关键字来分配空间;
    (2)栈内存:保存的是堆内存的地址(在这里为了分析方便,可以简单理解为栈内存保存的是对象的名字)。

    1

    任何情况下,只要看见关键字new,都表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。

    于是,上面两种对象实例化对象方式内存表示如下:
    这里写图片描述

    两种方式的区别在于①②,第一种声明并实例化的方式实际就是①②组合在一起,而第二种先声明然后实例化是把①和②分步骤来。

    另外,如果使用了没有实例化的对象,结果如何?
    如下:

    package com.wz.classandobj;
    
    class Person { 
        String name ;
        int age ;
        public void get() {
            System.out.println("姓名:" + name + ",年龄:" + age);
        }
    }
    
    public class TestDemo {
            public static void main(String args[]) {
                Person per = null;//声明对象
                //per = new Person() ;//实例化对象
                per.name = "张三" ;//操作属性内容
                per.age = 30 ;//操作属性内容
                per.get() ;//调用类中的get()方法
            }
    }

    运行结果:

    Exception in thread "main" java.lang.NullPointerException
        at com.wz.classandobj.TestDemo.main(TestDemo.java:15)

    此时,程序只声明了Person对象,但并没有实例化Person对象(只有了栈内存,并没有对应的堆内存空间),则程序在编译的时候不会出现任何的错误,但是在执行的时候出现了上面的错误信息。这个错误信息表示的是“NullPointerException(空指向异常)”,这种异常只要是应用数据类型都有可能出现。

    四、对象引用传递初步分析

    引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。

    下面通过若干个程序,以及程序的内存分配图,来进行代码的讲解。

    我们来看一个范例:

    class Person {     
             String name ;
             int age ;
             public void tell() {        
                       System.out.println("姓名:" + name + ",年龄:" + age) ;
             }
    }
    public class TestDemo {
             public static void main(String args[]) {
                       Person per1 = new Person() ;         // 声明并实例化对象
                       per1.name = "张三" ;
                       per1.age = 20 ;
                       Person per2 = per1 ;  // 引用传递
                       per2.name = "李四" ;
                       per1.tell() ;
             }
    }

    对应的内存分配图如下:

    11

    再来看另外一个:

    class Person {
             String name ;
             int age ;
             public void tell() {
                       System.out.println("姓名:" + name + ",年龄:" + age) ;
             }
    }
    public class TestDemo {
             public static void main(String args[]) {
                       Person per1 = new Person() ;         // 声明并实例化对象
                       Person per2 = new Person() ;
                       per1.name = "张三" ;
                       per1.age = 20 ;
                       per2.name = "李四" ;
                       per2.age = 30 ;
                       per2 = per1 ;// 引用传递
                       per2.name = "王五" ;
                       per1.tell() ;
             }
    }

    对应的内存分配图如下:
    12

    垃圾:指的是在程序开发之中没有任何对象所指向的一块堆内存空间,这块空间就成为垃圾,所有的垃圾将等待GC(垃圾收集器)不定期的进行回收与空间的释放。

    未完待续。。。

    展开全文
  • 大厂,我同事搞对象

    千次阅读 多人点赞 2021-08-18 16:48:24
    更不消被大大拉长的工作时间,让他们将一天中大部分醒着的时间都贡献工位前、会议室中、茶水间厕所隔间里。一次原本单纯地提需求,一场原本平淡无奇的团建,一个原本见怪不怪的分享会,都有可能让两个原本沉迷...

    本文转载自虎嗅网

    在动辄一层几百人、一栋楼几千人、一个园区好几栋楼的互联网公司里,有着大把的适婚单身男女。

    能进入大厂一起工作,意味着他们在社会阶层、知识素养、收入水平等各方面,比在社交媒体上匹配的陌生人,有着更高的相似度。

    更不消说被大大拉长的工作时间,让他们将一天中大部分醒着的时间都贡献在工位前、会议室中、茶水间和厕所隔间里。一次原本单纯地提需求,一场原本平淡无奇的团建,一个原本见怪不怪的分享会,都有可能让两个原本沉迷工作的年轻人,在电光火石之间,擦出爱的小火花。像两个齿轮突然找到彼此,办公室恋情随时都有可能转动起来。

    27岁的张静在字节跳动工作的时候,遇到了现在的男友。他们一起共事,关系很“铁”。在男方离职之后,没有了同事关系的束缚,两个人迅速成为恋人。

    王珊珊在前公司一次团建时,一次回眸间与本无交集的同事一见钟情,成为情侣之后,他们双双跳槽到腾讯。

    饿了么的李书籍和同事坠入爱河,缘于一次路过他正在做培训分享的会议室,被他的工作能力戳中。

    一个人有机会老在你面前晃,更容易产生好感。办公室恋情的发生,用社会心理学中的(简单)曝光效应可以部分解释。曝光效应也可以称作多看效应,指的是人们会偏好自己熟悉的事物,是人们在熟悉陌生事物后产生的结果。人们对经常暴露在眼前的人或事物会产生好感,随着熟悉程度加深,喜欢程度也会加深。

    办公室恋情在一些互联网公司中是一种禁忌,尤其是大厂,有可能直接在员工手册中黑纸白字写明禁止办公室恋情。多位字节员工告诉字母榜:办公室恋情在字节是红线,如果同一条业务线的同事在一起,至少得有一人离职。一位阿里的员工则告诉字母榜,办公室恋情在阿里至少跨BU才可以,否则也得离职一人。

    在华为的一名实习生则表示,恋爱双方如果同样是“小兵”,那就问题不大,但是如果一方升职,那就得有一方调岗了。近来脉脉上有人盘点各互联网大厂的办公室恋情规定,提到华为是支持的、甚至有内部相亲会,此消息并不完全。一定限度的鼓励和严格的红线并存,是当代互联网企业对待办公室恋爱的普遍态度。

    脉脉的匿名讨论,部分说法有争议。

    对同业务线、同部门、同组,最基本的,对上下级之间的办公室恋情持有的限制措施,其核心考量还是在利益冲突。与师生恋类似,并不是说两个人想着避免就可以避免得了,其关系中本身就包含着权力的不对等和利益关系千丝万缕的交叉。

    年轻人自己也多有顾虑。要不要开始谈恋爱,需要想一想;要不要公开,也是个问题。对于办公室恋情,大多数人会选择隐瞒。在2012年天际网(职场社交网站,已于2015年关停)的一项覆盖9个国家、4000余人的调查显示,超过43%的人表示不愿公开办公室恋情。除了公司的既有规定,自身专业度受质疑、被“八卦”、分手后尴尬等,是年轻人面对办公室情愫常见的担心。

    不过规定归规定,顾虑归顾虑,当爱情真的来了,人有情难自禁,又有几人能不让感情的激流冲破理性的藩篱?字母榜和4位办公室恋情的亲历者聊了聊,其中3位是互联网大厂员工,还有1位供职于某内容平台,以下为受访者的口述实录。

    (文章中张静、李书籍、王珊珊、舒克均为化名)

    张静,27岁,女,字节(已离职)

    “我了解他整个人。”

    办公室恋情最大的问题就是,这玩意是一点点发展的,最开始的时候,你俩可能压根没往那方面想,结果是什么都聊,什么都说,该知道的不该知道的,都知道了!

    我和我男友都是2018年校招进的字节,而且被分到了同一个组。我们那个组有六个人,大家关系特别好。而我和他的关系,又格外铁。他是狮子座,我是天秤,他就挺强势的,在工作里有时候就比较凶,而且还老调侃我,有时候甚至觉得这人真烦人呢。那时候他有女朋友,我有男朋友,还彼此会倾诉自己的事情。

    入职差不多两三个月的时候吧,我和我前男友分手了,他也和前女友分手了,这就是纯巧合了。不巧合的是,他来和我倾诉,说自己有多爱她啊,多不甘心啊,诸如此类的。而且呢,他没多久居然就去追了一个大学同学,还在一起了。那时候我们就损他,可真渣啊,变真快。

    2019年底的时候,他就离职了。我们关系好嘛,就也会发发信息什么的。也是在这个时候,两个人之间的状态开始“不对”了,有暧昧的感觉了。

    我记得有次,我下小夜班,半夜1点钟坐上滴滴,他的电话就进来了。他问我:下班了吗?听他的声音还是有睡意的,我反应过来,他这大概是上了闹钟,在我下班的点醒来给我打电话的吧?就在那一刻我觉得不得了啊!我恋爱了啊!

    在一起的时候,我还在字节,我们也会觉得说,公开可能有些不好。毕竟我俩不仅是一个业务线的,甚至之前还是一个组的。虽然他离职了我们才在一起,但是还是有点可疑不是?在我离职之前有俩月,我俩都地下恋情。

    我们不是有六个人一直关系都很好嘛,他们都没看出来。一方面我们会避嫌,他接我下班,都绕到后门去,不让人看到。另一个方面是,之前大家真的是铁到不行的好朋友,他们都想不到。

    说实话我在朋友圈也没少发和他出去的照片,一看就是单独的,我甚至还会手动艾特他。就这样其他四个人都完全没想到,我俩竟然是在搞对象。

    有次我们六个人一起还去吃了个饭。他给我夹菜,其他人不但没觉得哪里不对,还跟着也给我夹菜,边夹还边损我:咋滴,小张今天是不能自理了?

    临离职之前还是公开了,在办公室大家都很震惊的样子。有个和我关系很好的女孩子还说呢,说还记得吗,他之前对前女友可好了,不吃饭攒钱给她买口红,他肯定会是个好男朋友的!给我气的呀……

    这是我的第一份工作,自然也是我头回办公室恋情,而且我之前甚至没有从朋友发展成恋人的经历,最多是从恋人变朋友。

    虽然直到现在我们还会因为他前女友吵架,比如上次我拿他手机点外卖,结果一选地址,第一个就赫然是她的,气死我了,虽然知道是以前存的,我还是给自己关了五个小时不愿意搭理他。

    但是,我们之间的那种相互了解很美好。我是在爱他整个人啊,我知道他是怎样的人,我们不用怎么磨合,甚至刚在一起几个月就见双方家长了。

    遇到他之前,我完全没有想过结婚,甚至恐婚。但现在我就觉得,如果是他的话,我还真的蛮想结婚的,就这样一直在一起吧。

    李书籍,28岁,女,饿了么

    “大家一起哄,气氛就更微妙了。”

    我老公跟我说在学校就有关注到我什么的,他学市场营销的,我合理怀疑他在给自己做品牌包装。

    2016年我毕业就通过校招,来到了饿了么,开始了我沉迷工作的生活。两年以后,有天工作软件弹出一个消息,问:XX大学?我回:?他就介绍自己是谁谁谁呀,之前是校友什么的。讲真我问了同学才想起来是谁……

    我们确实是大学同学,一个学院,不过我学人力,他学市场营销,不是一个专业,也就上大课的时候会见到,最多算脸熟。这算是重新建立了联系,但也不怎么聊天,我依旧沉迷工作。

    现在回想起来,此后的几个月,他行动一波接一波的。他先是叫上A,A是我关系特好的大学舍友,和他是同个社团的。他约周末三个人一起吃饭,还号称这是“上海校友铁三角”。不过这铁三角有点名不副实,也就这一回,后来我们吃饭就没喊他了。

    那次他送我回家,我倒是发现他腿很好看,精准戳到我的点。说起来也是好玩,他刚提车,开的不熟练,也可能是紧张?反正到我家开过了好大一截,害得我又往回走了十分钟。

    一计不成又生一计嘛,他之后说,下班一起,顺路带我。我本来想拒绝的,他说和同事B一起,我心说那好呗,就答应了。每次都得出现点状况,电梯里,上来另一个女同事。B和她热聊起来,结果她顺路,看得出来她也很想同行,特明显,但是我老公就是不作声。

    坐地铁的话应该是在B2下,她没下,到了B3停车场那层,她说哎呀刚没下去。我老公立刻说,没事我帮你再按上去!后来我老公和我说,当时觉得如果带上她了肯定没戏了。

    还是让他碰到其他状况了,上车的时候B主动说要坐前排,我就坐后排。然后B和他一直聊天,我没怎么说话。后来他跟我说当时特后悔,就应该让我坐前排。哦对了,这次他开车还是开过了,我又得往回走。

    之后我还蹭了几回车,不过那时候我沉迷工作,也没察觉出什么。结果因为总是加班,我生病了,发烧。有天大早晨的,他来我们这层给我送药。我当时才觉得,这不对劲啊。而且他来,还被我们同事看到了,大家一起哄,气氛就更加微妙了。

    这下俩人私下里聊天变多了,还会分享文章分享歌给对方。后来我老公说,那时候我分享给他的歌,他还会研究歌名和歌词,琢磨我是不是通过歌在传达什么讯息。我对他也好感也在直线上升,有时候下公司路过会议室,他正好在做业务培训啊分享啊,就能看出这个人工作能力挺强的。日常聊天,他的知识储备也很可。

    第一次正式约会,去看电影。必须得提一下,接我的时候他又开错路了……在车上他伸出手牵住了我的手,牵了一下,松开了。我问:“你就牵这一下吗?”他就懵了呀,被我反杀。傻傻地憋出一句:“我能一直牵着吗?”

    那天回家之后,他还发微信问我算不算在一起了,我说算。就这么恋爱了。

    那是2018年的9月,到了12月,我生了一场病,盆腔有血块,很危险,住院了。在医院能看尽人生百态,病房里还有个小姑娘,他男友一直打游戏;另外一对安徽来的老夫妻,丈夫每天唤她“老婆”,细心照顾。

    那时候我们刚在一起没多久,其实是场考验了。而且医生说可能会影响生育,他也不在乎。我住了半个月院,他每天陪夜照顾我,白天上班,晚上来医院睡板凳,很感人的。那年我们连跨年夜都是在医院度过的。

    出院之后,我还持续治疗了大半年。我还记得最后一次去医院,医生说血块没有了,我老公激动的哭了,搞的我也一起哭了。

    感谢这次生病的经历,让我们一起经历了困难。在一起一年,我们就领证了,有了之前的事情,我觉得他可以托付,我也不怕。不然可能不会这么快就结婚吧。

    现在我们在一起三年了,是饿了么的“双职工家庭”,我和你说这些的时候,正在医院等产检报告呢。还有一个月就要“卸货”

    王珊珊,27岁,女,腾讯

    “我们俩换了两家公司,5年都是同事,信任度很高。”

    我和我男友现在都在腾讯,要结婚了,去年就已经买好了婚房。我们在深圳,这套房子他家出了大头,我这边只出了四分之一,房本上是我的名字——这得要很信任彼此才可以。

    相遇时天时地利人和,浪漫得不得了。

    17年公司组织团建。有天的活动是爬山看海,我站在半山腰,面对大海的方向,海风吹过来,感觉很不错。一扭头,就看到一个男孩子,高高的,挂着台相机。我心里一激灵,心说还蛮帅的。他也看到了我,好几秒钟,我们就这么看对方。

    后来下山,我看到他上了公司另一辆大巴,才确认他是我的同事。后来在接下来的旅途中,我们俩又遇到了很多次。到最后再见到时,两个人都会点点头打个招呼,明明就连句话都没说过。不多久,小红点来了,他加了我的微信,微信名就是他的大名,这个名字我早就打听到了。当然啦,得装傻嘛:你是谁呀?就这么认识了,再后来,顺理成章地在一起了。

    两个人在一起1年后,他跳槽去了腾讯,而我还在原来的中厂。那时他刚换工作,需要做出成绩,于是越来越忙,经常加班到晚上12点,周末也在加班,而我当时工作比较轻松,于是我们俩出现矛盾了,我觉得他工作狂,没时间陪我,这恋爱谈得跟单身差不多了,他觉得我不体谅他,还无理取闹。两人那段时间经常吵架,差点就坚持不下去了。

    后来转折点来了,因为公司发展停滞,我也跳槽去腾讯了,进去后我也成了昏天黑地加班模式,前几个月基本都是10点下班的节奏,经历了一样的体验后,我对他之前的状态完全感同身受了。自然两个人就不吵架了(也没时间吵了哈哈哈)甚至两人还产生了 “战友情”

    在腾讯1年多后,我们俩就同居了,开始了每天一起上下班的日子,刚开始觉得很甜蜜,久了后会觉得有点没个人空间。这也是办公室恋情的一个可能的缺点吧,人还是多多少少需要点自己的时间和空间的。于是我们俩商量了一下,就分开上下班了,还是要给彼此多一些空间。

    在工作上,他是产品人,我呢是做运营的,我俩在工作上也经常交流,有时还可以互相帮对方写方案(笑)。

    这几年相处下来,我们可以将对方作为一个完整的个体去看待,完全知晓对方的工作状态,也得以很立体地了解对方,我俩之间的信任,回想起来,和我们一直在同公司是有很大关系的。

    而这种理解也是一点一点加深的。我记得有一次,他面临了一个抉择。他组里有一个同事,工作能力不太行,他觉察到领导想让这个人离职。这时候,他同时产生了两种念头,第一是帮这位同事一把,把他拉进自己的项目,这样兴许可以帮他留下来。第二是不拉他,招一个新人来自己的项目,这样一来,那位老同事就更边缘化了,更可能被淘汰了。

    我从情感上来讲更倾向于第一种,觉得能帮一下就帮一下。但是我男友在职场更激进些,他更希望团队做出成绩,而不是同情同事,最终他选择了第二种,我觉得有些唏嘘,但也可以理解他的选择背后的逻辑。

    这样的事还有很多。我们之间的这种相互理解,建立在同一个职场环境基础之上,并不是说我们总是同意彼此,也并不是说不管三七二十一只要是对方的选择我们就赞成,而是结合背景,结合自身,加上沟通和交流,去逐渐加深对他这个人的了解。

    舒克,28岁,女,某知名内容平台

    “和同事谈对象能撕到天亮。”

    办公室恋情的特点?为了工作撕到天明,这就是我们的日常啊。

    之前我俩是一个组的。共事非常紧密。那时候就觉得很聊得来,慢慢地下班或者周末也会一起出去玩。我一般是不会这样的,同事是同事,朋友是朋友,她那时候已经是我的朋友了。

    我们的兴趣爱好不太一样,我是金属狗,她是西海岸黑怕,不过这不打紧,有那么一个月每天都在一块玩。有天工作间隙,我俩在楼下抽烟,她突然问我:要不要谈恋爱?我拒绝了,我以为她在开玩笑。

    然后她又很认真地说了一遍,有个问题是,我不知道自己是喜欢和这个人一起玩,还是喜欢这个人,这是不一样的。

    回家以后我就在想,到底是哪一种?别的朋友如果生病了,我会问:好了吗?好了出来吃饭。但是她生病了,我会拉她去医院,甚至吃到一半,她不舒服,我当下的第一反应就是走,一起去医院。在乎她,在乎她的健康。想到这个我就明白了,我确实是喜欢这个人。

    也会考虑到在一个公司,尤其都在一个组,工作关系太近了,会有顾虑。但是之后就发现可能搞内容的不太一样,不管同事还是领导,知道了都没什么,甚至我领导的领导还和我领导八卦呢。

    办公室恋情,说起来有好有不好。不好的部分现在其实解决了,之前在一个组,生活和工作确实都太近了,一般的情侣可能见了面,可以聊聊最近都发生了什么,我们不是,太了解彼此都在干嘛了,很了解对方,但是也着实没什么新鲜事。现在我们已经不在同一个部门了,达到了完美的平衡。既在一个公司,很能理解对方,又不至于没新鲜事。

    之前工作上有啥问题,需要提意见提需求,还得在那遣词造句,琢磨怎么委婉不失优雅地提问题。在一块就不一样了,直接就“你这个写的不行”,完全肆无忌惮。

    这种感觉与其说是甜蜜,不如说,我们非常亲近。现在我俩的日常就是“吃饭吗?走。”“抽烟吗?走。”简简单单,平平淡淡。不用刻意隐瞒,反正关系好的同事自然就知道我们在一起的。

    七夕了,我俩打算去吃个椰子鸡,喝咖啡,逛公园。

     

    展开全文
  • C++中,初始化时执行相关代码时才会进行初始化,主要是由于C++引入对象后,要进行初始化必须执行相应构造函数析构函数,构造函数或析构函数中经常会需要进行某些程序中需要进行的特定操作,并非简单地...

    我发现呀,这大家对面试题的需求还是很大的,这里总结了上千道知识点,能换您一个收藏吗

    C++

     

    引用和指针的区别?

    指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。

    引用在定义的时候必须进行初始化,并且不能够改变。指针在定义的时候不一定要初始化,并且指向的空间可变。(注:不能有引用的值不能为NULL)

    有多级指针,但是没有多级引用,只能有一级引用。

    指针和引用的自增运算结果不一样。(指针是指向下一个空间,引用时引用的变量值加1)

    sizeof 引用得到的是所指向的变量(对象)的大小,而sizeof 指针得到的是指针本身的大小。

    引用访问一个变量是直接访问,而指针访问一个变量是间接访问。

    使用指针前最好做类型检查,防止野指针的出现;

    引用底层是通过指针实现的;

    作为参数时也不同,传指针的实质是传值,传递的值是指针的地址;传引用的实质是传地址,传递的是变量的地址。

    从汇编层去解释一下引用

    x的地址为ebp-4,b的地址为ebp-8,因为栈内的变量内存是从高往低进行分配的。所以b的地址比x的低。lea eax,[ebp-4]  这条语句将x的地址ebp-4放入eax寄存器mov dword ptr [ebp-8],eax 这条语句将eax的值放入b的地址ebp-8中上面两条汇编的作用即:将x的地址存入变量b中,这不和将某个变量的地址存入指针变量是一样的吗?所以从汇编层次来看,的确引用是通过指针来实现的。

    C++中的指针参数传递和引用参数传递

    指针参数传递本质上是值传递,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,会在栈中开辟内存空间以存放由主调函数传递进来的实参值,从而形成了实参的一个副本(替身)。值传递的特点是,被调函数对形式参数的任何操作都是作为局部变量进行的,不会影响主调函数的实参变量的值(形参指针变了,实参指针不会变)。

    引用参数传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参(本体)的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量(根据别名找到主调函数中的本体)。因此,被调函数对形参的任何操作都会影响主调函数中的实参变量。

    引用传递和指针传递是不同的,虽然他们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将应用不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量(地址),那就得使用指向指针的指针或者指针引用。

    从编译的角度来讲,程序在编译时分别将指针和引用添加到符号表上,符号表中记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值(与实参名字不同,地址相同)。符号表生成之后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

    形参与实参的区别?

    形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量。

    实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值,会产生一个临时变量。

    实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。

    函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。

    当形参和实参不是指针类型时,在该函数运行时,形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变。

    值传递:有一个形参向函数所属的栈拷贝数据的过程,如果值传递的对象是类对象    或是大的结构体对象,将耗费一定的时间和空间。(传值)

    指针传递:同样有一个形参向函数所属的栈拷贝数据的过程,但拷贝的数据是一个固定为4字节的地址。(传值,传递的是地址值)

    引用传递:同样有上述的数据拷贝过程,但其是针对地址的,相当于为该数据所在的地址起了一个别名。(传地址)

    效率上讲,指针传递和引用传递比值传递效率高。一般主张使用引用传递,代码逻辑上更加紧凑、清晰。

    static的用法和作用?

    1.先来介绍它的第一条也是最重要的一条:隐藏。(static函数,static变量均可)

    当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。

    2.static的第二个作用是保持变量内容的持久。(static变量中的记忆功能和全局生存期)存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。

    3.static的第三个作用是默认初始化为0(static变量)

    其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。

    4.static的第四个作用:C++中的类成员声明static

    函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;  

    在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;   

    在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;   

    在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;   

    在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

    类内:

    static类对象必须要在类外进行初始化,static修饰的变量先于对象存在,所以static修饰的变量要在类外初始化;

    由于static修饰的类成员属于类,不属于对象,因此static类成员函数是没有this指针的,this指针是指向本对象的指针。正因为没有this指针,所以static类成员函数不能访问非static的类成员,只能访问 static修饰的类成员;

    static成员函数不能被virtual修饰,static成员不属于任何对象或实例,所以加上virtual没有任何实际意义;静态成员函数没有this指针,虚函数的实现是为每一个对象分配一个vptr指针,而vptr是通过this指针调用的,所以不能为virtual;虚函数的调用关系,this->vptr->ctable->virtual function

    静态变量什么时候初始化

    初始化只有一次,但是可以多次赋值,在主程序之前,编译器已经为其分配好了内存。

    静态局部变量和全局变量一样,数据都存放在全局区域,所以在主程序之前,编译器已经为其分配好了内存,但在C和C++中静态局部变量的初始化节点又有点不太一样。在C中,初始化发生在代码执行之前,编译阶段分配好内存之后,就会进行初始化,所以我们看到在C语言中无法使用变量对静态局部变量进行初始化,在程序运行结束,变量所处的全局内存会被全部回收。

    而在C++中,初始化时在执行相关代码时才会进行初始化,主要是由于C++引入对象后,要进行初始化必须执行相应构造函数和析构函数,在构造函数或析构函数中经常会需要进行某些程序中需要进行的特定操作,并非简单地分配内存。所以C++标准定为全局或静态对象是有首次用到时才会进行构造,并通过atexit()来管理。在程序结束,按照构造顺序反方向进行逐个析构。所以在C++中是可以使用变量对静态局部变量进行初始化的。

     const?

    阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;   

    对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;   

    在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;   

    对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量,类的常对象只能访问类的常成员函数;   

    对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。

    const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;

    非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;

    一个没有明确声明为const的成员函数被看作是将要修改对象中数据成员的函数,而且编译器不允许它为一个const对象所调用。因此const对象只能调用const成员函数。

    const类型变量可以通过类型转换符const_cast将const类型转换为非const类型;

    const类型变量必须定义的时候进行初始化,因此也导致如果类的成员变量有const类型的变量,那么该变量必须在类的初始化列表中进行初始化;

    对于函数值传递的情况,因为参数传递是通过复制实参创建一个临时变量传递进函数的,函数内只能改变临时变量,但无法改变实参。则这个时候无论加不加const对实参不会产生任何影响。但是在引用或指针传递函数调用中,因为传进去的是一个引用或指针,这样函数内部可以改变引用或指针所指向的变量,这时const 才是实实在在地保护了实参所指向的变量。因为在编译阶段编译器对调用函数的选择是根据实参进行的,所以,只有引用传递和指针传递可以用是否加const来重载。一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。

    const成员函数的理解和应用?

    const Stock & Stock::topval (②const Stock & s) ③const

    ①处const:确保返回的Stock对象在以后的使用中不能被修改

    ②处const:确保此方法不修改传递的参数 S

    ③处const:保证此方法不修改调用它的对象,const对象只能调用const成员函数,不能调用非const函数

    指针和const的用法

    当const修饰指针时,由于const的位置不同,它的修饰对象会有所不同。

    int *const p2中const修饰p2的值,所以理解为p2的值不可以改变,即p2只能指向固定的一个变量地址,但可以通过*p2读写这个变量的值。顶层指针表示指针本身是一个常量

    int const *p1或者const int *p1两种情况中const修饰*p1,所以理解为*p1的值不可以改变,即不可以给*p1赋值改变p1指向变量的值,但可以通过给p赋值不同的地址改变这个指针指向。底层指针表示指针所指向的变量是一个常量。

    int const *const p;

    mutable

    如果需要在const成员方法中修改一个成员变量的值,那么需要将这个成员变量修饰为mutable。即用mutable修饰的成员变量不受const成员方法的限制;

    可以认为mutable的变量是类的辅助状态,但是只是起到类的一些方面表述的功能,修改他的内容我们可以认为对象的状态本身并没有改变的。实际上由于const_cast的存在,这个概念很多时候用处不是很到了。

    extern用法?

    extern修饰变量的声明

    如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。

    extern修饰函数的声明

    如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。

    extern修饰符可用于指示C或者C++函数的调用规范。

    比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同。

    int转字符串字符串转int?strcat,strcpy,strncpy,memset,memcpy的内部实现?

    c++11标准增加了全局函数std::to_string

    可以使用std::stoi/stol/stoll等等函数

    strcpy拥有返回值,有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,

    深拷贝与浅拷贝?

    浅复制 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。

    深复制 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。

    在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

    C++模板是什么,底层怎么实现的?

    编译器并不是把函数模板处理成能够处理任意类的函数;编译器从函数模板通过具体类型产生不同的函数;编译器会对函数模板进行两次编译:在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

    这是因为函数模板要被实例化后才能成为真正的函数,在使用函数模板的源文件中包含函数模板的头文件,如果该头文件中只有声明,没有定义,那编译器无法实例化该模板,最终导致链接错误。

    C语言struct和C++struct区别

    C语言中:struct是用户自定义数据类型(UDT);C++中struct是抽象数据类型(ADT),支持成员函数的定义,(C++中的struct能继承,能实现多态)。

    C中struct是没有权限的设置的,且struct中只能是一些变量的集合体,可以封装数据却不可以隐藏数据,而且成员不可以是函数。

    C++中,struct的成员默认访问说明符为public(为了与C兼容),class中的默认访问限定符为private,struct增加了访问权限,且可以和类一样有成员函数。

    struct作为类的一种特例是用来自定义数据结构的。一个结构标记声明后,在C中必须在结构标记前加上struct,才能做结构类型名

    虚函数可以声明为inline吗?

    虚函数用于实现运行时的多态,或者称为晚绑定或动态绑定。而内联函数用于提高效率。内联函数的原理是,在编译期间,对调用内联函数的地方的代码替换成函数代码。内联函数对于程序中需要频繁使用和调用的小函数非常有用。

    虚函数要求在运行时进行类型确定,而内敛函数要求在编译期完成相关的函数替换;

    类成员初始化方式?构造函数的执行顺序 ?为什么用成员初始化列表会快一些?

    赋值初始化,通过在函数体内进行赋值初始化;列表初始化,在冒号后使用初始化列表进行初始化。

    这两种方式的主要区别在于:

    对于在函数体中初始化,是在所有的数据成员被分配内存空间后才进行的。

    列表初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。 

    一个派生类构造函数的执行顺序如下:

    虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数)。

    基类的构造函数(多个普通基类也按照继承的顺序执行构造函数)。

    类类型的成员对象的构造函数(按照初始化顺序)

    派生类自己的构造函数。

    方法一是在构造函数当中做赋值的操作,而方法二是做纯粹的初始化操作。我们都知道,C++的赋值操作是会产生临时对象的。临时对象的出现会降低程序的效率。

    成员列表初始化?

    必须使用成员初始化的四种情况

    当初始化一个引用成员时;

    当初始化一个常量成员时;

    当调用一个基类的构造函数,而它拥有一组参数时;

    当调用一个成员类的构造函数,而它拥有一组参数时;

    成员初始化列表做了什么

    编译器会一一操作初始化列表,以适当的顺序在构造函数之内安插初始化操作,并且在任何显示用户代码之前;

    list中的项目顺序是由类中的成员声明顺序决定的,不是由初始化列表的顺序决定的;

    构造函数为什么不能为虚函数?析构函数为什么要虚函数?

    1. 从存储空间角度,虚函数相应一个指向vtable虚函数表的指针,这大家都知道,但是这个指向vtable的指针事实上是存储在对象的内存空间的。问题出来了,假设构造函数是虚的,就须要通过 vtable来调用,但是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。

    2. 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到相应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。虚函数的作用在于通过父类的指针或者引用来调用它的时候可以变成调用子类的那个成员函数。而构造函数是在创建对象时自己主动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

    3. 构造函数不须要是虚函数,也不同意是虚函数,由于创建一个对象时我们总是要明白指定对象的类型,虽然我们可能通过实验室的基类的指针或引用去訪问它但析构却不一定,我们往往通过基类的指针来销毁对象。这时候假设析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。

    4. 从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数从实际含义上看,在调用构造函数时还不能确定对象的真实类型(由于子类会调父类的构造函数);并且构造函数的作用是提供初始化,在对象生命期仅仅运行一次,不是对象的动态行为,也没有必要成为虚函数。

    5. 当一个构造函数被调用时,它做的首要的事情之中的一个是初始化它的VPTR。因此,它仅仅能知道它是“当前”类的,而全然忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码——既不是为基类,也不是为它的派生类(由于类不知道谁继承它)。所以它使用的VPTR必须是对于这个类的VTABLE。并且,仅仅要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持被初始化为指向这个VTABLE, 但假设接着另一个更晚派生的构造函数被调用,这个构造函数又将设置VPTR指向它的 VTABLE,等.直到最后的构造函数结束。VPTR的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生类顺序的还有一个理由。可是,当这一系列构造函数调用正发生时,每一个构造函数都已经设置VPTR指向它自己的VTABLE。假设函数调用使用虚机制,它将仅仅产生通过它自己的VTABLE的调用,而不是最后的VTABLE(全部构造函数被调用后才会有最后的VTABLE)。

    因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。

    直接的讲,C++中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。

    析构函数的作用,如何起作用?

    构造函数只是起初始化值的作用,但实例化一个对象的时候,可以通过实例去传递参数,从主函数传递到其他的函数里面,这样就使其他的函数里面有值了。规则,只要你一实例化对象,系统自动回调用一个构造函数,就是你不写,编译器也自动调用一次。

    析构函数与构造函数的作用相反,用于撤销对象的一些特殊任务处理,可以是释放对象分配的内存空间;特点:析构函数与构造函数同名,但该函数前面加~。 析构函数没有参数,也没有返回值,而且不能重载,在一个类中只能有一个析构函数。 当撤销对象时,编译器也会自动调用析构函数。 每一个类必须有一个析构函数,用户可以自定义析构函数,也可以是编译器自动生成默认的析构函数。一般析构函数定义为类的公有成员。

    构造函数和析构函数可以调用虚函数吗,为什么

    在C++中,提倡不在构造函数和析构函数中调用虚函数;

    构造函数和析构函数调用虚函数时都不使用动态联编,如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本;

    因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化,因此调用子类的虚函数时不安全的,故而C++不会进行动态联编;

    析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经销毁,这个时候再调用子类的虚函数没有任何意义。

    构造函数的执行顺序?析构函数的执行顺序?构造函数内部干了啥?拷贝构造干了啥?

    构造函数顺序

    基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。

    成员类对象构造函数。如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。

    派生类构造函数。

    析构函数顺序

    调用派生类的析构函数;

    调用成员类对象的析构函数;

    调用基类的析构函数。

    虚析构函数的作用,父类的析构函数是否要设置为虚函数?

    C++中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。

    纯虚析构函数一定得定义,因为每一个派生类析构函数会被编译器加以扩张,以静态调用的方式调用其每一个虚基类以及上一层基类的析构函数。因此,缺乏任何一个基类析构函数的定义,就会导致链接失败。因此,最好不要把虚析构函数定义为纯虚析构函数。

    构造函数析构函数可以调用虚函数吗?

    在构造函数和析构函数中最好不要调用虚函数;

    构造函数或者析构函数调用虚函数并不会发挥虚函数动态绑定的特性,跟普通函数没区别;

    即使构造函数或者析构函数如果能成功调用虚函数, 程序的运行结果也是不可控的。

    构造函数析构函数可否抛出异常

     C++只会析构已经完成的对象,对象只有在其构造函数执行完毕才算是完全构造妥当。在构造函数中发生异常,控制权转出构造函数之外。因此,在对象b的构造函数中发生异常,对象b的析构函数不会被调用。因此会造成内存泄漏。

    用auto_ptr对象来取代指针类成员,便对构造函数做了强化,免除了抛出异常时发生资源泄漏的危机,不再需要在析构函数中手动释放资源;

    如果控制权基于异常的因素离开析构函数,而此时正有另一个异常处于作用状态,C++会调用terminate函数让程序结束;

    如果异常从析构函数抛出,而且没有在当地进行捕捉,那个析构函数便是执行不全的。如果析构函数执行不全,就是没有完成他应该执行的每一件事情。

    类如何实现只能静态分配和只能动态分配

    前者是把new、delete运算符重载为private属性。后者是把构造、析构函数设为protected属性,再用子类来动态创建

    建立类的对象有两种方式:

    静态建立,静态建立一个类对象,就是由编译器为对象在栈空间中分配内存;

    动态建立,A *p = new A();动态建立一个类对象,就是使用new运算符为对象在堆空间中分配内存。这个过程分为两步,第一步执行operator new()函数,在堆中搜索一块内存并进行分配;第二步调用类构造函数构造对象;

    只有使用new运算符,对象才会被建立在堆上,因此只要限制new运算符就可以实现类对象只能建立在栈上。可以将new运算符设为私有。

    如果想将某个类用作基类,为什么该类必须定义而非声明?

    派生类中包含并且可以使用它从基类继承而来的成员,为了使用这些成员,派生类必须知道他们是什么。

    什么情况会自动生成默认构造函数?

    带有默认构造函数的类成员对象,如果一个类没有任何构造函数,但它含有一个成员对象,而后者有默认构造函数,那么编译器就为该类合成出一个默认构造函数。不过这个合成操作只有在构造函数真正被需要的时候才会发生;如果一个类A含有多个成员类对象的话,那么类A的每一个构造函数必须调用每一个成员对象的默认构造函数而且必须按照类对象在类A中的声明顺序进行;

    带有默认构造函数的基类,如果一个没有任务构造函数的派生类派生自一个带有默认构造函数基类,那么该派生类会合成一个构造函数调用上一层基类的默认构造函数;

    带有一个虚函数的类

    带有一个虚基类的类

    合成的默认构造函数中,只有基类子对象和成员类对象会被初始化。所有其他的非静态数据成员都不会被初始化。

    什么是类的继承?

    类与类之间的关系

    has-A包含关系,用以描述一个类由多个部件类构成,实现has-A关系用类的成员属性表示,即一个类的成员属性是另一个已经定义好的类;

    use-A,一个类使用另一个类,通过类之间的成员函数相互联系,定义友元或者通过传递参数的方式来实现;

    is-A,继承关系,关系具有传递性;

    继承的相关概念

    所谓的继承就是一个类继承了另一个类的属性和方法,这个新的类包含了上一个类的属性和方法,被称为子类或者派生类,被继承的类称为父类或者基类;

    继承的特点

    子类拥有父类的所有属性和方法,子类可以拥有父类没有的属性和方法,子类对象可以当做父类对象使用;

    继承中的访问控制

    public、protected、private

    继承中的构造和析构函数

    继承中的兼容性原则

    什么是组合?

    一个类里面的数据成员是另一个类的对象,即内嵌其他类的对象作为自己的成员;创建组合类的对象:首先创建各个内嵌对象,难点在于构造函数的设计。创建对象时既要对基本类型的成员进行初始化,又要对内嵌对象进行初始化。

    创建组合类对象,构造函数的执行顺序:先调用内嵌对象的构造函数,然后按照内嵌对象成员在组合类中的定义顺序,与组合类构造函数的初始化列表顺序无关。然后执行组合类构造函数的函数体,析构函数调用顺序相反。

     

    抽象基类为什么不能创建对象?

    抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。

    (1)抽象类的定义:
       称带有纯虚函数的类为抽象类。

    (2)抽象类的作用:
       抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。

    (3)使用抽象类时注意:
       抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。

    抽象类是不能定义对象的。一个纯虚函数不需要(但是可以)被定义。

    纯虚函数定义


     纯虚函数是一种特殊的虚函数,它的一般格式如下:
      class <类名>
      {
      virtual <类型><函数名>(<参数表>)=0;
      …
      };
      在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
      纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。除非在派生类中完全实现基类中所有的的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。

    纯虚函数引入原因


      1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
      2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔 雀等子类,但动物本身生成对象明显不合常理。
      为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;)。若要使派生类为非抽象类,则编译器要求在派生类中,必须对纯虚函数予以重载以实现多态性。同时含有纯虚函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
    例如,绘画程序中,shape作为一个基类可以派生出圆形、矩形、正方形、梯形等, 如果我要求面积总和的话,那么会可以使用一个 shape * 的数组,只要依次调用派生类的area()函数了。如果不用接口就没法定义成数组,因为既可以是circle ,也可以是square ,而且以后还可能加上rectangle,等等.

    相似概念


    1、多态性

    指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
      a.编译时多态性:通过重载函数实现
      b.运行时多态性:通过虚函数实现。
    2、虚函数
      虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载。
    3、抽象类
      包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。

     

    类什么时候会析构?

    1. 对象生命周期结束,被销毁时;
    2. delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
    3. 对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。

     

    为什么友元函数必须在类内部声明?

    因为编译器必须能够读取这个结构的声明以理解这个数据类型的大、行为等方面的所有规则。有一条规则在任何关系中都很重要,那就是谁可以访问我的私有部分。

    介绍一下C++里面的多态?

    (1)静态多态(重载,模板)

    是在编译的时候,就确定调用函数的类型。

    (2)动态多态(覆盖,虚函数实现)

    在运行的时候,才确定调用的是哪个函数,动态绑定。运行基类指针指向派生类的对象,并调用派生类的函数。

    虚函数实现原理:虚函数表和虚函数指针。

    纯虚函数: virtual int fun() = 0;

    函数的运行版本由实参决定,在运行时选择函数的版本,所以动态绑定又称为运行时绑定。

    当编译器遇到一个模板定义时,它并不生成代码。只有当实例化出模板的一个特定版本时,编译器才会生成代码。

    用C语言实现C++的继承

    #include <iostream>
    using namespace std;
    
    //C++中的继承与多态
    struct A
    {
        virtual void fun()    //C++中的多态:通过虚函数实现
        {
            cout<<"A:fun()"<<endl;
        }
    
        int a;
    };
    struct B:public A         //C++中的继承:B类公有继承A类
    {
        virtual void fun()    //C++中的多态:通过虚函数实现(子类的关键字virtual可加可不加)
        {
            cout<<"B:fun()"<<endl;
        }
    
        int b;
    };
    
    //C语言模拟C++的继承与多态
    
    typedef void (*FUN)();      //定义一个函数指针来实现对成员函数的继承
    
    struct _A       //父类
    {
        FUN _fun;   //由于C语言中结构体不能包含函数,故只能用函数指针在外面实现
    
        int _a;
    };
    
    struct _B         //子类
    {
        _A _a_;     //在子类中定义一个基类的对象即可实现对父类的继承
        int _b;
    };
    
    void _fA()       //父类的同名函数
    {
        printf("_A:_fun()\n");
    }
    void _fB()       //子类的同名函数
    {
        printf("_B:_fun()\n");
    }
    
    
    void Test()
    {
        //测试C++中的继承与多态
        A a;    //定义一个父类对象a
        B b;    //定义一个子类对象b
    
        A* p1 = &a;   //定义一个父类指针指向父类的对象
        p1->fun();    //调用父类的同名函数
        p1 = &b;      //让父类指针指向子类的对象
        p1->fun();    //调用子类的同名函数
    
    
        //C语言模拟继承与多态的测试
        _A _a;    //定义一个父类对象_a
        _B _b;    //定义一个子类对象_b
        _a._fun = _fA;        //父类的对象调用父类的同名函数
        _b._a_._fun = _fB;    //子类的对象调用子类的同名函数
    
        _A* p2 = &_a;   //定义一个父类指针指向父类的对象
        p2->_fun();     //调用父类的同名函数
        p2 = (_A*)&_b;  //让父类指针指向子类的对象,由于类型不匹配所以要进行强转
        p2->_fun();     //调用子类的同名函数
    }
    

     

     

    继承机制中对象之间如何转换?指针和引用之间如何转换?

    • 向上类型转换

    将派生类指针或引用转换为基类的指针或引用被称为向上类型转换,向上类型转换会自动进行,而且向上类型转换是安全的。

    • 向下类型转换

    将基类指针或引用转换为派生类指针或引用被称为向下类型转换,向下类型转换不会自动进行,因为一个基类对应几个派生类,所以向下类型转换时不知道对应哪个派生类,所以在向下类型转换时必须加动态类型识别技术。RTTI技术,用dynamic_cast进行向下类型转换。

    组合与继承优缺点?

    一:继承

    继承是Is a 的关系,比如说Student继承Person,则说明Student is a Person。继承的优点是子类可以重写父类的方法来方便地实现对父类的扩展。

    继承的缺点有以下几点:

    ①:父类的内部细节对子类是可见的。

    ②:子类从父类继承的方法在编译时就确定下来了,所以无法在运行期间改变从父类继承的方法的行为。

    ③:如果对父类的方法做了修改的话(比如增加了一个参数),则子类的方法必须做出相应的修改。所以说子类与父类是一种高耦合,违背了面向对象思想。

    二:组合

    组合也就是设计类的时候把要组合的类的对象加入到该类中作为自己的成员变量。

    组合的优点:

    ①:当前对象只能通过所包含的那个对象去调用其方法,所以所包含的对象的内部细节对当前对象时不可见的。

    ②:当前对象与包含的对象是一个低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码。

    ③:当前对象可以在运行时动态的绑定所包含的对象。可以通过set方法给所包含对象赋值。

    组合的缺点:①:容易产生过多的对象。②:为了能组合多个对象,必须仔细对接口进行定义。

    左值右值

    1. 在C++11中所有的值必属于左值、右值两者之一,右值又可以细分为纯右值、将亡值。在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。举个例子,int a = b+c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;表达式b+c、函数int func()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b+c)这样的操作则不会通过编译。
    2. C++11对C++98中的右值进行了扩充。在C++11中右值又分为纯右值(prvalue,Pure Rvalue)和将亡值(xvalue,eXpiring Value)。其中纯右值的概念等同于我们在C++98标准中右值的概念,指的是临时变量和不跟对象关联的字面量值;将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如返回右值引用T&&的函数返回值、std::move的返回值,或者转换为T&&的类型转换函数的返回值。将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值。在确保其他变量不再被使用、或即将被销毁时,通过“盗取”的方式可以避免内存空间的释放和分配,能够延长变量值的生命期。
    3. 左值引用就是对一个左值进行引用的类型。右值引用就是对一个右值进行引用的类型,事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。右值引用和左值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。左值引用通常也不能绑定到右值,但常量左值引用是个“万能”的引用类型。它可以接受非常量左值、常量左值、右值对其进行初始化。不过常量左值所引用的右值在它的“余生”中只能是只读的。相对地,非常量左值只能接受非常量左值对其进行初始化。
    4. 右值值引用通常不能绑定到任何的左值,要想绑定一个左值到右值引用,通常需要std::move()将左值强制转换为右值。

     

    移动构造函数

    1. 我们用对象a初始化对象b,后对象a我们就不在使用了,但是对象a的空间还在呀(在析构之前),既然拷贝构造函数,实际上就是把a对象的内容复制一份到b中,那么为什么我们不能直接使用a的空间呢?这样就避免了新的空间的分配,大大降低了构造的成本。这就是移动构造函数设计的初衷;
    2. 拷贝构造函数中,对于指针,我们一定要采用深层复制,而移动构造函数中,对于指针,我们采用浅层复制。浅层复制之所以危险,是因为两个指针共同指向一片内存空间,若第一个指针将其释放,另一个指针的指向就不合法了。所以我们只要避免第一个指针释放空间就可以了。避免的方法就是将第一个指针(比如a->value)置为NULL,这样在调用析构函数的时候,由于有判断是否为NULL的语句,所以析构a的时候并不会回收a->value指向的空间;
    3. 移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的初值是一个右值引用。意味着,移动构造函数的参数是一个右值或者将亡值的引用。也就是说,只用用一个右值,或者将亡值初始化另一个对象的时候,才会调用移动构造函数。而那个move语句,就是将一个左值变成一个将亡值。

     

    C语言的编译链接过程?

    源代码-->预处理-->编译-->优化-->汇编-->链接-->可执行文件

    • 预处理

    读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。包括宏定义替换、条件编译指令、头文件包含指令、特殊符号。 预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。.i预处理后的c文件,.ii预处理后的C++文件。

    • 编译阶段

    编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。.s文件

    • 汇编过程

    汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。.o目标文件

    • 链接阶段

    链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。

    vector与list的区别与应用?怎么找某vector或者list的倒数第二个元素

    1. vector数据结构
      vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝。连续存储结构:vector是可以实现动态增长的对象数组,支持对数组高效率的访问和在数组尾端的删除和插入操作,在中间和头部删除和插入相对不易,需要挪动大量的数据。它与数组最大的区别就是vector不需程序员自己去考虑容量问题,库里面本身已经实现了容量的动态增长,而数组需要程序员手动写入扩容函数进形扩容。
    2. list数据结构
      list是由双向链表实现的,因此内存空间是不连续的。只能通过指针访问数据,所以list的随机存取非常没有效率,时间复杂度为o(n);但由于链表的特点,能高效地进行插入和删除。非连续存储结构:list是一个双链表结构,支持对链表的双向遍历。每个节点包括三个信息:元素本身,指向前一个元素的节点(prev)和指向下一个元素的节点(next)。因此list可以高效率的对数据元素任意位置进行访问和插入删除等操作。由于涉及对额外指针的维护,所以开销比较大。

    区别:

    vector的随机访问效率高,但在插入和删除时(不包括尾部)需要挪动数据,不易操作。list的访问要遍历整个链表,它的随机访问效率低。但对数据的插入和删除操作等都比较方便,改变指针的指向即可。list是单向的,vector是双向的。vector中的迭代器在使用后就失效了,而list的迭代器在使用之后还可以继续使用。 

    int mySize = vec.size();vec.at(mySize -2);

    list不提供随机访问,所以不能用下标直接访问到某个位置的元素,要访问list里的元素只能遍历,不过你要是只需要访问list的最后N个元素的话,可以用反向迭代器来遍历:

    ​​​​​​​STL vector的实现,删除其中的元素,迭代器如何变化?为什么是两倍扩容?释放空间?

    size()函数返回的是已用空间大小,capacity()返回的是总空间大小,capacity()-size()则是剩余的可用空间大小。当size()和capacity()相等,说明vector目前的空间已被用完,如果再添加新元素,则会引起vector空间的动态增长。

    由于动态增长会引起重新分配内存空间、拷贝原空间、释放原空间,这些过程会降低程序效率。因此,可以使用reserve(n)预先分配一块较大的指定大小的内存空间,这样当指定大小的内存空间未使用完时,是不会重新分配内存空间的,这样便提升了效率。只有当n>capacity()时,调用reserve(n)才会改变vector容量。

     resize()成员函数只改变元素的数目,不改变vector的容量。

    1. 空的vector对象,size()和capacity()都为0

    2. 当空间大小不足时,新分配的空间大小为原空间大小的2倍。

    3. 使用reserve()预先分配一块内存后,在空间未满的情况下,不会引起重新分配,从而提升了效率。

    4. 当reserve()分配的空间比原空间小时,是不会引起重新分配的。

    5. resize()函数只改变容器的元素数目,未改变容器大小。

    6. 用reserve(size_type)只是扩大capacity值,这些内存空间可能还是“野”的,如果此时使用“[ ]”来访问,则可能会越界。而resize(size_type new_size)会真正使容器具有new_size个对象。

     

     

    1. 不同的编译器,vector有不同的扩容大小。在vs下是1.5倍,在GCC下是2倍;
    2. 空间和时间的权衡。简单来说, 空间分配的多,平摊时间复杂度低,但浪费空间也多。
    3. 使用k=2增长因子的问题在于,每次扩展的新尺寸必然刚好大于之前分配的总和,也就是说,之前分配的内存空间不可能被使用。这样对内存不友好。最好把增长因子设为(1,2)
    4. 对比可以发现采用采用成倍方式扩容,可以保证常数的时间复杂度,而增加指定大小的容量只能达到O(n)的时间复杂度,因此,使用成倍的方式扩容。

    如何释放空间:

    由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

    如果需要空间动态缩小,可以考虑使用deque。如果vector,可以用swap()来帮助你释放内存。

    vector(Vec).swap(Vec);
    将Vec的内存空洞清除;
    vector().swap(Vec);
    清空Vec的内存;

     

    容器内部删除一个元素​​​​​​​

    顺序容器​​​​​​​

    erase迭代器不仅使所指向被删除的迭代器失效,而且使被删元素之后的所有迭代器失效(list除外),所以不能使用erase(it++)的方式,但是erase的返回值是下一个有效迭代器;

    It = c.erase(it);

    关联容器

    erase迭代器只是被删除元素的迭代器失效,但是返回值是void,所以要采用erase(it++)的方式删除迭代器;

    c.erase(it++)

    ​​​​​​​STL迭代器如何实现

    1. 迭代器是一种抽象的设计理念,通过迭代器可以在不了解容器内部原理的情况下遍历容器,除此之外,STL中迭代器一个最重要的作用就是作为容器与STL算法的粘合剂。
    2. 迭代器的作用就是提供一个遍历容器内部所有元素的接口,因此迭代器内部必须保存一个与容器相关联的指针,然后重载各种运算操作来遍历,其中最重要的是*运算符与->运算符,以及++、--等可能需要重载的运算符重载。这和C++中的智能指针很像,智能指针也是将一个指针封装,然后通过引用计数或是其他方法完成自动释放内存的功能。
    3. 最常用的迭代器的相应型别有五种:value type、difference type、pointer、reference、iterator catagoly;

    ​​​​​​​set与hash_set的区别

    1. set底层是以RB-Tree实现,hash_set底层是以hash_table实现的;
    2. RB-Tree有自动排序功能,而hash_table不具有自动排序功能;
    3. set和hash_set元素的键值就是实值;
    4. hash_table有一些无法处理的型别;

    ​​​​​​​hashmap与map的区别

    1. 底层实现不同;
    2. map具有自动排序的功能,hash_map不具有自动排序的功能;
    3. hashtable有一些无法处理的型别;

    map、set是怎么实现的,红黑树是怎么能够同时实现这两种容器? 为什么使用红黑树?

    1. 他们的底层都是以红黑树的结构实现,因此插入删除等操作都在O(logn)时间内完成,因此可以完成高效的插入删除;
    2. 在这里我们定义了一个模版参数,如果它是key那么它就是set,如果它是map,那么它就是map;底层是红黑树,实现map的红黑树的节点数据类型是key+value,而实现set的节点数据类型是value
    3. 因为map和set要求是自动排序的,红黑树能够实现这一功能,而且时间复杂度比较低。
    4.  

    如何在共享内存上使用stl标准库?

    1. 想像一下把STL容器,例如map, vector, list等等,放入共享内存中,IPC一旦有了这些强大的通用数据结构做辅助,无疑进程间通信的能力一下子强大了很多。我们没必要再为共享内存设计其他额外的数据结构,另外,STL的高度可扩展性将为IPC所驱使。STL容器被良好的封装,默认情况下有它们自己的内存管理方案。当一个元素被插入到一个STL列表(list)中时,列表容器自动为其分配内存,保存数据。考虑到要将STL容器放到共享内存中,而容器却自己在堆上分配内存。一个最笨拙的办法是在堆上构造STL容器,然后把容器复制到共享内存,并且确保所有容器的内部分配的内存指向共享内存中的相应区域,这基本是个不可能完成的任务。

     

    1. 假设进程A在共享内存中放入了数个容器,进程B如何找到这些容器呢?一个方法就是进程A把容器放在共享内存中的确定地址上(fixed offsets),则进程B可以从该已知地址上获取容器。另外一个改进点的办法是,进程A先在共享内存某块确定地址上放置一个map容器,然后进程A再创建其他容器,然后给其取个名字和地址一并保存到这个map容器里。进程B知道如何获取该保存了地址映射的map容器,然后同样再根据名字取得其他容器的地址。

    map插入方式有几种?

    1. 用insert函数插入pair数据,

    mapStudent.insert(pair<int, string>(1, "student_one"));  

    1. 用insert函数插入value_type数据

    mapStudent.insert(map<int, string>::value_type (1, "student_one"));

    1. 在insert函数中使用make_pair()函数

    mapStudent.insert(make_pair(1, "student_one"));  

    1. 用数组方式插入数据

    mapStudent[1] = "student_one";  

    ​​​​​​​STL中unordered_map(hash_map)和map的区别,hash_map如何解决冲突以及扩容

    1. unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是unordered_map不会根据key的大小进行排序,
    2. 存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。
    3. 所以使用时map的key需要定义operator<。而unordered_map需要定义hash_value函数并且重载operator==。但是很多系统内置的数据类型都自带这些,
    4. 那么如果是自定义类型,那么就需要自己重载operator<或者hash_value()了。
    5. 如果需要内部元素自动排序,使用map,不需要排序使用unordered_map
    6. unordered_map的底层实现是hash_table;
    7. hash_map底层使用的是hash_table,而hash_table使用的开链法进行冲突避免,所有hash_map采用开链法进行冲突解决。
    8. 什么时候扩容:当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值---即当前数组的长度乘以加载因子的值的时候,就要自动扩容啦。
    9. 扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。

    vector越界访问下标,map越界访问下标?vector删除元素时会不会释放空间?

    1. 通过下标访问vector中的元素时不会做边界检查,即便下标越界。也就是说,下标与first迭代器相加的结果超过了finish迭代器的位置,程序也不会报错,而是返回这个地址中存储的值。如果想在访问vector中的元素时首先进行边界检查,可以使用vector中的at函数。通过使用at函数不但可以通过下标访问vector中的元素,而且在at函数内部会对下标进行边界检查。
    2. map的下标运算符[]的作用是:将key作为下标去执行查找,并返回相应的值;如果不存在这个key,就将一个具有该key和value的某人值插入这个map。
    3. erase()函数,只能删除内容,不能改变容量大小; erase成员函数,它删除了itVect迭代器指向的元素,并且返回要被删除的itVect之后的迭代器,迭代器相当于一个智能指针;clear()函数,只能清空内容,不能改变容量大小;如果要想在删除内容的同时释放内存,那么你可以选择deque容器。

    map[]与find的区别?

    1. map的下标运算符[]的作用是:将关键码作为下标去执行查找,并返回对应的值;如果不存在这个关键码,就将一个具有该关键码和值类型的默认值的项插入这个map。
    2. map的find函数:用关键码执行查找,找到了返回该位置的迭代器;如果不存在这个关键码,就返回尾迭代器。

    STL中list与queue之间的区别

    1. list不再能够像vector一样以普通指针作为迭代器,因为其节点不保证在存储空间中连续存在;
    2. list插入操作和结合才做都不会造成原有的list迭代器失效;
    3. list不仅是一个双向链表,而且还是一个环状双向链表,所以它只需要一个指针;
    4. list不像vector那样有可能在空间不足时做重新配置、数据移动的操作,所以插入前的所有迭代器在插入操作之后都仍然有效;
    5. deque是一种双向开口的连续线性空间,所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操作;可以在头尾两端分别做元素的插入和删除操作;
    6. deque和vector最大的差异,一在于deque允许常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓容量概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,deque没有所谓的空间保留功能。

    STL中的allocator,deallocator

    1. 第一级配置器直接使用malloc()、free()和relloc(),第二级配置器视情况采用不同的策略:当配置区块超过128bytes时,视之为足够大,便调用第一级配置器;当配置器区块小于128bytes时,为了降低额外负担,使用复杂的内存池整理方式,而不再用一级配置器;
    2. 第二级配置器主动将任何小额区块的内存需求量上调至8的倍数,并维护16个free-list,各自管理大小为8~128bytes的小额区块;
    3. 空间配置函数allocate(),首先判断区块大小,大于128就直接调用第一级配置器,小于128时就检查对应的free-list。如果free-list之内有可用区块,就直接拿来用,如果没有可用区块,就将区块大小调整至8的倍数,然后调用refill(),为free-list重新分配空间;
    4. 空间释放函数deallocate(),该函数首先判断区块大小,大于128bytes时,直接调用一级配置器,小于128bytes就找到对应的free-list然后释放内存。
    5.  

    ​​​​​​​STL中hash_map扩容发生什么? 

    1. hash table表格内的元素称为桶(bucket),而由桶所链接的元素称为节点(node),其中存入桶元素的容器为stl本身很重要的一种序列式容器——vector容器。之所以选择vector为存放桶元素的基础容器,主要是因为vector容器本身具有动态扩容能力,无需人工干预。
    2. 向前操作:首先尝试从目前所指的节点出发,前进一个位置(节点),由于节点被安置于list内,所以利用节点的next指针即可轻易完成前进操作,如果目前正巧是list的尾端,就跳至下一个bucket身上,那正是指向下一个list的头部节点。

     

    ​​​​​​​map如何创建?

    1.vector      底层数据结构为数组 ,支持快速随机访问

    2.list            底层数据结构为双向链表,支持快速增删

    3.deque       底层数据结构为一个中央控制器和多个缓冲区,详细见STL源码剖析P146,支持首尾(中间不能)快速增删,也支持随机访问

    deque是一个双端队列(double-ended queue),也是在堆中保存内容的.它的保存形式如下:

    [堆1] --> [堆2] -->[堆3] --> ...

    每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品.

    4.stack        底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时

    5.queue     底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时(stack和queue其实是适配器,而不叫容器,因为是对容器的再封装)

    6.priority_queue     的底层数据结构一般为vector为底层容器,堆heap为处理规则来管理底层容器实现

    7.set                   底层数据结构为红黑树,有序,不重复

    8.multiset         底层数据结构为红黑树,有序,可重复 

    9.map                底层数据结构为红黑树,有序,不重复

    10.multimap    底层数据结构为红黑树,有序,可重复

    11.hash_set     底层数据结构为hash表,无序,不重复

    12.hash_multiset 底层数据结构为hash表,无序,可重复 

    13.hash_map    底层数据结构为hash表,无序,不重复

    14.hash_multimap 底层数据结构为hash表,无序,可重复 

     

    ​​​​​​​vector的增加删除都是怎么做的?为什么是1.5倍?

    1. 新增元素:vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素;
    2. 对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了 ;
    3. 初始时刻vector的capacity为0,塞入第一个元素后capacity增加为1;
    4. 不同的编译器实现的扩容方式不一样,VS2015中以1.5倍扩容,GCC以2倍扩容。

     

    对比可以发现采用采用成倍方式扩容,可以保证常数的时间复杂度,而增加指定大小的容量只能达到O(n)的时间复杂度,因此,使用成倍的方式扩容。

    1. 考虑可能产生的堆空间浪费,成倍增长倍数不能太大,使用较为广泛的扩容方式有两种,以2二倍的方式扩容,或者以1.5倍的方式扩容。
    2. 以2倍的方式扩容,导致下一次申请的内存必然大于之前分配内存的总和,导致之前分配的内存不能再被使用,所以最好倍增长因子设置为(1,2)之间: 
    3. 向量容器vector的成员函数pop_back()可以删除最后一个元素.
    4. 而函数erase()可以删除由一个iterator指出的元素,也可以删除一个指定范围的元素。
    5. 还可以采用通用算法remove()来删除vector容器中的元素.
    6. 不同的是:采用remove一般情况下不会改变容器的大小,而pop_back()与erase()等成员函数会改变容器的大小。

    什么是函数指针?

    函数指针指向的是特殊的数据类型,函数的类型是由其返回的数据类型和其参数列表共同决定的,而函数的名称则不是其类型的一部分。

    一个具体函数的名字,如果后面不跟调用符号(即括号),则该名字就是该函数的指针(注意:大部分情况下,可以这么认为,但这种说法并不很严格)。

    函数指针的声明方法

    int (*pf)(const int&, const int&); (1)

    上面的pf就是一个函数指针,指向所有返回类型为int,并带有两个const int&参数的函数。注意*pf两边的括号是必须的,否则上面的定义就变成了:

    int *pf(const int&, const int&); (2)

    而这声明了一个函数pf,其返回类型为int *, 带有两个const int&参数。

    为什么有函数指针

    函数与数据项相似,函数也有地址。我们希望在同一个函数中通过使用相同的形参在不同的时间使用产生不同的效果。

    1. 一个函数名就是一个指针,它指向函数的代码。一个函数地址是该函数的进入点,也就是调用函数的地址。函数的调用可以通过函数名,也可以通过指向函数的指针来调用。函数指针还允许将函数作为变元传递给其他函数;
    2. 两种方法赋值:

    指针名 = 函数名;  指针名 = &函数名

    ​​​​​​​说说你对c和c++的看法,c和c++的区别?

    1. 第一点就应该想到C是面向过程的语言,而C++是面向对象的语言,一般简历上第一条都是熟悉C/C++基本语法,了解C++面向对象思想,那么,请问什么是面向对象?
    2. C和C++动态管理内存的方法不一样,C是使用malloc/free函数,而C++除此之外还有new/delete关键字;(关于malooc/free与new/delete的不同又可以说一大堆,最后的扩展_1部分列出十大区别);
    3. 接下来就不得不谈到C中的struct和C++的类,C++的类是C所没有的,但是C中的struct是可以在C++中正常使用的,并且C++对struct进行了进一步的扩展,使struct在C++中可以和class一样当做类使用,而唯一和class不同的地方在于struct的成员默认访问修饰符是public,而class默认的是private;
    4. C++支持函数重载,而C不支持函数重载,而C++支持重载的依仗就在于C++的名字修饰与C不同,例如在C++中函数int fun(int ,int)经过名字修饰之后变为 _fun_int_int ,而C是 
      _fun,一般是这样的,所以C++才会支持不同的参数调用不同的函数;
    5. C++中有引用,而C没有;这样就不得不提一下引用和指针的区别(文后扩展_2);
    6. 当然还有C++全部变量的默认链接属性是外链接,而C是内连接;
    7. C 中用const修饰的变量不可以用在定义数组时的大小,但是C++用const修饰的变量可以(如果不进行&,解引用的操作的话,是存放在符号表的,不开辟内存);
    8. 当然还有局部变量的声明规则不同,多态,C++特有输入输出流之类的,很多,下面就不再列出来了; “`

    c/c++的内存分配,详细说一下栈、堆、静态存储区?

    1、栈区(stack)—  由编译器自动分配释放,存放函数的参数值,局部变量的值等

    其操作方式类似于数据结构中的栈。  
    2、堆区(heap) —  一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。  
    3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。  
    4、文字常量区  —常量字符串就是放在这里的。程序结束后由系统释放。
    5、程序代码区    —存放函数体的二进制代码。  

    ​​​​​​​堆与栈的区别?

    1. 管理方式对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。 
    2. 空间大小一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改: 打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。 注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。 
    3. 碎片问题对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。 
    4. 生长方向对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。 
    5. 分配方式堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现。 
    6. 分配效率栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

    ​​​​​​​野指针是什么?如何检测内存泄漏?

    1. 野指针:指向内存被释放的内存或者没有访问权限的内存的指针。
    2. “野指针”的成因主要有3种:
    • 指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如
      char *p = NULL;
      char *str = new char(100);
    • 指针p被free或者delete之后,没有置为NULL;
    • 指针操作超越了变量的作用范围。

    如何避免野指针:

    • 对指针进行初始化

    ①将指针初始化为NULL。

    char *   p  = NULL;

    ②用malloc分配内存

    char * p = (char * )malloc(sizeof(char));

    ③用已有合法的可访问的内存地址对指针初始化

    char num[ 30] = {0};

    char *p = num;

    • 指针用完后释放内存,将指针赋NULL。

    delete(p);

    p = NULL;

    ​​​​​​​悬空指针和野指针有什么区别?

    1. 野指针:野指针指,访问一个已删除或访问受限的内存区域的指针,野指针不能判断是否为NULL来避免。指针没有初始化,释放后没有置空,越界
    2. 悬空指针:一个指针的指向对象已被删除,那么就成了悬空指针。野指针是那些未初始化的指针。
        1. 内存泄漏
    3. 内存泄漏

    内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制;

    • 后果

    只发生一次小的内存泄漏可能不被注意,但泄漏大量内存的程序将会出现各种证照:性能下降到内存逐渐用完,导致另一个程序失败;

    • 如何排除

    使用工具软件BoundsChecker,BoundsChecker是一个运行时错误检测工具,它主要定位程序运行时期发生的各种错误;

    调试运行DEBUG版程序,运用以下技术:CRT(C run-time libraries)、运行时函数调用堆栈、内存泄漏时提示的内存分配序号(集成开发环境OUTPUT窗口),综合分析内存泄漏的原因,排除内存泄漏。

    • 解决方法

    智能指针。

    检查、定位内存泄漏

    检查方法:在main函数最后面一行,加上一句_CrtDumpMemoryLeaks()。调试程序,自然关闭程序让其退出,查看输出:

    输出这样的格式{453}normal block at 0x02432CA8,868 bytes long

    被{}包围的453就是我们需要的内存泄漏定位值,868 bytes long就是说这个地方有868比特内存没有释放。

    定位代码位置

    在main函数第一行加上_CrtSetBreakAlloc(453);意思就是在申请453这块内存的位置中断。然后调试程序,程序中断了,查看调用堆栈。加上头文件#include <crtdbg.h>

    ​​​​​​​new和malloc的区别?

    1. new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持;
    2. 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
    3. new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
    4. new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
    5. new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

    ​​​​​​​delete p;与delete[]p,allocator

    1. 动态数组管理new一个数组时,[]中必须是一个整数,但是不一定是常量整数,普通数组必须是一个常量整数;
    2. new动态数组返回的并不是数组类型,而是一个元素类型的指针;
    3. delete[]时,数组中的元素按逆序的顺序进行销毁;
    4. new在内存分配上面有一些局限性,new的机制是将内存分配和对象构造组合在一起,同样的,delete也是将对象析构和内存释放组合在一起的。allocator将这两部分分开进行,allocator申请一部分内存,不进行初始化对象,只有当需要的时候才进行初始化操作。

    ​​​​​​​new和delete的实现原理, delete是如何知道释放内存的大小的额?

    • new简单类型直接调用operator new分配内存;而对于复杂结构,先调用operator new分配内存,然后在分配的内存上调用构造函数;对于简单类型,new[]计算好大小后调用operator new;对于复杂数据结构,new[]先调用operator new[]分配内存,然后在p的前四个字节写入数组大小n,然后调用n次构造函数,针对复杂类型,new[]会额外存储数组大小;
    1. new表达式调用一个名为operator  new(operator new[])函数,分配一块足够大的、原始的、未命名的内存空间;
    2. 编译器运行相应的构造函数以构造这些对象,并为其传入初始值;
    3. 对象被分配了空间并构造完成,返回一个指向该对象的指针。
    • delete简单数据类型默认只是调用free函数;复杂数据类型先调用析构函数再调用operator delete;针对简单类型,delete和delete[]等同。假设指针p指向new[]分配的内存。因为要4字节存储数组大小,实际分配的内存地址为[p-4],系统记录的也是这个地址。delete[]实际释放的就是p-4指向的内存。而delete会直接释放p指向的内存,这个内存根本没有被系统记录,所以会崩溃。
    • 需要在 new [] 一个对象数组时,需要保存数组的维度,C++ 的做法是在分配数组空间时多分配了 4 个字节的大小,专门保存数组的大小,在 delete [] 时就可以取出这个保存的数,就知道了需要调用析构函数多少次了。

    ​​​​​​​malloc申请的存储空间能用delete释放吗

    不能,malloc /free主要为了兼容C,new和delete 完全可以取代malloc /free的。malloc /free的操作对象都是必须明确大小的。而且不能用在动态类上。new 和delete会自动进行类型检查和大小,malloc/free不能执行构造函数与析构函数,所以动态对象它是不行的。当然从理论上说使用malloc申请的内存是可以通过delete释放的。不过一般不这样写的。而且也不能保证每个C++的运行时都能正常。

    ​​​​​​​malloc与free的实现原理?

    1. 在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk、mmap、,munmap这些系统调用实现的;
    2. brk是将数据段(.data)的最高地址指针_edata往高地址推,mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系;
    3. malloc小于128k的内存,使用brk分配内存,将_edata往高地址推;malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配;brk分配的内存需要等到高地址内存释放以后才能释放,而mmap分配的内存可以单独释放。当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩。
    4. malloc是从堆里面申请内存,也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

    ​​​​​​​malloc、realloc、calloc的区别

    • malloc函数

    void* malloc(unsigned int num_size);

    int *p = malloc(20*sizeof(int));申请20个int类型的空间;

    • calloc函数

    void* calloc(size_t n,size_t size);

    int *p = calloc(20, sizeof(int));

    省去了人为空间计算;malloc申请的空间的值是随机初始化的,calloc申请的空间的值是初始化为0的;

    • realloc函数

    void realloc(void *p, size_t new_size);

    给动态分配的空间分配额外的空间,用于扩充容量。

    ​​​​​​​__stdcall和__cdecl的区别?

    • __stdcall

    __stdcall是函数恢复堆栈,只有在函数代码的结尾出现一次恢复堆栈的代码;在编译时就规定了参数个数,无法实现不定个数的参数调用;

    • __cdecl

    __cdecl是调用者恢复堆栈,假设有100个函数调用函数a,那么内存中就有100端恢复堆栈的代码;可以不定参数个数;每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用__stacall函数大。

    ​​​​​​​使用智能指针管理内存资源,RAII

    1. RAII全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”,也就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。
    2. 智能指针(std::shared_ptr和std::unique_ptr)即RAII最具代表的实现,使用智能指针,可以实现自动的内存管理,再也不需要担心忘记delete造成的内存泄漏。毫不夸张的来讲,有了智能指针,代码中几乎不需要再出现delete了。

    手写实现智能指针类

    1. 智能指针是一个数据类型,一般用模板实现,模拟指针行为的同时还提供自动垃圾回收机制。它会自动记录SmartPointer<T*>对象的引用计数,一旦T类型对象的引用计数为0,就释放该对象。除了指针对象外,我们还需要一个引用计数的指针设定对象的值,并将引用计数计为1,需要一个构造函数。新增对象还需要一个构造函数,析构函数负责引用计数减少和释放内存。通过覆写赋值运算符,才能将一个旧的智能指针赋值给另一个指针,同时旧的引用计数减1,新的引用计数加1
    2. 一个构造函数、拷贝构造函数、复制构造函数、析构函数、移走函数;

    ​​​​​​​内存对齐?位域?

    1、 分配内存的顺序是按照声明的顺序。

    2、 每个变量相对于起始位置的偏移量必须是该变量类型大小的整数倍,不是整数倍空出内存,直到偏移量是整数倍为止。

    3、 最后整个结构体的大小必须是里面变量类型最大值的整数倍。

     

    添加了#pragma pack(n)后规则就变成了下面这样:

    1、 偏移量要是n和当前变量大小中较小值的整数倍

    2、 整体大小要是n和最大变量大小中较小值的整数倍

    3、 n值必须为1,2,4,8…,为其他值时就按照默认的分配规则

    ​​​​​​​结构体变量比较是否相等

    1. 重载了 “==” 操作符
    struct foo {
    
        int a;
    
        int b;
    
        bool operator==(const foo& rhs) // 操作运算符重载
    
        {
    
            return( a == rhs.a) && (b == rhs.b);
    
        }
    
    };
    1. 元素的话,一个个比;
    2. 指针直接比较,如果保存的是同一个实例地址,则(p1==p2)为真;
    展开全文
  • 文章目录《C++面向对象程序设计》✍千处细节、万字总结一、面向对象程序设计二、C++基础2.1 C++的产生特点2.2 一个简单的C++示例程序2.3 C++非面向对象方面对C语言的扩充输入输出cinconst修饰符void型指针内联...
  • Java面向对象详解

    万次阅读 2022-03-07 11:33:08
    面向对象思想:物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后,才对某个分类下的细节进行面向过程的思索。面向对象适合处理复杂的问题,适合处理需要多人协作的...
  • COM组件的接口和对象

    万次阅读 2017-02-07 17:21:04
    一、 前言 COM规范中,最基本的两个要素就是对象与接口,因为COM...COM规范的核心内容就是对接口的定义,甚至可以COM中接口就是一切”。组件与组件之间、组件与客户之间都要通过接口进行交互。接口成员函数
  • JSON数组对象和对象数组

    千次阅读 2017-11-22 09:59:07
    Json的简单介绍 从结构上看,所有的数据最终都可以...第二种类型是sequence(序列),也就是若干个相关的数据按照一定顺序并列在一起,又叫做array(数组)或List(列表),比如“北京,东京”。 第三种类型是mapping(映射
  • 一个Java对象和Hashmap对象占用多大内存

    万次阅读 多人点赞 2019-12-03 21:47:35
    1、JAVA 对象布局1.1对象头(Header):1.2实例数据(Instance Data)1.3对齐填充(Padding)2、Java数据类型有哪些2.1基础数据类型内存占用如下2.2引用类型 内存占用如下:2.3字段重排序3、验证3.1有一个Fruit类...
  • 【实战】到底什么是C语言对象编程?

    万次阅读 2020-09-01 20:36:40
    ID:技术让梦想更伟大作者:ZhengNL整理:李肖遥前言之前肖遥分享写过一篇关于面都对象的文章,真的可以,用C语言实现面向对象编程OOP , 本篇肖遥给大家整理了ZhengNL三合一...
  • JavaScript基础之对象与内置对象(三)

    万次阅读 多人点赞 2021-07-30 16:07:54
    JavaScript 中,对象是一组无序的相关属性方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。 对象是由属性方法组成的: 属性:事物的特征,在对象中用属性来表示(常用名词) 方法:事物的...
  • 深入理解Python中的面向对象

    万次阅读 多人点赞 2017-06-12 06:42:41
    1、面向过程与面向对象的对比面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。 优点是:极大的降低了程序的
  • 深入理解Java类型信息(Class对象)与反射机制

    万次阅读 多人点赞 2017-05-01 23:19:19
    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ...深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解
  • 您是否请求映射方法中用@RequestParam注释了... 您不能在对象内部使用@RequestParam批注,但这并不意味着您没有其他解决方案。 本文中,我将向您展示如何用object替换多个@RequestParams 。 1. @RequestParams...
  • Java类和对象 详解(一)(转载)

    千次阅读 2018-07-04 10:44:26
    一、面向对象简述面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计...
  • 三流大学一流大学学生的简历有什么区别?

    万次阅读 多人点赞 2020-09-23 09:17:06
    小结 到目前为止我才讲了两个小点,但是就已经可以淘汰部分选手了,这真的就是一场竞赛,跟你一起比赛的还是一群学历很好履历很好的人,结果你还没人家仔细认真,你你拿什么去赢? 很多人抱怨自己的学历,但是...
  • 本文通过对象的创建步骤中的检查加载->分配内存->内存空间初始化->设置->对象初始化,对象的内存布局,什么是垃圾的两种算法以及四种引用,讲述JVM中对象及引用。
  • C++程序设计(四)—— 类和对象

    千次阅读 多人点赞 2018-08-18 15:19:09
    一、类及其实例化 1、定义类  类要先声明后使用;不能声明两个名字相同的类,类是具有唯一标识符的...类中有数据成员成员函数,不有类声明中对数据成员使用表达式进行初始化。 ⑴ 声明类  声明类以class...
  • 什么是抽象:从特定的角度出发,从已经存在的一些事物中抽取我们所关注的特性、行为,从而形成一个新的事物的思维过程,是一种从复杂到简洁的思维方式。如下图: 抽象这一说法美术以及我们日常生活中也能够经常...
  • MyBatis4大核心对象前言MyBatis四大核心对象SqlSessionFactoryBuiler(2+1)方法分析InputStreamReaderSqlSessionFactoryDefaultSqlSessionFactoryExecutorSqlSessionManagerSqlSessionMapper四大对象生命周期总结 ...
  • 如何理解模块、组件和对象

    千次阅读 2018-01-15 15:42:15
    **模块化开发的最大价值是分治、分治、分治! 模块化能分离职责,从而达到分治!...模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码数据结构的集合体),或指大型软件系统的
  • 2.对象和属性 3.枚举一个对象的所有属性 4.创建新对象 4.1使用对象初始化器 4.2使用构造函数 4.3使用 Object.create 方法 4.4继承 4.5对象属性引用 4.5.1为对象类型定义属性 JavaScript的设计是一个简单的...
  • ES嵌套对象和父子文档

    千次阅读 2019-09-19 10:23:37
    嵌套对象 由于 Elasticsearch 中单个文档的增删改都是原子性操作,那么将相关实体数据都存储同一文档中也就...又比如,我们可以将一篇博客文章的评论以一个comments数组的形式博客文章放在一起: PUT /...
  • java对象在内存中的分配

    万次阅读 多人点赞 2017-02-10 22:45:33
    堆内存中存放的是new出的对象,new出的对象只包含成员变量。  栈内存中:存放的是局部成员变量。对于基本的数据类型存放的是基本变量的值,而对于对象变量,存放的是堆内存的地址。  静态、常量区:存放的是静态...
  • 多态,大概每个人都知道。但是,又有几个人真的理解什么是多态、多态有哪些细节呢?如果你看到这篇文章的名字,脑海中对多态没有一个清晰的概念,不妨点进来看看,也许会有收获。
  • js:面向对象编程,带你认识封装、继承多态

    万次阅读 多人点赞 2017-06-05 22:59:25
    先上一张图,可以对面向对象有一个大致的了解,然而什么是面向对象呢,用java中的一句经典语句来就是:万事万物皆对象。 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完
  • 设计模式之面向对象七大基本原则

    万次阅读 多人点赞 2015-04-27 16:25:48
    概述运用面向对象的思想进行软件设计时,需要遵循的原则一共有7个,他们是:1. 单一职责原则(Single Responsibility Principle)每一个类应该专注于做一件事情。2. 里氏替换原则(Liskov Substitution Principle...
  • Java面向对象面试题总结

    万次阅读 多人点赞 2019-02-27 17:22:05
    (3)其他对象不能直接修改本对象所拥有的属性方法 (4)封装反映了事物的相对独立性  (5)封装编程上的作用是使对象以外的部分不能随意存取对象的内部数据(属性),从而有效地避免了外部错误对它的“交叉...
  • C++面向对象基础

    万次阅读 多人点赞 2018-05-20 12:40:59
    面向对象基础面向对象三大特性封装性:数据代码捆绑在一起,避免外界干扰不确定性访问。封装可以使得代码模块化。优点:确保用户代码不会无意间破坏封装对象的状态被封装的类的具体实现细节可以随时改变,而无须...
  • 面向对象的五大基本原则

    万次阅读 2019-01-06 20:18:24
    面向对象的三大特性:封装、继承、多态 面向对象的七(或五大原则,前五项)大基本原则:单一职责原则(SRP) 、开放封闭原则(OCP) 、里氏替换原则(LSP)、 依赖倒置原则(DIP)、 接口隔离原则(ISP)、迪米特...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 262,431
精华内容 104,972
关键字:

和对象单独在一起说什么