C++基础知识点整理(二)

billy
billy
billy
8
文章
0
评论
2021-02-0313:02:00 评论 785 4454字
摘要

本篇是C++基础知识整理第二篇,和 C++基础知识点整理(一)一起,将C++当中的重点难点、面试常见考点问题比较全面的总结。

C++基础知识点整理(一)点击查看→http://www.itongji.cn/detail?type=99993475&id=108812

后面将会开始结合几个深度学习训练框架,逐步跟大家分享和探讨构建一个全流程的深度学习框架过程中涉及到的一些知识点,比如涉及到的硬件、机器间的通信、模型训练与部署等等。

关键字Const

1) const修饰变量:存储在常量区,不允许被修改。修饰指针变量分为指向常量的指针和指针本身是常量。如果修饰的是类的成员变量那么必须在类中定义的时候初始化或者在构造函数的初始化列表中初始化。

2) const修饰函数:如果修饰返回值表示返回值不可被修改。如果修饰成员函数表示不可在函数中修改任何成员变量。

3) const修饰对象:表示这个对象是一个常量,在初始化的时候要对所有成员变量都初始化。共有变量只能读,并且只能调用const成员函数。

关键字static

1)静态局部变量:作用域只在函数内部,但是在全局静态存储区分配内存,也就是说生存周期随着程序运行结束而结束。会在第一次被执行的时候初始化,以后的函数调用不再初始化。

2)全局静态变量/static修饰的函数:隐藏。该变量/函数不能被其他文件引用。

3)静态数据成员:只会被初始化一次,与实例无关,并且只能在类外初始化。存储在全局静态区。

4)静态成员函数:用于修饰类的成员函数。只能访问静态数据成员或静态成员函数。

volatile关键字

volatile 关键字告诉编译器该关键字修饰的变量是随时可能发生变化的,每次使用它的时候必须从内存中取出它的值,因而编译器生成的汇编代码会重新从它的地址处读取数据放在左值中。这样看来,如果该变量是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile 可以保证对特殊地址的稳定访问。

重载overload,覆盖(重写)override,隐藏(重定义)overwrite三者之间的区别

1)overload,将语义相近的几个函数用同一个名字表示,但是参数列表(参数的类型,个数,顺序不同)不同,这就是函数重载,返回值类型可以不同

特征:相同范围(同一个类中)、函数名字相同、参数不同、virtual关键字可有可无

2)override,派生类覆盖基类的虚函数,实现接口的重用,返回值类型必须相同

特征:不同范围(基类和派生类)、函数名字相同、参数相同、基类中必须有virtual关键字(必须是虚函数)

3)overwrite,派生类屏蔽了其同名的基类函数,返回值类型可以不同

特征:不同范围(基类和派生类)、函数名字相同、参数不同或者参数相同且无virtual关键字

class 和 struct 的区别

struct 的成员默认是公有的,而类的成员默认是私有的

定义和声明的区别

声明是告诉编译器变量的类型和名字,不会为变量分配空间

定义需要分配空间,同一个变量可以被声明多次,但是只能被定义一次

深拷贝和浅拷贝的区别

深拷贝和浅拷贝可以简单的理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,如果资源重新分配了就是深拷贝;反之没有重新分配资源,就是浅拷贝。

赋值运算符和拷贝构造函数的区别?

相同点:都是将一个对象copy 到另一个中去。

不同点:拷贝构造函数涉及到要新建立一个对象

typdef和define区别

  • #define是预处理命令,在预处理是执行简单的替换,不做正确性的检查。
  • typedef是在编译时处理的,它是在自己的作用域内给已经存在的类型一个别名。
  • typedef (int*)pINT; pINT a,b 的效果同int *a; int *b,表示定义了两个整型指针变量。
  • #define pINT2 int*;pINT2 a,b 的效果同int *a, b,表示定义了一个整型指针变量a和整型变量b。

空指针和悬垂指针的区别

空指针是指被赋值为NULL的指针;delete指向动态分配对象的指针将会产生悬垂指针。

(1) 空指针可以被多次delete,而悬垂指针再次删除时程序会变得非常不稳定;

(2) 使用空指针和悬垂指针都是非法的,而且有可能造成程序崩溃,如果指针是空指针,尽管同样是崩溃,但和悬垂指针相比是一种可预料的崩溃

左值和右值的区别

  • 左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;
  • 右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。
  • 一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。

移动语义(move)和完美转发(forward)

  • Move作用是无论给move传递了一个左值引用还是一个右值引用,最终返回的,都是一个右值引用。
  • forward指的是函数模板在向其他函数传递自身形参的时候,如果相应的实参是左值,那么它就应该被转发为左值,如果相应的实参是右值,那么它就应该被转发为右值。为了保留在其他函数针对转发而来的参数的左右值属性进行不同处理(比如参数为左值时实施拷贝语义;参数为右值时实施移动语义)的可能性。

