程序员面试宝典压缩

第5章:

A&&B,其中A,B是布尔类型表达式,这样的话从左到右机制,A如果是错的就没有必要执行B了。

‘A’==    a //这种方法比较好,原因是可以防止a=‘A’的赋值操作。

1.类型转换:

float a=1.0f;  //1.0f显示表示在常量区占用float类型,否则1.0占用double类型

(int &)a;    // 这里是将a当作int类型来读取

(int *) &a ;//这里是强制类型指针转换

指针截断问题:char *b = (char *)&((int)a);  //这里b就会指向int型最低的那个字节的地址。这里面涉及到小端,大端存储问题

类型转换原则:1.转为当前有关的中最宽的数据类型,记住,int遇到unsigned int 就会转为unsigned int。

                        2.所有小于整形的算术表达在计算之前均会转为整形(机制决定的吧)。

2.运算符问题

(对当前寻址->对当前数操作->二目操作)

这里面比较重要的位运算。

3.C和C++的关系,

仅Cpp支持重载,两者编译后在库中的名字不相同,extern “C”解决名字匹配问题。

switch相关的细节。

第6章:

1.宏定义,

简单的替换而已,不建议去用

#define AAA (60)UL       //其中UL表示无符号长整型

嵌入操作(标准C的一部分),用法:inline void ha(){    //具体定义}        //这里面必须得用于定义的函数,声明的不行

内联函数相当于直接内嵌代码,会产生多个代码副本,利在于不用调用函数,较为迅速,但是弊在于增加总代码量。编译器也是有选择的选择是否使用内联函数。

2.const

const int* a=&b   //*a不能改,a可以改

int const *a=&b   //同上

int* const a=&b   //a不能改,*a可以改

const int* const a=&b    //a和*a均不能改

总结:const位于*左边表示指向的数据不可更改,位于*右边表示指针不可更改

2.1带有const的函数

const int app(){    };   //返回值为const

类成员函数:int app() const;   //表示函数不能修改类中成员,并且app只能调用与其相同的const成员函数。

const buff=1;//这里必须赋值,不然报错

mutable int h_ppy;   //使用mutable修饰的类成员可以可以在const函数中修改。

3.sizeof

char q[]="dasa";  //这里q指的是数组,隐含‘\0’字符。

char *q="dada";  //这里q是指针。

结构体内存对齐机制:对齐于最长的数据元素(最长对齐为处理器的位数)。对齐就是为了让CPU减少寻址次数更快。

例子(32位机器):均为short 以2对齐;有short 有int 以4对齐;有short 有double 仍以4对齐。

对齐可以用pack调整,具体操作没弄过。

见57面列子

unsigned int strlen(char *a);   //字符串长度,实质为for循环,以‘\0’结束。

注意!!sizeof(strlen(a));//这里直接别按照返回值编译为 sizeof(int),也就是说,函数并没有使用

类占用空间:空类空间为1,单一继承,多重继承的均为1,虚继承涉及虚表(虚指针,指针大小恒为4;

第七章(相当重要,仍有一些不理解,需要回看一波):

1.指针和引用

(int &a=b,意思就是a是b的一个别名,必须得初始化)

注意:指向字符串只读空间的指针只能读不可写,函数中的指针传递需要考虑函数的栈帧机制。

已经试过:灵活使用引用能够减少副本的产生,函数返回值为引用只能是传入参数为引用的情况下(不然返回的值没有意义,会被清除),传入的参数可以考虑多用引用。

2,函数指针

int (*hanshu)(int,int)=HS; //其中HS是同参数的实体函数。

int (*(*hanshu)(int))(int)   ;//要命的玩意。返回一个函数指针的函数指针

花里胡哨的一堆给整明白

3.指针数组和数组指针

int a[10];

int (*a)[10]=&a;  //务必制定数组的大小,这里&a与a输出值一样,不同的是&a以数组为单位,可以sizeof出其数组大小。

int (*a[10])(int,int) ; //这里是存储函数指针的一个数组,其中所有函数指针类型都是一样。

&a+1  ;//这里是以数组作为单位的,所以地址直接跳到数组后的那个区域。

4.迷途指针

就是被delete的指针,因为指针所指此时已经无效,所以强行改变会出大问题。delete之后将其设置为NULL最好。

malloc/free和new/delete的不同:前者为c中函数,不在编译器范围之内,而且对象类型什么的千遍万化,总之不如new/delete作为运算符很多事情编译完成的要妥当。

5.指针和句柄:

句柄是操作系统相当于是逻辑地址,32位,实际的对象在操作系统的作用下可能会发生位置的变化(位置非静态性),但是操作系统中会存在一个映射表,对应着句柄到相应的对象的物理地址。这样就具有可选性。

指针就是静态的指向过程。还有智能指针没有看

6.this指针

不占用类的空间,编译器角度来看相当于是作为参数传入每一个成员函数中。

在成员函数执行前构造,在结束之后清楚。

存放的位置有可能是堆,栈,或者是寄存器(vs编译的是,目的是加快速度)

第九章 STL模板与容器(仍需强化理解)

1.STL是跨平台的?这个怎么理解:

跨平台指的意思就是相同的代码可以跨操作系统使用,这得益于不同操作系统下编译器按照具体的体系结构进行的不同的底层适配。

类的深浅复制问题:类默认的复制构造函数仅仅只是对其参数的简单复制(值传递),如果涉及指针相关的一些重要逻辑就不行。

深复制:自己构造复制构造函数,用以完善深层次的逻辑。

迭代器的正确值问题:vector <int> a;

a.erase(迭代器);  //之后返回删除部分下一个指针的迭代器。

2.泛型编程

模板类和模板函数搞明白。

template <typename T>   //使用C++自带的类型

void happy(T A){。。。}

使用的时候:happy<int> (10);

template<classname T>...    //使用类

还有一种方法就是使用函数调用函数的方法。以外函数作为类似容器,内函数作为被调用的方法。

模板和容器:使用以及注意整理一下。

 

第十章:面向对象

开闭原则:继承的功能叙述,设计好的代码通过多态,继承等追加新的代码而完成新的操作。

编译器对空类会默认产生4个成员函数:默认构造函数,析构函数(浅析构),复制构造函数(浅复制),赋值函数

空类或者结构体大小为1原因:类实例化之后必须要占有一个空间,所以至少需要1Bit类型数据用来占坑,哪怕这里面没有任何数据。

2.类和结构体

结构体在C++中和class几乎一样,唯一不同的就是struct里面默认的访问控制是public(当然可以显式地指定为private)而class中默认的是private,C++中结构体唯一目的就是兼容C中的struct。

在函数内声明其它函数的作用:只可以声明不能直接定义。定义还是得在函数外部的。这个声明的意义在于如果有个函数与本函数下方不声明是无法调用的。

3.成员变量(vs2013编译环境)

非静态变量能在构造函数中初始化赋值,也可以在类内设置初始值(const常量类型必须得这样)。静态变量成员得在类外部显式赋值,静态常量成员得立即赋值(坑爹的东西),均并且存于全局变量区域。

class haa{ int a; int b;...}

也可以用如下方式:  happy(int i,int j):a(i),b(j){。。。}   //这里需要注意,赋值顺序仅与class中声明的顺序有关系,如果改为:happy(int i,int j):b(j),a(i){。。。}  这里仍然先执行a(i),因为i先定义

静态函数仅能访问静态成员。

成员函数后面加const表示该函数不能修改类的成员。int a() const {    };

4.构造函数以及析构函数

调用派生类的构造函数时:先从最古老的祖先处调用基类的构造函数,再依次调用,再调用自己的构造函数。

析构的时候先析构自己的,再回调临近基类的。

析构函数为虚函数目的:防止基类指针指向派生类的时候产生的析构将直接调用基类的析构函数而忽略派生类的。

构造函数不能为虚的原因:构造函数需要知道准确的相关类型。

每个虚函数的对象都得维护一个v表(所以带有虚函数的空对象占用内存要大一些(vs上不带虚的空占用1Bit,带虚的空占用4Bit))

隐式转换,得事先定义一个参数的构造函数, 便可以通过这种类型参数强制类型转换了

5.复制构造函数和赋值函数

复制构造函数多用引用,以免产生不必要的副本

复制构造函数说白了就是一种特殊的构造函数。

派生类中的虚函数的声明必须与基类中的定义方式完全一致(返回基类指针的虚函数,也可以在派生类的虚函数中返回派生类的指针。)

6.多态的概念

通过虚函数实现,实质是动态绑定。

过程(猜测):每生成一个对象会生成相应的虚表,虚表直接指向自己的虚函数的代码地址,即使赋予给基类的指针,仍然搜寻的是对象本身的虚函数(因为都存在了一起。

7.友元(破坏了封装性,使得类外可以使用类内元素)

类内声明:friend float distance(T &a,T &b);

类外(直接定义):float distance(T &a,T &b){    。。。    };

8.异常

自己的异常自己吃掉。

第11章:继承与接口

1.覆盖

在派生类中可以显式指定基类定义的虚函数(会调用指向基类的虚指针(指向虚表)),但是默认会用自己的虚指针来指向自己的虚表。

c.A::GetData();  //这里派生类c显式指定基类A的虚函数。

一般继承来说指针类型决定访问的类元素(如果基类指针则访问基类的东西,如果是派生类指针是访问派生类的东西,如果找不到再去基类找,基类指针无论如何都不能访问派生类的陌生函数!!)。“自己指针用自己类型的东西”

但是一旦在基类中定义了虚函数(并且派生类中予以重新定义的话(覆盖)),则基类指针默认调用的是派生类的虚函数(因为覆盖了),但是如果显式调用基类的虚函数也是可以的(不过这样产生的潜在问题不清楚,时间会慢一些?)

原则:

虚函数的沉底原则!

1.所有函数默认调用的是当前的this,基类函数中调用基类的指针,派生类函数中调用派生类的this。

2.默认调用自己类型的函数, 虚函数就默认调用最后定义的,当然可以显式地指定基类的虚函数或者是派生类的

https://www.jianshu.com/p/d07e0ac0ba3c?from=singlemessage

2.继承方式

公有(基类的元素类型不变),保护(公有变保护,保护和私有不变),私有(全变私有)

3.虚函数继承,虚继承

虚函数表是线性表,类中有指向这些虚函数虚指针。

一个虚继承就必定会产生一个虚指针(虚继承一次就会产生一个虚指针,所以具有累加性)(不管继承的是什么类,以区分本次派生类以及以上基类,用以单独指向自己的虚函数),普通继承会和上一个合并虚指针(目前猜测是使用最近的有虚指针的那个基类的):这个从sizeof可以看出来(只有一个虚指针)。

static_cast函数不能用于强制转换虚继承的派生类,因为不安全。

就使用而言:一般派生类可以显式调用基类的虚函数,虚继承的派生类同样可以,目前没有看出来有什么使用上面的差别。

static_cast<typename T>(转化源) 与强制类型转换的区别:使用这个可以在编译的时候对输入的值进行判断,而传统的强制转换不行,所以这种方式比较安全。但是从基类强制转化为派生类这种情况,由于其动态性(通过虚表来安排),所以仍然是不安全的。

虚继承可以防止菱形继承,A为基类,B,C为A的派生类,D继承B,C,那么如果B,C普通继承将产生两个A,造成浪费和指代不明,如果B,C虚继承A则只会产生一个A,但是要在C的构造函数里面执行对A的构造(而不能使用B,C中构造函数对A的操作,因为仍然会产生指代不明的问题)

虚指针产生原则(自测):

空对象占用1字节,原因是占空间

1.一次虚继承就会产生一个虚指针,多次虚继承会累加

2.如果不存在虚继承,存在虚函数的时候也会产生虚指针,在继承的时候这个虚指针就只能有一个

4.多重继承

强制转化:

其它类型->类:定义仅有那个类型的构造函数即可实现隐形转换,使用explicit来消除这种隐式的转换情况。

类->其它类型:使用operator int(){。。;}来指定转化的通常类型。

operator重载用法:尽量用于类中作为针对于类的或者是友元的运算符重载。只能重载参数为类或者是枚举类型的(当参数均为int(可以允许一个为int,另外一个为类,总之不能违反正常的运算),等会报错,从这里可以看出不能影响运算符的基本操作)。

dynamic_cast<目标>(源)       :源必须是完整类的指针,目标可为void*或者是其他类的指针。

static_cast<目标>(源)            :不能将指向基类的指针转化为指向派生类的(虚继承情况下)。源和目标必须是指针,可以强制转化通用类型(截断式)

转化类的时候:

1.两者在指向派生类的指针可以随便转化为指向基类的指针(原理上说得通)

2.将指向基类的指针指向派生类指针的时候(1.如果基类指针本来就指向一个派生类,则两个都能够正常转化,不会出错,因为此时内存什么的都是正确的。2.如果基类指向的是一个基类,强行转化为派生类指针,那么需要多的空间从哪里来,这将会出现问题,所以dynamic_cast此时将会返回NULL,而static_cast将会继续转换(),但是会产生不安全问题。dynamic_cast其具体过程(因为其会通过虚表判断当前指向的是对象和将要转化的类型是什么关系(如果要转化的类型包含在指向对象之中就可以正常转化,不会出什么问题)))

使用dynamic_cast必须得有虚表来判断转换是否正确:如果没有虚函数,继承也是普通继承,也就不存在虚表,所以使用dynamic_cast就不能完成判断,返回NULL

有虚函数以及虚继承就会产生虚表。

原文:https://www.cnblogs.com/wft1990/p/7126812.html

对比两个指针变量,需要对其进行隐式类型转化。

不显式指定继承模式,那么默认为private继承

自己定义了构造函数之后,就缺失了默认构造函数(所以在派生类中需要显式地调用基类的构造函数。)

虚继承的意义

6.纯虚函数

virtual void draw() = 0;      //只要有这种函数改类就变成了抽象类,就只能

7.运算符重载与RTTI(dynamic_cast和typeid(用以回传类型的关键字))

RTTI存在于虚表中,dynamic会对虚表中RTTI进行判断以判断源与目的的区别。

RTTI要慎用。

虚表中类型的说明:https://www.cnblogs.com/zhehan54/p/5582136.html

运算符重载方面看4.多重继承的相关。

重载ostream& operator<<(ostream& out,classname &A);  //多用引用,要是完全利用classname中的东西,就得在类中定义,或者是使用友元。

使用:out<<A<<A<<A<<endl;   //因为返回的是ostream的引用,所以可以连续使用<<

常用的数据结构知识:

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值