在某一时段内数值a连续为1,用c++如何识别这一现象?


     这样的情况,让这种错误在编译的时候就被发现了。

条款04:确定对象被使用前已先被初始化

     non-local static对象的初始化,主要指的是多文件的情况,想某头文件中的某变量在别的头文件或CPP中也被使用。最直观的方法是用extern。如下代码

     但这里的问题是我在别的文件调用a时,我不能保证a已经被初始化了(都怪糟糕的编译器),于是就有以下方法

     要调用时只需要用a()就可以了,调用的是local变量的引用,而且保证有初始化。

2. 构造/析构/赋值运算

条款05:了解C++默默编写并调用哪些函数

     类若用户没有定义会默认有copy构造函数、copy assignment操作符、析构函数,这种copy就是二进制的copy。而如果有引用或者是const成员变量时,以上的函数若需要只能自己定义,特别是copy assignment操作符,因为reference是不能指向不同对象的,而const也是不能被改变的。

条款06:若不想使用编译器自动生成的函数,就该明确拒绝

     不想类拥有copy构造与copy assignment操作符,可以把这些函数显式地定义在private中。另一个方案,就是定义一个uncopyable的类,这一个类就是把这些函数定义在private中,需要定义不能被copy的类时可以直接继承。

条款07:为多态基类声明virtual析构函数

     这样做的原因是,用父类指针指向子类时,如果没有在父类定义的virtual析构,不能保证指向的子类能正确的被销毁,可能调用的还是父类的析构。

条款08:别让异常逃离析构函数

     C++不喜欢析构函数吐出异常。原因是当C++遇到两个以上的异常同时存在时不是结束执行就是导致不明确行为。而出现两个以上的异常在容器成员要析构时要吐出异常的话可能就是一系列的异常,于是就出现了C++不允许的情况了。(个人理解,其实感觉还是没说清)

     方法三:重新设计,对可能出现的异常先作出反应

     从以上可知,要尽量把析构中可能出现的异常的部分转移到别的成员函数中,把异常扼杀在别的成员函数中,而不是析构函数中。

条款09:绝不在构造和析构过程中调用virtual函数

 这里应该针对的是子类没有定义构造函数,直接继承父类构造函数的情况,原因是子类在继承父类的构造函数后其调用的顺序是先调用父类的,再调用子类的,然后这里的问题是,如果是先调用父类的话,子类中的成员变量是被编译器认为是没有定义的(包括virtual函数的定义),因此是调用不了的。同样的析构函数在被调用时,是先把子类的成员变量空间都释放了,然后再调用父类的,此时对于父类而言,子类中的定义都是不可知的(包括virtual函数的定义),因此也并没有调用理想中的函数。

     直接在构造或者析构中放virtual是比较明显的错误,但是当在virtual函数之外再定义一个非virtual的函数,这样的错误就很隐蔽了,不可不防。

条款11:在operator=中处理“自我赋值”

     这里就涉及到另一个问题,“异常安全”,如果pb在new的时候出异常了,pb指向的数据就没了,这时候数据就不完整了,异常无法处理。

     这样可能不如方法一效率来得高,但至少安全。

条款12:复制对象时勿忘其每一个成分

     这里容易出问题的是子类继承父类时,如果重新定义了构造函数,然后又没有考虑到父类的构造函数(因为父类构造函数可能会存在一些私有变量的初始化),这样的后果是,对象会调用父类的无参构造函数,如果无参构造函数是你想要的那还能接受,但是如果无参构造参数里面出了问题(比如说忘了给某私有变量赋值,那样就黑了)。

     另外,copy赋值与copy构造如果代码相同,不应该互相调用,应该借助别的成员函数(如init())来完成。

条款13:以对象管理资源

条款14:在资源管理类中小心copying行为

     这里主要是针对RAII对象,因为RAII对象是资源管理的脊柱(书是这么写的。)。设计好自己的RAII对象的COPYING行为,一般是禁止复制或者是引用计数。还有一些就是复制底部资源以及转移底部资源的拥有权(如auto_ptr)。