数组和指针的区别

  • 数组要么在全局数据区被创建,要么在栈上被创建;指针可以随时指向任意类型的内存块。
  • 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof指针得到的是一个指针变量的字节数,而不是p所指的内存容量。
  • C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

进程和线程的区别

(1)进程是程序的一次执行,线程是进程中的执行单元。

(2)进程间是独立的,这表现在内存空间、上下文环境上,线程运行在进程中。

(3)一般来讲,进程无法突破进程边界存取其他进程内的存储空间;而同一进程所产生的线程共享内存空间。

(4)同一进程中的两段代码不能同时执行,除非引入多线程。

(5) 线程产生的速度快,执行开销小,线程间通信快、切换快,使用公共变量或者资源时需要同步机制。

内存中的堆与栈的区别

  • 堆空间的内存是动态分配的,一般存放对象,并且需要手动释放内存。
  • 栈空间的内存是由系统自动分配,一般存放局部变量,比如对象的地址等值,不需要程序员对这块内存进行管理。
  • 栈是运行时的单位,而堆是存储的单位。
  • 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
  • 堆解决的是数据存储的问题,即数据怎么放、放在哪儿。

内联函数和宏定义的区别

  • 宏定义在预处理的时候进行简单的字符串替换,而内联函数在编译时在每个调用内联函数的地方将函数展开,这样不用使内联函数占用栈空间,提高效率。
  • 宏定义没有类型检查,但是内联函数还是具有函数的性质,有参数以及返回值。

sizeof与strlen的区别

  • sizeof的返回值类型为size_t(unsigned int);
  • sizeof是运算符,而strlen是函数;
  • sizeof可以用类型做参数,其参数可以是任意类型的或者是变量、函数,而strlen只能用char*做参数,且必须是以’’结尾;
  • 数组作sizeof的参数时不会退化为指针,而传递给strlen就退化为指针;
  • sizeof是编译时的常量,而strlen要到运行时才会计算出来,且是字符串中字符的个数而不是内存大小;

线程/进程间通信

  • 线程间资源是共享的,讲安全:共享变量,信号量,wait/notify机制,锁,原子操作
  • 进程间资源是独立的,讲通讯:信号, 信号量, 管道,共享内存, 消息队列,socket

内存池

内存池是一种内存分配方式。通常我们习惯直接使用new、malloc申请内存,这样做的缺点在于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。内存池则是在真正使用内存之前,预先申请分配一定数量、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。

sizeof小结

sizeof计算的是在栈中分配的内存大小。

(1) sizeof不计算static变量占得内存;

(2) 32位系统的指针的大小是4个字节,64位系统的指针是8字节,而不用管指针类型;

(3) char型占1个字节,int占4个字节,short int占2个字节,long int占4个字节,float占4字节,double占8字节,string占4字节,一个空类占1个字节,单一继承的空类占1个字节,虚继承涉及到虚指针所以占4个字节

(4) 数组的长度:若指定了数组长度,则不看元素个数,总字节数=数组长度*sizeof(元素类型);若没有指定长度,则按实际元素个数类确定;若是字符数组,则应考虑末尾的空字符。

(5) 结构体对象的长度

在默认情况下,为方便对结构体内元素的访问和管理,当结构体内元素长度小于处理器位数的时候,便以结构体内最长的数据元素的长度为对齐单位,即为其整数倍。若结构体内元素长度大于处理器位数则以处理器位数为单位对齐。

(6) unsigned影响的只是最高位的意义,数据长度不会改变,所以sizeof(unsigned int)=4

(7) 自定义类型的sizeof取值等于它的类型原型取sizeof

(8) 对函数使用sizeof,在编译阶段会被函数的返回值的类型代替

(9) sizeof后如果是类型名则必须加括号,如果是变量名可以不加括号,这是因为sizeof是运算符

(10) 当使用结构类型或者变量时,sizeof返回实际的大小。当使用静态数组时返回数组的全部大小,sizeof不能返回动态数组或者外部数组的尺寸

STL容器与算法

  • STL 包括两部分内容:容器和算法。(重要的还有融合这二者的迭代器)
  • 容器,即存放数据的地方,比如 array 等,在 STL 中,容器分为两类:序列式容器和关联式容器。
  • 序列式容器,其中的元素不一定有序,但都可以被排序,如:vector、list、deque、stack、queue、heap、priority_queue、slist;
  • 关联式容器,内部结构基本上是一颗平衡二叉树。所谓关联,指每个元素都有一个键值和一个实值,元素按照一定的规则存放。如:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap。
  • 算法由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。
  • 迭代器提供了一种方法,使它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构,它将容器和算法分开,好让这二者独立设计。

End.

爱数据网专栏作者:billlee

专栏名称:推荐系统工业实践

专栏简介:介绍工业界最前沿的推荐系统架构、模型及相关实战指南

个人公众号:比尔的新世界

本文为挖数网专栏作者原创文章,未经允许禁止转载,需要转载请微信联系授权(微信号:lovedata0520)

  • 我的微信公众号
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: