内联函数(inline)
inline
内联函数的目的是为了减少函数调用的时间, 它是把内联函数的函数体在编译器预处理的时候替换到函数调用处,这样代码运行到这里的时候不需要花时间去调用函数。但内联函数的缺点也在于此, 它会增加执行文件的大小。
需要注意:
1.头文件中不仅要包含inline函数的声明, 还要包含inline函数的定义
编译器需要把inline函数的函数体替换到函数调用处,所以编译器必须知道inline函数的函数体是啥, 定义和声明写在同一头文件中 便于编译器查找替换。
2.可以在同一个项目的不同源文件内定义函数名相同, 实现相同的inline函数
同一个inline函数可以多处声明和定义, 但是必须要完全相同
3.定义在class内的成员函数默认是inline函数(至于编译器是否处理为inline就不一定了); 类外定义的成员函数就不是内联的了
virtual函数
1.类的构造函数不能是虚函数
类的构造函数是为了构造对象的, 所以在调用构造函数时必然知道是哪个对象调用了构造函数。
2.类的静态成员函数不能是虚函数
类的静态成员函数与对象无关, 是该类共用的, 没有this指针
使用inline关键字的函数可能会被编译器忽略而不在调用处展开, 如虚函数
1.如果定义的inline函数过大,为了放置生成的obj文件太大,编译器会忽略这里的inline声明
2.inline是在编译期将函数体替换到调用处的; 而虚函数的调用是运行期才能决定的,编译期并不能确定调用的是哪个类的虚函数,所以编译器会忽略inline关键字。(虚函数可以声明为inline,但是毫无内联的意义)
1 |
|
语法上面来讲,在基类和子类的who函数前面加上inline关键字,是可以正常编译,运行的。(编译时编译器会忽略之)
基类采用虚函数是为了防止内存泄漏。
如果派生类中申请了内存空间, 并在其析构函数中对这些内存空间进行释放。 假设基类中采用的是非虚析构函数, 当删除基类指针指向的派生类对象时就不会触发动态绑定,只会调用基类的析构函数。于是, 派生类申请的空间就得不到释放从而产生内存泄漏。
(派生类的析构函数只负责销毁由派生类自己分配的资源)
构造函数不能是虚函数
虚函数通过 虚函数表调用, 而vbtl在构造函数调用后才建立, 因而构造函数不可能是虚函数。
C++ 只会析构已经完成的对象
1.对象只有在其构造函数执行完毕时才算是完全构造妥当, 当构造函数中发生异常, 析构函数不会调用, 可能会造成内存泄漏。
2.如果异常从析构函数抛出, 那个析构函数便是执行不完全的, 即 它没有完成应该执行的每一件事情…
抽象类的作用
- 带有纯虚函数的类 称为抽象类, 抽象基类负责定义接口, 由其派生类覆盖该接口。
- C++ 支持两种多态:
1.编译时多态: 通过重载函数来实现;
2.运行时多态: 通过虚函数实现
- 虚函数是在基类中被声明为virtual, 并在派生类中重新定义的成员函数。
友元函数必须在类内部声明
向上类型转换与向下类型转换 + static_cast 和 dynamic_cast
static_cast (编译时类型检查)
static_cast <type-id> (expression) 该运算符把expression转换为type-id类型。用法:
- 用于基本数据类型之间的转换, 转换的安全性由人来保证, 如 把int转换为char时, 如果char没有足够的位来存放int的值,那么static_cast所做的只是简单的截断,即简单地把int的低8位复制到char的8位中,直接抛弃高位。
- 把空指针转换成目标类型的指针
- 把任何类型的表达式转换成void类型
- 用于类层次结构中父类和子类之间指针和引用的转换
对于4. 存在两种形式的转换: - 向上转换(派生类到基类)
- 向下转换(基类到派生类)
static_cast在向上转换时是安全的,向下转换时是不安全的。(之所以说static_cast在下行转换时不安全, 是因为即使转换失败,它也不返回NULL)
dynamic_cast(运行时类型检查)
主要用于类层次结构中父类和子类之间指针和引用的转换。由于具有运行时类型检查,因此可以保证下行转换的安全性(即,转换成功返回转换后的正确类型指针, 如果转换失败, 就返回NULL)。之所以说static_cast在下行转换时不安全, 是因为即使转换失败,它也不返回NULL。
转型失败时
- 转型对象为引用时, 抛出异常bad_cast
- 转型对象为指针时, 返回NULL
使用dynamic_cast时, 该类型必须含有虚函数
因为dynamic_cast使用了存储在虚函数表中的信息来判断实际的类型。
对于上行转换, static_cast 与 dynamic_cast 一样
下面通过程序说明:
1 | class Base { |
由于子类继承于父类, 父类指针可以指向父类对象, 也可以指向子类对象; 如果pb 指向的是子类对象, 则dynamic_cast 和 static_cast 都可以转换成功
1 | Base* pb = new Derived(); |
如果pb指向的是父类对象
1 | Base* pb = new Base(); |
转换就是把对象从一种类型转换到另一种类型, 这时,如果用pd1去访问子类中有而父类中没有的成员,就会出现访问越界的错误,导致程序溃。
static_cast在编译时不会报错, 可以返回一个子类对象的指针,但这是不安全的;
dynamic_cast 具有运行时类型检查的功能, 由于上述转换不合理, 所以它返回NULL。
总结
C++中层次类型转换中无非两种:上行转换和下行转换
对于上行转换,static_cast和dynamic_cast效果一样,都安全;
对于下行转换:你必须确定要转换的数据确实是目标类型的数据,即需要注意要转换的父类类型指针是否真的指向子类对象,如果是,static_cast和dynamic_cast都能成功;如果不是static_cast能返回,但是不安全,可能会出现访问越界错误,而dynamic_cast在运行时类型检查过程中,判定该过程不能转换,返回NULL。
内存对齐
如何判断两个浮点数是否相等
lambda
const_cast 去除 指向常数对象的指针或引用的常量性, 其去除常量性的对象必须为指针或引用
隐式转换
是指不需要用户干预, 编译器私下进行的类型转换行为。
- 基本数据类型: 隐式转换发生在从小 ——> 大的转换中, 比如从char转换为int。(保证精度不丢失)
- 自定义对象 : 子类对象可以隐式地转换为父类对象。
在构造函数声明的时候加上explicit关键字, 能够禁止隐式转换。
如果构造函数只接受一个参数,则它实际上定义了转换为此类类型的隐式转换机制。可以通过将构造函数声明为explicit加以制止隐式类型转换。
关键字explicit只对一个实参的构造函数有效,因为需要多个实参的构造函数不能用于执行隐式转换, 所以无需将其指定为explicit.
多重继承 虚继承
模板
在成员函数中调用delete this
类对象的内存空间中, 只有数据成员和虚函数表指针,并不包含代码内容, 类的成员函数单独放在代码段中。
在调用成员函数时,隐含传递一个this指针, 让成员函数知道当前是哪个对象在调用它。 当调用delete this时, 类对象的内存空间被释放。
在这之后进行的其他任何函数调用, 只要不涉及this指针的内容, 都能正常运行, 一旦涉及到this指针, 如操作数据成员、调用虚函数等, 就会出现不可预期的问题。
因为delete this 释放了类对象的内存空间, 但是内存空间却并不是马上被回收到系统中,此时这段内存是可以访问的,但其中的值确是不确定的。当访问数据成员,可能得到的是一长串随机数; 访问虚函数表,指针无效。
在类的析构函数中调用delete this
会导致堆栈溢出。
因为delete本质是 为将被释放的内存调用一个或多个析构函数, 然后释放内存。
显然,delete this 会去调用本对象用本对象的析构函数, 而析构函数中又调用 delete this, 形成无限递归, 造成堆栈溢出, 系统崩溃。
智能指针
union 与 大端小端
大端模式: 是指数据的高字节保存在内存的低地址中, 而数据的低字节保存在内存的高地址端。
小端模式: 是指数据的高字节保存在内存的高地址中, 而数据的低字节保存在内存的低地址端。
理解: 看低字节保存在内存的哪一端
- 数据的低字节保存在内存的高地址端 : 大端模式
- 数据的低字节保存至内存的低地址端 : 小端模式
检测方法
- 直接读取存放在内存中的十六进制数值, 取第位进行值判断 (存疑)
1 | int a = 0x12345678; |
- 用union来判断
union所有数据成员是共享一段内存的,后写入的数据成员将覆盖之前的数据成员, 数据成员都有相同的书地址。
union的大小为最大数据成员的大小。
union的数据成员共用内存,并且首地址都是低地址首字节。
int i = 1 时, 大端存储1放在最高位, 小端存储1放在最低位; 当读取char ch 时, 是最低地址首字节。
1 | union w { |
strcpy和memcpy的区别
- 复制的内容不同。 strcpy只能复制字符串, 而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
- 复制的方法不同。 strcpy不需要指定长度,它遇到被复制字符的串结束符 “\0”时才结束, 所以容易溢出。memcpy则是ge根据其第三个参数决定复制的长度。
- 用途不同。通常在复制字符串时用strcpy, 而需要复制其他类型数据时用memcpy
assert()断言 只在DEBUG下生效
assert是宏, 不是函数。
原型 void assert (int expression);
assert常用于在函数开始处检验传入参数的合法性。 先计算表达式expression然后判断:
- 表达式为真: 继续运行后面的程序
- 表达式为假: 先向stderr打印错误信息, 然后通过调用abort来终止程序运行
strcat strcpy strncpy strcmp memset memcpy的内部实现
字符串拷贝strcpy
1 |
|
strlen
des 和 src 所指内存区域不可以重叠且 des 必须有足够的空间来容纳 src 的字符串。
1 | int strlen (char* str) { |
strcat
des 和 src 所指内存区域不可以重叠且 des 必须有足够的空间来容纳 src 的字符串。
1 | char* strcat (char* dest, const char* src) { |
strcmp
- 1.字符串1 小于 字符串2, strcmp函数返回一个负值
- 2.字符串1 等于 字符串2, strcmp函数返回 0
- 3.字符串1 大于 字符串2, strcmp函数返回一个正值
两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇\0为止。
1 | int strcmp(const char* dest, cosnt char* src) { |
空类会默认添加的东西
1 | - Empty(); //默认构造函数 |
C++标准库
分为两部分
- 标准函数库 : 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自C语言。
- 面向对象类库 :这个库是类及其相关函数的集合。
C++标准库包含了所有的C标准库,为了支持类型安全做了一定的添加和修改。标准函数库
- I/O
- 字符串和字符处理
- 数学
- 时间、日期和本地化
- 动态分配
- 其他
- 宽字符函数
面向对象类库
- 标准的C++ I/O类
- string 类
- STL容器类
- STL算法
- STL函数对象
- STL迭代器
- STL分配器
- 本地化库
- 异常处理类
- 杂项支持库
const char* char* string 三者的相互转化
new operator
- new operator 可以分解为 operator new 和 placement new两个动作.
- operator new 负责申请内存 (类比C的malloc)
- placement new 用于在给定的内存中初始化对象
- delete operator 完成 析构对象和 释放内存的操作
operator delete 只用于释放内存.(类比C的free)
为什么拷贝构造函数必须传引用不能传值
拷贝构造函数用来初始化一个非引用类类型对象, 如果用传值的方式传参, 那么构造实参需要调用拷贝构造函数, 而拷贝构造函数需要传递实参, 所以会一直递归。
this指针首先入栈, 然后成员函数的参数从右向左入栈, 最后函数返回地址入栈
C++阻止一个类被实例化
- 将类定义为抽象基类或者将构造函数声明为private;
- 不允许类外部创建类对象, 只能在类内部创建对象。
抽象类和接口
含有纯虚函数的类就叫抽象类, 抽象类只能被继承,不能实例化
1 | class A { |
接口是一种特殊的抽象类
- 1.类中没有定义任何成员变量
- 2.类中所有成员函数都是公有且都是纯虚函数
1 | class deviceOp { |
华为
1.
作者:onceorange
链接:https://www.nowcoder.com/discuss/249864?type=post&order=time&pos=&page=1
来源:牛客网
岗位是上海无线部门的软开。
一面:问了下笔试的一个题的思路。然后问了一些项目的定西,然后写代码,用笔写在纸上,二分查找,写了个递归版的,然后又让写个循环版的。
二面:上来先写一个,求一个二叉树节点的最大值。我用先序遍历写了一个。
然后加强版,求一个二叉树第k大的节点值,我用小顶堆+先序遍历写了一个,面试官看了说了声“我去,可以啊”。
然后面试官说 再问你一个进阶的,给你一个排序二叉树,升序输入里面节点值的
我说 中序遍历不就行了吗 面试官露出满意笑容。
三面 技术主管面 面试官感觉是个大佬 气场非常强 搞得我有点紧张 不过他透露出前两面面试官都是他的人 对我评价很不错。聊了聊天就结束了。
评价:华为效率很高,我八点半到,11点半三轮面试就结束了。面试官都很有礼貌,来了和走的时候会和你握手,而且都面带笑容。
手撕代码难度不大,注意细节要写好,比如楼主就忘记引用&这个符号怎么写了,还跟面试官请教了一下。
感觉今年确实激烈一点,中间淘汰了很多人,到达终面的远没有刚来的人多。
2.
广联达
leetcode 11.
leetcode 983.
leetcode 130.