条款15:在资源管理类中提供对原始资源的访问

     RAII对象把资源保护得好好的,但是有些函数却需要直接用到RAII对象所保护的对象,如auto_ptr里的指针是A* a,但是我函数f(A* a)要接收的参数类型是A*,那样是不可能把auto_ptr传递给函数f的,这时候就需要一个获取原始资源的接口了。这种访问可能经由显式转换(即通过get()成员函数)或者是隐式转换(即定义operator

     显式转换比较安全,因为它将“非故意的类型转换”的可能性最小化了。但使用不太方便,因为看着太长太烦了。

条款16:成对使用new和delete时要采取相同形式

     这里主要强调的是new时用的[],delete别忘了[]。没有的当然就不用加了。因为数组的new是带数组长度的。

条款17:以独立语句将已new的对象置入智能指针

     这里的问题是不知道编译器的处理顺序(这个问题只能怪编译器,特别是各种不同标准的C++编译器。),理想顺序应该是先new A,然后把它传给shared_ptr,然后再执行func(),最后再赋给process。或者是先执行func(),然后再new A,然后blabla。这样都没有问题。

条款18:让接口容易被正确使用,不易被误用

     一方面是促进正确使用,这里提到的主要是接口的一致性,以及与内置类型的行为兼容。

     这里要正确使用很容易,要误用也很容易,一个不小心把month与day的顺序搞错了,就出问题了。于是就有个方案是建立新类型Month, Day, Year。对应的类就应该这么写

     这里说的例子还是日期,月份只有12个,别的都是错的,为了防止用户出错,我们可以限制它的值,于是Month类可以这么写:

     书中列的一系列注意问题,没细讲,都列出来吧

条款21:必须返回对象时,别妄想返回其reference

     就是搞清楚返回的东西是在stack还是在heap,就算是返回static也是有可能出错的,问题代码是这样的:

这里一定会是true的!

条款22:将成员变量声明为private

     首先是代码的一致性(调用public成员时不用考虑是成员还是函数)。

     其次封装性,都写成函数进行访问可以提供以后修改访问方法的可能性,而不影响使用方法。另外,public影响的是所有使用者,而protected影响的是所有继承者,都影响巨大,所以都不建议声明成员变量。

     书中展开讨论的是这个情况,当你有一个类这么写的:

     然后因为有个经常使用的操作需要顺序的使用三个成员函数,所以就想写一个便利的函数。这里有个选择,是写成member函数,还是non-member non-friend函数。也就是

     而这个作者也有一套关于封装性的解释,作者通过计算能够访问对象内数据的函数数量,大概计算封装性。原则就是越多函数能访问,封装性越低。

     当然C++是不会阻止大家把函数都写成member函数的,java/c#使用者不用担心。只是针对以上情况,C++的写法一般是写在namespace里面(其实我觉得是不是可以写成static的member函数呢?)

     namespace是可以分好几个文件写的,不受约束,而且可扩充,当然它不是一个可能实例化的存在。

     class的声明必须得一起写,要扩充也只能继承,但问题是,不是所有的类都设计用于继承的。不过class是一个可以实例化的,就是数据是有自己的私有空间的,可能带着周围跑的。

条款24:若所有参数皆需类型转换,请为此采用non-member函数

     这里暗含一个条件就是构造函数是允许隐式类型转换(不带explicit),不允许的话两个就过不了。而如果用non-member就一点问题也没有了。

     这个operator*的特点就是,两个变量其实都有类型转换的需要,如果是写成member函数,那么左操作符就不能进行类型转换了。而non-member函数就能满足这一需求了。

条款25:考虑写出一个不抛出异常的swap函数

     首先要明白,swap函数用处很大,在之前的条款11中就用于处理自我赋值可能性上,而在条款29(往后看吧)将会说到与异常安全性编程相关的用法。总之很重要,同时很复杂。

 但当自己定义的类,有更高效的swap方法时,如成员变量中的数据包含指针,copy赋值或copy构造时进行的操作是对指针指向的内容进行完全的拷贝(很合理),但是放时swap里面时就要进行这样多次的指针指向内容的拷贝,再进行交换,而事实上,更好的方法是直接进行指针的交换就可以,不需要通过copy赋值与构造。(常适用于pimpl手法实现的class,pimpl,pointer

     因此这里就引进了特化的方法。这里要访问到类内数据(private),就得是member或者是friend了,而对于swap这么一个特殊的函数(可用于异常安全性编程,本人猜想),则倾向于先定义一个member的swap,然后再定义一个non-member的swap。STL内也是这么实现的,代码如下

     到这里,问题应该说是算解决了,但std是个很特殊的命名空间,里面的东西你可以用,可以特化,但写这个std的人们是不想我们去改里面的东西,甚至是重载也不行(虽然是可以编译通过)。作为乖孩子,我们只有在自己的小空间里满足自己的小要求了,也就是把这个重载写在自己的命名空间里面。

     到最后,就是要注意最终使用时,编译器会调用哪一个的问题了,容易不清楚的是以下情况

     试想如果没有的话,可能就只能找到你自己的小空间里的swap函数了,然而T本身是不受约束的,你自己的命名空间内的swap是不够用的。

     再想想,如果特定约束为std::swap(a, b),则问题就是只能找到std内对应的swap版本,却找不到你自己定义的更高效的实现版本。

条款26:尽可能延后变量定义式的出现时间

     主要是延后到要用时才定义,延后到你愿意赋初值时才定义。

     另外有个问题就是应该把变量定义在循环体内还是体外,这得看情况。

     copy赋值开销低于构造+析构开销,且效率要求高,则建议定义在循环体外,否则定义在循环体内(主要考虑便于管理)。

条款27:尽量少做转型动作

     1. 转型不会改变待转型对象的值,只是产生一个转型后的副本。

条款28:避免返回handles指向对象内部成分

条款29:为“异常安全”而努力是值得的

     3. 不抛保证:保证不抛出异常,这与带着空白的异常明细的函数不一样,带着空白异常明细的函数是指一旦抛出异常,将是严重错误。

          b) 另外,不是所有函数都能实现的,特别是有嵌套的保证需求的时候。比如,要实现强烈保证,函数内调用的函数也同样需要强烈保证,而就算保证了调用的函数都能做到强烈保证,其调用带来的状态改变也有可能是不能复原的。

     3. “异常安全保证”服从木桶原理,决定异常安全的关键在于最薄弱的“异常安全保证”

条款30:透彻了解inlining的里里外外

     1. inline函数的调用,是对函数本体的调用,是函数的展开,使用不当会造成代码膨胀。

     3. inline函数只代表“函数本体”,并没有“函数实质”,是没有函数地址的。

     1. 构造函数与析构函数往往不适合inline。因为这两个函数都包含了很多隐式的调用,而这些调用付出的代价是值得考虑的。可能会有代码膨胀的情况。

     2. inline函数无法随着程序库升级而升级。因为大多数都发生在编译期,升级意味着重新编译。

6. 继承与面向对象设计

条款31:将文件间的编译依存关系降至最低

     这个问题产生是源于希望编译时影响的范围尽量小,编译效率更高,维护成本更低,这一需求。

     实现这个目标首先第一个想到的就是,声明与定义的分离,用户的使用只依赖声明,而不依赖定义(也就是具体实现)。

     但C++的Class的定义式却不仅仅只有接口,还有实现细目(这里指实现接口需要的私有成员)。而有时候我们需要修改的通常是接口的实现方法,而这一修改可能需要添加私有变量,但这个私有变量对用户是不应该可见的。但这一修改却放在了定义式的头文件中,从而造成了,使用这一头文件的所有代码的重新编译。

     这里会有个有意思的地方,为什么用的是指针,而不是具体对象呢?这就要问编译器了,因为编译器在定义变量时是需要预先知道变量的空间大小的,而如果只给一个声明而没有定义的话是不知道大小的,而指针的大小是固定的,所以可以定义指针(即使只提供了一个声明)。

     这样把实现细节隐藏了,那么实现方法的改变就不会引起别的部分代码的重新编译了。而且头文件中只提供了impl类的声明,而基本的实现都不会让用户看见,也增加了封装性。

     以上说的为了去耦合而使用的方法不可避免地会带上一些性能上的牺牲,但作者建议是发展过程中使用以上方法,当以上方法在速度与/或大小上的影响比耦合更大时,再写成具体对象来替换以上方法。

条款32:确定你的public继承塑模出is-a关系

条款33:避免遮掩继承而来的名称

     这个问题来源是变量的有效区域引起的,而引入到继承,就是子类与基类同名函数的关系问题了。

     2. 当子类重载基类的函数的时候,基类所有同名函数均不会被继承,即被遮掩了。

     既然是继承,那就是is-a关系了,没有基类的被重载函数一般情况是不合适的。

     解决方法二:转交函数(forwarding function),只继承个别版本。有时候我们只需要继承重载函数中的个别版本,如以上的mf1的无参版本.

条款34:区分接口继承和实现继承

     pure virtual,只能继承接口。若被继承,则必须提供实现方案,基类也可以实现默认方案(函数名同名),只是不会被继承,要调用必须得inline调用(类似Base::func()这样的形式)。

     impure virtual,继承接口与缺省实现。基类提供默认实现方案,可被子类继承,也可被重写,继承时要搞清楚是否需要继承缺省实现。

     总的来说,就是接口是一定会被继承的(至少接口名),实现方法怎么继承就看实际。

条款35:考虑virtual函数以外的其他选择

     这条款谈了两种设计模式,鼓励我们多思考的。

     以游戏中的人物设计继承体系为例子,不同的人物有不同的计算健康指数的方法,就叫healthValue函数吧,很自然的就会想到设计一个基类,把healthValue函数设计为virtual的用于继承。

virtual(当然这里是可以protected的),这样的好处在于其中的前后“...”(省略号),这部分可以进行一些类似检查、调整的操作,保证doHealthValue()在一个适当的场景下调用。而且子类也可以继承实现private virtual成员函数。

     2. Strategy模式,这一模式令实现方法是个变量,就算是同一个对象在不同的时段也可以有不同的实现方法。但这里都有个约束,就是对私有成员变量的访问限制。

条款36:绝不重新定义继承而来的non-virtual函数

     继承non-virtual函数的后果是,最终函数的实现效果不由声明时的类型决定,而是由使用时用的指针或者引用类型决定。简单些用代码表达如下:

     这个其实是不符合non-virtual成员函数的不变性特点的。也不能体现出B的特异性(因为t被调用f时的效果不一定来自B)

条款37:绝不重新定义继承而来的缺省参数值

     另外,对应的,静态类型就是在声明时所采用的类型,动态类型就是目前所指对象的类型。

     而这条款讨论的是更细的一层,“继承带有缺省参数值的virtual函数”。(因为条款36说过了,绝不继承non-virtual函数)

     这里的问题是C++编译器对缺省参数是静态绑定的(出于效率考虑),virtual函数却是动态绑定的。因为这样的设定就会可能会导致调用的不一致(当用到多态时),看以下代码:

     因为p的静态类型是A*,所以缺省是来自A,而动态类型是B,所以f()调用来自B。

条款39:明智而审慎地使用private继承

     2. private继承可能会增加编译依存关系。因为一般可以通过只在class内包含一个仅仅是声明而没有实现的类型的指针,实现对用户是不可见的方式(可以是在别的cpp文件中public继承)去替代private继承。这就涉及到条款31提到的编译依存性最小化的问题了。

 还有一种情况就是需要对象尺寸最小化时。当一个类里面没有non-static的数据时,C++编译器认为对象都应该有非零大小,因此,当用包含的方式(当为对象中的成员变量)时,没有non-static的数据仍然会被分配空间(至少char的大小,虽然没有意义),而如果是private继承就不会增加空间开销的。当然这种基类就是一般只有一些typedef或者non-virtual的函数,没有任何可能带来空间花销的成员。

条款40:明智而审慎地使用多重继承

     使用多重继承就要考虑歧义的问题(成员变量或者成员函数的重名)。

     复杂一点的,就可能会出现“钻石型多重继承”,以File为例:

     2. virtual继承会非常复杂(编写成本),因为无论是间接还是直接地继承到的virtual base class都必须承担这些bases的初始化工作,无论是多少层的继承都是。针对这一特性,可以让class实现类似java的final功能,这就不是这一条款涉及的内容了,这里只贴代码跟说明吧:

// 因此,不能通过,

     最后总结就是,能用单一继承尽量使用单一继承,而多继承在审慎考虑过后也要大胆使用,如之前提到的is-a与is-implemented-in-terms-of两个关系分别与两个base class相关时,只要审慎考虑过了再使用就可以了。

条款41:了解隐式接口和编译期多态

     隐式接口,就是例子中类型T的变量w使用到的所有相关的函数。就是要求使用时调用的类型T必须具备的接口。

     编译期多态,就是通过不同的template参数(T)导致不同的调用结果,而这些发现在编译期。

条款43:学习处理模板化基类内的名称

     C++编译时,如果继承的是模板化的基类,那么像普通的基类继承那样直接调用基类的函数是不合法的。本条款说到的原因是,模板化的基类是可以被特化的(可以参考条款33),而特化后的基类是可以不具备某一函数的,而这一函数也许就是你继承时需要调用的。把书中的代码敲一下看看吧:

     因为MsgSender是能被特化的,而特化的版本是允许不存在某些接口的。如我们特化一个CompanyZ的版本如下:

条款44:将与参数无关的代码抽离templates

     (其实这条款没有太多的实际代码参考,将懂不懂吧,直接把最后的总结抄了下来)

     类型参数造成的代码膨胀,往往可降低,做法是让带有完全相同的二进制表述的具现类型共享实现码

条款45:运用成员函数模板接受所有兼容类型

     这样的操作对于指针是很自然的,也是很方便的,只是直接用的指针实在太不安全了,应该让智能指针也能有这样的能力。类似以下的效果:

     其实认真观察以上的操作,以上都是copy构造(声明时使用=调用的是copy构造)。于是就有了以下的解决方案。

     以上就是成员函数模板,就是泛化了的成员函数。

     这里需要注意到,泛化的copy构造其实在U==T时就与正常的copy构造在实质上是一样的。

     但是事实上,如果你没有定义正常的copy构造(没有泛化的),编译器依然会默默地生成正常的copy构造。

     所以,如果想所有的copy构造都在自己的掌控,还是要进行正常的定义。

条款46:需要类型转换时请为模板定义非成员函数

     另外,用doMultiply函数去封装是因为写进class内意味着inline,这样是可能带来代码膨胀的,为了避免这种情况出现,一般都会在外面封装一个实现函数(这里这么用其实是为了提醒大家使用时要注意inline的问题而已,就这个例子来说,代码展开了也只是一行,封装意义不大)。

     说到这里应该明白我不同意此条款的翻译的原因吧。

     2. 以上为什么会有继承关系?或者更进一步问,为什么需要继承关系?

     先说说第3个问题的个人理解,因为这只涉及有类型信息,没有不可被用户所知的私有信息,因此struct合适。

     至于第1、第2个问题先不具体回答,但首先我们明确的一点是这些classes是为类型区分服务的。

     这里就需要一个能够得到类型信息的方法,也就是我们要提到的traits。

     由以上分析可知,traits class内包含信息的方法是不可取的(内置类型做不到),因此就必然用templates的方法实现并为传统类型提供特化版本(不太明白的话,往下看就明白了)。当然一般也是struct的:

     实际上,这个advance函数其实是有缺点的(条款48深入解释)。而事实上用if来进行类型上的判断也不是最好的方案,因为if判断发生在运行期。这里提供的方案是利用函数重载的特性,因为重载函数的选取就已经包括了函数参数类型的判断并且发生在编译期实现方案如下

     1. 因为如果用enum或者const这种方式,相应的类型判断就应该用if,而if判断只能在运行期,不是最好方案,而用class的形式就可以借助重载函数的选择过程中进行类型判断的这一特性了,并且这一切都发生在编译期。

     2. 这个继承关系无疑是正确的,都是is-a关系。除了明确关系外,这样做其实是有好处的,细心的同学可能会发现这里没有实现std::forward_iterator_tag版本的重载函数,但事实上只需要std::input_iterator_tag的重载函数就足够了,因为两者是is-a关系。

     4. 为需要兼容的类型提供特化版本,如上例对指针的特化。

 这个代码在运行结果上是没有问题的,但缺点就是,这个具现出来的代码里,存在废话。因为当参数类型(std ::list < int>:: iterator)确定以后,if里面的判断其实是固定的,就是说,这个函数,的if或者是else里面那部分代码实际上是永远都不会被调用的。这是一种浪费。用条款47中的方案也就是TMP的方案就没有这个问题了。

     然后书中就列了几个TMP的应用例子,没有代码实现(失望。得自己去找找看了),我就抄一下吧:

     TMP很酷,不过很不直观,而且资料还很少,虽不能成为主流,但不可缺少。

     首先我们需要明确的就是我们需要currentHandler存当前的new_handler,而这个是所有Widget对象共享的(不是独有的),所以需要static。static成员变量必须得在外内被定义(const的整型可以在成员内部定义),如下:

     最后就是重头戏new了,这个函数为什么也是static呢?这是要被全局调用的,而且如果不是static,对象还不存在,又如何调用new呢?所以static也可以理解,但可能一些事多的同学(如我)可能会发现,在实际操作时,我们不加static的声明,其实效果也是一样的!为什么呢?其实是这样的,在C++的标准里面说到:

     这里的精华我觉得是NewHandlerHolder的使用,利用了临时对象在栈中的特性,就lock对象一样,在出栈时自动调用析构函数,还原之前的状态。

     对Widget这个类来说,支持设置自己的new_handler的功能算是实现好了,很明显这样的代码是可以复用的,怎么复用呢?这段代码有个核心问题就是需要同样的class(不是对象)共享相同的currentHandler,很自然就会想到使用base classes的templates。

 用了nothrow为什么说是可能成功呢?这里的成功是指测试能达到想要的效果,这里的意思就是分配失败了,但pw2可能并不等于0。因为nothrow只作用于给Widget分配内存时起作用,而当Widget进行自己的构造函数时所调用的东西(比如说进行一个可能会失败的new操作)就不是nothrow所管的事情的。这里的建议是忘记nothrow吧,它是为了照顾老使用者而产生的东西。

条款50:了解new和delete的合理替换时机

条款51:编写new和delete时需固守常规

调用标准的new } 大概这样的形式。另外提醒下sizeof的返回不可能为0的(条款39),因此当size为0(0 byte的请求)时,就一定会交给大括号内的内容,这里就是标准的new去处理了。

     当分配到了内存,但是Widget的构造函数出错时,那么就需要做到分配的内存取消掉,并恢复原样。在这里,这个任务就交给了正常operator new对应的operator delete(注意是对应的)。到这里我们需要认识下这些正常的东西的函数签名式:

     这一版本已经纳入了C++标准程序库了,在<new>中,负责在vector未使用的空间上创建对象。而这一目的也导致了placement new这一名称的出现:在特点位置上new。这也是placement new多数所指的版本:唯一额外参数是个void*的new。

     而这里讨论的主要是那个比较小众的版本:带任意额外参数的new。我就继续抄书中的代码了(做过整合的)。

是不是有点像条款49中nothrow的调用?

     这里我们特别强调对应关系啊,因为如果调用了placement new,出错时候如果没有对应版本的delete的话,程序是不知道如何delete的!而且这些都是发生在运行期的!

     另外,还有一个问题,就是函数名被掩盖的问题。因为所有(不同版本)的operator new重写都会掩盖global版本和继承而得的operator new!就像刚才写的Widget,没有写正常的new版本,然后

     同样的子类继承父类后,重写了new,那么父类的new也是会被掩盖的。

这一部分以提醒介绍为主,没有太多的代码。作者说很重要。不过我是看过就算了。把各条款的总结抄一下吧。

条款53:不要轻忽编译器的警告

     1. 严肃对待编译器发出的警告信息,努力争取无任何警告。

     2. 不要过度依赖编译器的报警能力,因为编译器是有可能变的。

条款54:让自己熟悉包括TR1在内的标准程序库

条款55:让自己熟悉Boost

1、上面代码 Aobj *f 只是声明了一个野指针,是不是 运行到 f->x = 1;这句,编译器会自动分配一块Aobj类型的内存,并且让f指向这块内存?
2、f 指向的这块内存是在栈上还是堆上分配的,如果是在堆上的,需要手动释放吗?如果不需要,是因为不是自己主动申请的,所以编译器自己会释放掉吗?

项目相关的问题就暂时隐去了,毕竟没有参考性。一些问题给出了自己稚嫩的回答,但不保证是正确的回答,建议使用markdown复制阅读,这样看大纲就可以忽略烦人的辣鸡个人答案了。第一次发,如有违规,私信我然后会进行修改或删除。祝大家找到好的实习的工作。

1.项目,问这个主要干了啥,不用说细节

2.Linux指令怎么查看占用内存情况,怎么查看占用端口情况

4.数组和的内存存储和插入

  • 数组插入需要移位,空间不够需要扩容;直接插入即可。
  • ""先在项目找,再去全盘找。
  • <>先在库文件找,再去项目找。
  • <>不能找的自己定义的;""可以找到。
  • #include<>:编译器直接从系统类库目录里查找头文件:
  • #include"":默认从项目当前目录查找头文件,所谓项目当前目录,就是项目工程文件(.vcxproj)所在的目录。如果在项目当前目录下查找失败,再从项目配置的头文件引用目录查找头文件,所谓项目配置的引用目录,就是我们在项目工程中设置的头文件引用目录。如果项目配置的头文件引用目录中仍然查找失败,再从系统类库目录里查找头文件

7.vector增加元素时,内存变化情况以及push_back使用的是指针、引用还是拷贝

  • 主要看size和capacity,如果capacity不够需要重新分配内存。(扩容大小忘了)
  • Push_back使用的是拷贝,类的话也是使用拷贝。
  • TCP是可靠的;UDP是不可靠的。
  • TCP是基于连接的;UDP只管发送,不管对方收不收得到。

9.TCP可靠是怎么保证

11.socket相对于另外几个的优点

  • socket可以用在不同机器之间的进程间通信

12.计算机内存只有1M,有一个文件1G,怎么

  • 归并:先每一个1M文件进行,然后对这一1024个文件进行合并。

13.不用pow实现计算m的n次幂,时间复杂度

  • 举例:4的7次方计算方法:

14.代码题:对一个正数组成的数组,用最少的元素构建一个子集,满足该子集之和大于剩余元素之和。输出子集的一种情况即可。

  • 直接从大到小,计算sum;注:面试官说用sort就行了。
  • 从前往后遍历将数组分成两部分,当前面部分比后面大时,输出前面数组。

15.代码题:实现两个函数模拟EXCEL的列,一个是由数字到字母,一个是由字母到数字。即28->AC;AC->28。

  • 就很基础,注意A对应的数字是1而不是0即可。

16.文本输入时撤销键应该用什么数据结构实现

    • 有的,去年记得好像是房补
  • 面试官一开始说的是属于哪个组?
  • 在xxx需要什么技术嘛?
  • 可以多熟悉linux的使用
  • 下次面试会问些啥问题呀hhh?
    • 这个我不知道,下次面试官不知道是谁
  • 感觉这次面试的内容太简单了
    • 这次面试没有问项目的内容
    • 之后的面试会更多偏向项目和技术
    • 最后一次技术面会是组里的boss来面
  • 后天要笔试了,笔试结果对最终录用有影响吗?
    • 这个会参考的,但是不作为决定指标
    • 有些同学技术好,有些同学沟通能力强,但总归是喜欢聪明一点的同学。

答:我叫xxx,广东清远人,本科和研究生就读于xxxx大学,本科是xx专业,大四保研到xx专业。求职意愿是C++后台开发。就这么多。

答:打打王者,看看国外名著。

4. 我看你这个简历上面有实习是吗

答:不是的,我放到项目那块了。只是一个合作的项目。

5. C++特性这块哪方面用的比较多,类、STL之类的

答:STL用的多,太好用了。

6. STL中你用哪个容器比较多

答:这个不太清楚,好像是按照2的幂次数进行向上取整。

9. 其他容器的内存情况呢

答:是我提到的那些嘛(是的)。栈和队列是双端队列的容器适配器,是非连续内存存储的。(unordered_map)底层是(有点奇怪)。

10. 析构函数和构造函数可以用虚函数嘛

  • 析构应该用:因为如果不虚函数化,基类指针指向派生类对象时,调用析构函数只调用了基类的析构函数。
  • 构造函数不应该用:因为虚函数调用机制是根据虚函数指针来调用的,而虚函数指针是需要构造对象之后才有的,因此把构造函数虚函数化会无法成功进行类的实例化。

答:是指虚函数表吗(是的),虚函数表保存的是虚函数的指针,所以大小是虚函数个数*4个字节

12. 你平时用什么语言

13. 另外两个语言各自的优点和缺点呢

  • Python的优缺点都是一样的,库和包都很多,如果熟悉包和库就能很快很简洁地满足需求。
  • Matlab话缺点是脚本语言,没那么灵活。优点是处理矩阵运算特别快。

15. TCP滑动窗口什么意思,怎么做流量控制

  • 滑动窗口为发送的请求队列,若最左边的请求收到ack,则窗口移动。
  • 流量控制包括慢启动、快速重传、一些概念忘了。大概流程是这样……

17. 能说一下用的什么模式吗

答:模式是指并发模式还是I/O复用的模式(I/O复用的模式)。用的是epoll。

18. 那epoll是用什么形式使用的

19. 我的意思是epoll的触发模式是什么

答:ET模式,边沿触发模式。

20. 为什么要用ET模式

答:要求请求被一次处理,不再进行通知。减少事件遍历个数。

21. 还有什么触发模式吗

答:LT模式。水平触发模式。

22. 两个触发模式的应用场景

  • 我觉得LT模式适合请求不能被忽略或者错过的场景,并发量高但是处理能力较低的场景。因为LT会重复通知请求事件。
  • ET的话适用于处理能力较强的场景。

23. 那请求量大但简单的场景适合用什么模式

24. 实现后的性能怎么样

答:没有测试,对压力测试不是很了解。主要是学习知识。

25. 你上面那个项目也是参考网上别人的项目吗

答:内存池吗(是的)。是的。

26. 大概讲下实现的原理

  • 快是因为线程都有各自独立的Thread Cache,不用加锁。
  • 数据结构使用的是Free-list,细节很难说得清楚。。。

27. 那你从这两个项目里面学到了什么

  • 另一个项目主要就是网络的相关知识,像I/O复用这些。

答:最近换的macos,只会一些基本的操作。

答:没有。(那你这两个那么大的项目怎么编译?)直接g++,其实这两个项目很小。

30. 怎么用指令修改文件大小

32. 用过什么数据库

答:本科学过,用得少不太记得了。

答:最简单的方法:使用索引

35. 为什么使用索引快

答:索引能够缩小遍历的范围,和查字典一样,比如查林字,可以直接查L的拼音。

36. 索引快的原因是什么

答:不太清楚。我知道索引有哈希索引和B树索引。

37. 索引的存储格式是什么

答:我觉得应该是树。。。不清楚

39. 你英语怎么样,能用英语说一下上面这个项目吗

答:普通交流程度。直接介绍的话有点难,因为没有准备。但是如果是沟通的话可以的。

40. :char a[n]和char b[m],用最小时间复杂度和最小空间复杂度构建数组c,c的元素为a中与b不重复的元素。(就是c=a-b)用C语言写。

答:疯狂argue,忘了怎么写动态数组,而且这个输入输出没给定。我先讲下思路吧。因为是字符串,所以用128长度的int数组表示b中对应字符的出现情况。若出现,置为1,没出现置为0。遍历a数组,若对应位置为0,加入c,否则不加入。实现的时候最终还是用上了心爱的vector数组。被怼了句“用C++我还考你什么”

1. 你非科班怎么学计算机的

答:本科有辅修操作系统、计网、数据库。课余时间看推荐的课程,b站等学习网站很多。

2. 你对从事游戏领域的工作有兴趣吗

答:以前玩的多。王者荣耀、和平精英。端游的话玩DNF。

三面速度面(20min)

3. 如果这次过了下次是什么面


因为一二面是连续面的,而且一面难度很低,就没记录了。

1. 先做个自我介绍吧

  • 姓名、学校、本科、硕士、研究方向、转C++开发原因。
  • 项目自上往下按照自己的理解依次介绍。
  • 以上就是我的自我介绍。(最后一句话)
  • 关联式容器:map以及它的几个变种,set以及它的几个变种。
  • 散列表作为不考虑顺序的键值对很好用。

4. 你刷的什么题目,看你简历上面也没有ACM的比赛经历

  • ACM已经不能参加了,要高中毕业后5年内才能参加。
  • 刷的上面的题,大概刷了一个月。

5. 所以说你是最近刷的题吧,就是为了准备面试对吧,说实话就好

  • 是的,其实是寒假过年的时候刷的了,最近刷的少了。

6. 那我出道题考考你。(map怎么删除value值为奇数的节点)

  • 我觉得这道题的难点应该在于迭代器失效吧。

  • 然后写了代码,出了几个问题。。。。。。。

  • 第一个问题,忘记了怎么取map的value值,不确定是不是当作pair来处理,面试官说是的,但是问我为什么不知道这种访问方式,解释说一般在使用map的时候都是作为读取,然后写了一个if ( map.find(x)!=map.end() ) { int y = map[x] }这也为下一个问题做了铺垫。

  • 第二个问题,这个问题是他后来快结束的时候又返回来问我这个代码有没有问题的。当时写的代码是

    他问我,it++真的没问题吗,我回答说这样会跳过被删除的迭代器,他反问,那你这个for循环不是又回递增迭代器吗。好的,发现错误了,他让我重新写一个,我写了while循环。

  • 第三个问题,改成这样你再确认一遍,真的没问题了吗?看了一遍,我暂时发现不了问题了。嗯,你回去看看it是什么类型的变量,真的可以使用.操作符吗,立马纠错,哦哦,应该用指针。

7. 那你说说看什么容器可以使用[]进行访问,以及访问的原理

  • 首先说哪些容器吧,序列式容器里面的vector、string、deque,关联式容器map。
  • 原理是对内存连续,所以可以通过头指针进行偏移操作来访问。但是其实map的[]访问是一种搜索的过程。

8. 如果让你来对网络进行分层,你会怎么分,不一定要7层和4层5层,就按你的理解就行

  • 那就分为五层吧,物理层、数据链路层、协议层、应用层。
  • 这样分的理由是我认为传输层和网络层协议的层次关系有点复杂了,所以干脆分为一层hhh。
  • 然后物理层就是各种物理通信方式
  • 数据链路层负责将各种通信方式提供一个统一的接口向上为协议层服务
  • 协议层这么分主要是HTTP加了好多层东西,TLS/SSL在HTTP和TCP/UDP之间(挖坑)
  • 应用层呢就是各种应用浏览器使用协议进行通信

9. 按照这么分的话HTTP在哪一层

  • 这个,在协议层。反问,在协议层吗?嗯,应该是在协议层里面具体的网络层。你确定吗?不要用猜的,或者也可以用猜的。但是你要根据能得到的知识来进行推测,推测总比完全猜要好对吧。嗯,那么其实我认为HTTP本身是在TCP/UDP之上的,那么肯定不在传输层,而HTTP应用于各种浏览器场景,我认为是应用层。(我是真忘了在哪一层,确实是应用层)

10. TCP/UDP的区别有哪些呢(好像是一面的问题了,分不清了)

  • 可靠、不可靠(顺便提一下HTTP/3使用的就是UDP,但是是可靠的,模仿了TCP的实现)

11. TCP为什么可靠呢(好像是一面的问题了,分不清了)

  • 序列号保证有序(他说也算)
  • 校验和(他反馈这个确实是他想听到的答案)

12. 说一下拥塞控制(好像是一面的问题了,分不清了)

  • 慢启动、拥塞避免、快重传、快恢复。公式太多,细节记不清了,但是曲线是知道的。
  • 拥塞控制的主要思路呢是通过拥塞窗口来影响流量窗口。
  • 慢启动是指数上升,拥塞避免线性上升,快重传直接减半,快恢复线性上升。
  • 技术的发展都是为了解决某些问题
  • HTTP/1的问题是短链接每次都需要三握四挥、不安全(HTTPs)、无状态(Cookie)、服务端不能主动发送。
  • HTTP/2默认了长连接(Keep-Alive)、引入TLS/SSL、Cookie、服务端主动发送、头部压缩、多路复用。但是还有队头阻塞的问题(因为虽然进行了长连接,但是还有一种情况会出现队头阻塞,那就是 丢失重传)。
  • HTTP/3使用UDP解决了丢失重传导致的队头阻塞,并且使用了TLS/SSL1.3减少了建立HTTPs连接的时间到1.5-2个RTT(往返时间),还引入了二进制编码,其他细节忘记了。

14. 你知道怎么建立0RTT的连接吗

  • ????????????????我直接一堆问号,有吗有吗?(然后他也慌了。。。说是1RTT。。。然后最后又说确实就是0RTT。。。行吧,不懂,技术牛逼。)
  • 后来查了看看,QUIC也就是HTTP/3使用的技术有一个连接恢复,是建立在连接在以前建立的基础上的。

16. 这个web服务器我就不问你了,感觉人手一个

18. 做题吧:长度为TotalSize的数据怎么分为最长大小限制为Size的小数据块,要求块数越小越好,块长度的差值越小越好。

  • 写代码我是真的服了,不让我写ACM模式,还非得说你就假设这两个变量已经定义好了。嗯……就是不让我运行debug呗。然后还说都是整数,尼玛,整数就难顶了啊,答案就变成了{size,size,size,size,.....,size-1,size-1,size-1};就很烦。
  • 最后还反问,你确定这样构造出来的数组求和为TotalSize吗(玩战术的人心都脏!离谱)确认。

19. 我出个场景题吧,现在很多大公司都有一个困境,早晚高峰的时候很多员工堵在电梯。导致很容易迟到。你想个办法吧。什么办法都行,除了加电梯。。。10部电梯-100层楼-每层楼人数相同-员工早高峰同时抵达。

  • 一个想法是每部电梯只固定的10层。如1号只去1-10,2号只去10-20,这样往返时间就短了很多了,就不用每次都停顿100次。
  • 好家伙,这还不行,行吧,那就需要辛苦一下员工了hhh
  • 这样吧,我们把电梯的层数减少5倍,意思就是电梯只能去1 5 10 15 20 25层,之后要去到具体的层数让员工自己爬楼梯去吧,反正最多只要爬三层楼梯。顺便在加上我第一个思路,还能有效进行秩序的管理。
  • 可可可,其实这个问题没有正确答案,但是就是希望面试的同学能够想到楼梯,不要把目光只放在电梯上。

20. 如果我们这场面试不存在,或者说如果我和你第一次见面,你怎么用简短的话说服我招你。当然100句话也可以,但是2句话能就解决的就不要再多了。

  • 开始胡扯(首先我最大的特点是善于与人沟通,与他人合作能力强,并且学习能力很强,面对他人指出的错误能够很好的接受并改正,当然技术业务能力也靠谱。最后一点,hhhhhh我自愿加班。)

三面凉经(1h。反问环节纯作死勿模仿)

2. 介绍一下研究生的研究经历

9. 反问环节(喜闻乐见尬聊几分钟,满足面试官虐我的请求)

1. 这次面试是不是比较差

怎么样呢,没有好与坏的说法。最后的结果也是一个根据匹配度来进行选择的。

2. 大概多久会有通知

3-5天会有hr打电话通知

3. 被反问为什么不走校招(我选的是那个有转正机会的岗位)

4. 被反问有意来字节工作吗

5. 被反问投了什么公司的实习

6. 被反问你了解我们这个部门的工作吗

7. 被反问如果给你offer你还是会选择吗

我要回帖

更多关于 多动商数值多少算标准 的文章