建设高端网站需要多少钱,国内永久免费crm系统网站推荐有哪些,做相亲网站的安全责任,做一个购物app要多少钱一、基础语法
1. C 和 C的区别
i. C是面向对象的的编程语言#xff0c;C是面向过程的编程语言
ii. C中的内存分配运算符是new/delete而C 中是malloc和free
iii. C中有函数重载而C 中没有
iv. C中新增了引用的概念而C 中只有值和指针
2. struct 和 class的区别
i. struc…一、基础语法
1. C 和 C的区别
i. C是面向对象的的编程语言C是面向过程的编程语言
ii. C中的内存分配运算符是new/delete而C 中是malloc和free
iii. C中有函数重载而C 中没有
iv. C中新增了引用的概念而C 中只有值和指针
2. struct 和 class的区别
i. struct 一般用于描述一个数据结构集合而class是对一个对象数据的封装
ii. class 默认访问修饰符是私有的而struct 是公有的
iii. 在继承方面 class默认是私有继承而struct 默认是公有继承
3. define宏定义 和 const 的区别
i. 首先 宏定义是在编译的预处理阶段起作用而const是在编译、运行时起作用
ii. 其次 宏定义它只做替换并不会进行检查很容易报错而const有数据类型编译器会对它进行类型检查
iii. 最后 宏定义的数据没有分配内存只是插入替换
而const定义的变量只是值不能变但是会分配内存
4. define宏定义 和 line内联函数的区别
i. 首先 宏定义在预处理阶段起作用只做简单的字符串替换它没有返回值而 内联函数在编译阶段起作用有返回值
ii. 然后 内联函数在编译时直接将函数代码嵌入到目标代码中省去了函数调用的开销从而提高性能并且可以重载
iii. 最后 编译器会对内联函数进行类型检查以及语法判断
而宏定义不会
5. 指针和引用的区别
i. 指针是一个变量里面存放的是地址而引用是变量的别名它和原来的变量实际上是同一个东西ii. 指针可以有多级而引用只能有一级
iii. 指针在初始化依然可以改变指向而引用初始化后就不能改变
iv. 对指针取地址得到的是指针原本的地址
而对引用取地址得到的是变量的地址
6. 数组和指针的区别
i. 首先 数组是存储多个相同数据类型的集合。数组名是首元素的地址而指针是变量用于存放其它变量在内存中的地址。指针名指向内存的首地址
ii. 其次 数组在内存中是连续存储的通过数组下标进行访问数组不是在静态区就是在栈上而指针的存储的存储空间不能确定iii. 最后 用sizeof计算数组得到的是整个数组的大小
而指针得到的是该指针变量的大小
7. 数组指针和指针数组的区别
数组指针它是指向数组的指针它的本质是指针只不过指向数组中的某一个元素指针数组它是存放指针的数组其本质是数组只不过其中存放的元素是指针
数组指针写做 int(*ptr) [4]
指针数组写做 int* ptr[4]8. 深拷贝和浅拷贝的区别
原因在拷贝构造的过程中导致两个对象指向同一块内存区域这样再释放内存的过程中就会导致内存资源重复释放浅拷贝因为浅拷贝只是拷贝了一个指针并没有新开辟一块内存区域所以就导致了两个对象指向了同一块地址最后会导致内存资源的重复释放深拷贝深拷贝是直接开辟了一个新的空间新对象指向这个新的空间
这样 即使原对象被析构也不会影响到新对象
9. 移动构造和拷贝构造的区别
i. 首先 在拷贝构造函数中如果涉及到指针就需要用到深拷贝而在移动构造函数中,则用的是浅拷贝
ii. 最后 拷贝构造函数的参数是一个左值引用
而移动构造函数的参数是一个右值或将亡值的引用
10. 重载重写隐藏的区别
i. 首先只有同一范围定义的同名函数才存在重载关系重载特点是函数名相同参数类型和数目不同
ii. 然后重写是指子类覆盖父类中的同名函数要求子类函数必须是虚函数且与父类的虚函数有相同的参数类型参数个数以及返回值类型
iii. 所以重写和重载的区别重载是函数之间的关系重写是子类和父类的关系重载要求参数类型和数目不同重载要求参数列表和返回值相同iv. 最后隐藏是指子类中的函数屏蔽了父类中的同名函数。隐藏发生条件如下两者函数参数相同但父类函数不是虚函数。与重写的区别在于父类是否为虚函数
两者参数不同不管父类是不是虚函数都会隐藏
11. 指针常量和常量指针的区别
这里以以Cprimer为准
指针常量写作int const *p指针本身是一个常量它的值不能修改
常量指针写作int* const p ,指针的指向是一个常量它的指向不能修改
12. 野指针和悬空指针区别
首先它们都是指向无效内存区域的指针
野指针指向的位置是不确定的
原因指针定义时没有进行初始化
解决定义时就进行初始化或置空悬空指针指向的内存区域被释放
原因指针指向的内存被释放但指针没有及时置空
解决内存释放后及时置空
13. 静态类型和动态类型的区别
静态类型对象在声明时采用的类型在编译期就已经确定动态类型
一个指针或引用目前所指对象的类型在运行期时才能确定
14. 源文本到文本可执行文件经历的过程
C从源码到指向文件有四个过程预处理、编译、汇编、链接
i. 预处理过程如下将所有的#define删除并且展开所有的宏定义处理一些预编译指令如#ifndef、#ifdef等处理#include预编译指令将被包含的文件插入到该预编译指令的位置过滤所有的注释添加行号和文件名标识#ifndef、#ifdef的作用是防止重复包含头文件#include 从标准库中寻找头文件。#include从当前目录开始寻找头文件。
ii. 编译过程分为6步词法分析将源代码的字符序列分割成一系列的记号语法分析对记号进行语法分析产生语法树语义分析判断表达式是否有意义代码优化生成汇编代码汇编代码优化
iii. 汇编这个过程主要是将汇编代码转变成机器可以执行的指令
iv. 链接将不同源文件产生的目标文件进行链接从而形成一个可以执行的程序
15. 递归和循环的区别
i. 递归时每递归一层就会在内存生成一个调用栈来保存本次递归的信息所以如果递归深度过深就会有栈溢出的问题
ii. 循环是一次正向的过程递归则需要回溯
iii. 在写法上递归需要不断调用自身的函数循环则不需要
16. i 和 i的区别
i. 首先 i是先加后赋值而i是先赋值后加
ii. 其次 前置返回一个引用后置返回一个对象。而前置不会产生临时对象后置必须产生临时对象临时对象会导致效率降低。所以i更快
17. const的四种作用
i. const修饰局部变量或全局变量初始化后不能更改
ii. const修饰函数的参数为了避免该参数被修改
iii. 用const修饰函数返回值说明函数的返回类型是const的则返回值不能被修改
iv. 用const修饰函数则不能修改其成员变量的值
18. static的作用如果不考虑类的情况
i. 第一个作用是隐藏不加static的全局变量和函数具有全局可见性可以在其他文件中使用而加了之后只能在该文件所在的编译模块中使用
ii. 然后 作用在局部变量上可以提高其生命周期。它不会出代码块而销毁而是存储在静态存储区中
iii. 最后 用static修饰的变量默认初始化为0考虑类的情况static修饰成员变量或者成员函数
它就只与类进行关联而不再属于栈上某个对象的数据所有对象都共享这一块静态存储空间
二、面向对象
1. 面向对象的三大特性
i. 封装封装是把客观事物封装成抽象类。比如把公共的数据或方法用public修饰把私有的数据或方法用private修饰
ii. 继承继承是让某个类对象获得另一个类对象的属性和方法。
iii. 多态多态是指同一事物表现出不同的能力多态性是允许将子类类型的指针赋值给父类类型
实现多态的方式有两种重写(运行时多态)和重载(编译时多态)
2. 类的默认成员函数
i. 无参的构造函数用于完成对象的初始化工作
ii. 拷贝构造函数用于复制本类对象
iii. 赋值运算符重载函数同样也是负责本类的对象
iv. 析构函数用于对象的清理
3. C 类对象的初始化顺序有多重继承情况下的顺序
i. 父类的构造函数优先初始化若父类中包含成员类对象则再初始化成员类对象最后再初始化子类对象
ii. 成员变量的初始化与声明顺序有关
iii. 析构顺序与构造顺序相反
可以简单的理解为套娃问题,子类包含父类的属性和方法那么子类一定比父类大,所以我们要先初始化小的再从外面套上大的
4. 简述一下 C 中的多态
子类重写父类的方法然后用父类引用或指针指向子类对象最后当我们调用方法时会进行动态绑定这就是多态静态多态和动态多态区别静态多态在编译期就已经确定函数的重载就属于静态多态动态多态在运行时才能确定并且还需要完成动态绑定的条件
i. 父类中必须要有虚函数然后子类重写父类虚函数
ii. 通过父类的指针或引用来调用这个虚函数
5. 虚函数和纯虚函数的异同
异
i. 首先 含有纯虚函数的类被称为抽象类而只含有虚函数的类不能被称为抽象类。
ii. 其次 虚函数可以被直接使用也可以被子类重载以后以多态的形式调用而而纯虚函数必须在子类中实现该函数才可以使用因为纯虚函数在基类有声明而没有定义
iii. 最后 它们的定义形式也不同虚函数是virtual{}而纯虚函数是virtual{} 0;
同
i. 首先 虚函数和纯虚函数可以定义在同一个类中
ii. 其次 虚函数和纯虚函数都可以在子类中被重载以多态的形式被调用。
iii. 最后 在虚函数和纯虚函数的定义中不能有static标识符因为被static修饰的函数在编译时就要进行绑定然而它们却是动态绑定的
6. 为什么析构函数一般写成虚函数
如果析构函数不被声明成虚函数则编译器执行的是静态绑定在删除父类指针时只会调用父类的析构函数而不调用子类的析构函数这样就会造成子类对象析构不完全造成内存泄漏C默认的析构函数不写成虚函数是因为虚函数需要额外的虚函数表和虚函数指针会占用额外的内存空间。所以我们对于不会被继承的类来说是不会设置为虚函数的
7. 构造、析构函数是否可以为虚函数
构造函数不可以为虚函数因为 虚函数所对应的虚函数表的地址是存储在对象的内存空间中的而此时对象都还没实例化就没有空间让虚函数表存储地址。所以这里就会冲突析构函数可以为虚函数
我们可以将需要被继承的父类的析构函数设置为虚函数。当我们父类类型的指针绑定到子类对象时能够保证释放父类指针的时候 可以同时释放掉子类的空间防止内存泄漏
三、内存管理
1. 什么是内存对齐
系统对基本类型数据在内存中存放的位置有限制它们会要求这些数据的首地址的值是某个数k通常它为4或8的倍数这就是所谓的内存对齐。比如一个结构体中有一个int类型的数据和一个char类型的数据它实际sizeof出来的大小占8个字节
2. 为什么要进行内存对齐
为了提高数据读取的效率程序分配的内存并不是连续存储的而是按首地址为k的倍数的方式存储这样就可以一次性读取数据而不需要额外的操作结构体内成员按照声明顺序存储第一个成员地址和整个结构体地址相同
未特殊说明时按结构体中size最大的成员对齐若有double成员按8字节对齐。
3. 栈和堆的区别
i. 首先 栈由操作系统自动分配释放一般用于存放函数的参数或局部变量堆由程序员通过new/delete关键字手动分配和释放
ii. 其次 栈使用的是一级缓存通常是被调用时处于存储空间中调用完毕立即释放而堆则存在二级缓存中速度要慢一些
iii. 最后 栈的结果是先进先出而堆是先进后出
4. 内存模型
在C中内存分成5个区。分别是栈区、堆区、静态存储区、常量存储区、以及代码区
栈区仅在定义的程序块运行时才存在。一般用于存储局部变量和函数的形参
堆区堆区的内存是通过new运算符动态分配的也必须通过detele运算符手动销毁
静态存储区内存在程序编译时就以及分配好,主要用于存放全局变量和静态变量
常量存储区这是一块比较特殊的存储区域里面存放着常量。不允许进行修改
代码区存放着程序的二进制代码
5. 内存泄漏
简单地说就是申请了一块内存空间使用完毕后没有释放掉比如
i. new/malloc申请内存后没有用delete/free释放
ii. 子类继承父类时父类析构函数不是虚函数
6. 避免内存泄漏
首先 可以用计数法当我们使用new或者malloc的时候计数就1而使用detele或free时计数就-1。
最后打印这个计数看看最后结果是否为0然后 一定要将父类的析构函数声明为虚函数最后要保证new/delete malloc/free成对出现
解决方法使用智能指针
7. new/delete 与 malloc/free的异同
相同点 都可以用于内存的动态申请和释放
不同点
i. 首先 new/delete 是C中的运算符。而malloc/free是C/C中的标准库函数
ii. 其次 new 在分配内存时会自动计算空间大小。而malloc需要手动计算
iii. 最后 new/delete 除了分配内存和回收内存以外还具有调用构造函数和析构函数的功能。而malloc/free则只有会分配内存和回收内存
8. new和delete是如何实现的
new实现过程
i. 先通过operator new()函数它内部会调用malloc在堆中分配一块内存
ii. 然后 将void类型的指针转换为类类型的指针
iii. 最后再通过指针调用构造函数用于初始化对象
delete实现过程
i. 首先 调用析构函数删除内存中指针所指的数据
ii. 然后 通过operator delete()函数,它内部调用free去删除对象本身
四、STL
1. STL中迭代器的作用有指针为何还要迭代器
i. 迭代器的作用用于指向顺序容器和关联容器中的元素通过迭代器可以读取它指向的元素通过非const迭代器还可以修改其指向的元素
ii. 迭代器与指针的区别
迭代器是类模板它只是表现的像指针。它模拟了指针的一些功能重载了指针的一些操作符
2. vector 和 list的对比
vector一维数组
特点元素在内存连续存放支持动态扩容在堆中分配内存元素连续存放有保留内存如果减少大小后内存也不会释放。
优点和数组类似开辟一段连续的空间并且支持随机访问所以它的查找效率高其时间复杂度O(1)。
缺点由于开辟一段连续的空间所以插入删除会需要对数据进行移动比较麻烦时间复杂度On另外当空间不足时还需要进行扩容。
list双向链表
特点元素在堆中存放每个元素都是存放在一块内存中它的内存空间可以是不连续的通过指针来进行数据的访问。
优点底层实现是循环双链表当对大量数据进行插入删除时其时间复杂度O(1)。
缺点底层没有连续的空间只能通过指针来访问所以查找数据需要遍历其时间复杂度On没有提供[]操作符的重载。
3. map 和 unordered_map 的区别以及底层实现
map内部实现了一个红黑树红黑树有自动排序的功能因此map内部所有元素都是有序的。红黑树的每一个节点都代表着map的一个元素。因此对于map进行的查找、删除、添加等一系列的操作都相当于是对红黑树进行的操作。
map中的元素是按照二叉排序树的方式存储的特点就是左子树上所有节点的键值都小于根节点的键值右子树所有节点的键值都大于根节点的键值。使用中序遍历可将键值按照从小到大遍历出来
unordered_map内部实现了一个哈希表通过把关键值映射到Hash表中一个位置来访问记录查找时间复杂度可达O(1)。因此它是无序的
4. 请说说 STL 中常见的容器并介绍一下实现原理
容器是可以用于存放各种类型数据的数据结构。可以分为顺序容器、关联式容器、容器适配器
三种类型顺序容器容器中的元素没有进行排序元素插入位置与元素的值无关。包含vector、list、deque
i. vector 的本质是动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能。
ii. list 的本质是双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。但不支持随机存取。
iii. deque 的本质是双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成仅次于vector。在两端增删元素具有较佳的性能大部分情况下是常数时间
关联式容器元素是排序的插入任何元素都按相应的排序规则来确定其位置在查找时具有非常好的性能通常以平衡二叉树的方式实现。包含set、multiset、map、multimap(都是基于红黑树实现)
i. map 的底层容器是红黑树。map 的所有元素都是成对的它的第一个元素被当成键第二个元素被当成值。所有的元素都会根据元素的键值自动排序且不允许键值重复
ii.set 的底层容器也是红黑树。但set中所有的元素只有键没有值。不允许键重复其中的元素会被自动排序且不能通过迭代器来改变set的值因为set的值就是键set的迭代器是const的所以map和set的区别在于map的值不作为键键和值是分开的容器适配器
封装了一些基本的容器使之具备了新的函数功能比如把deque封装一下变为一个具有stack功能的数据结构
5. vector扩容机制以及1.5倍扩容因子的优点
vector有保留内存当减少vector的大小后内存并不会释放只有新增大小大于当前大小时才会开辟新的内存空间
扩容机制首先 开辟1.5倍的内存空间然后 将旧数据拷贝到新的内存再 释放旧内存最后 指向新内存
五、C 11特性
1. 简述一下移动构造函数
移动构造函数实现的是对象值真实的转移。比如从A移动到B这说明将分配给A的内存转移给了B。而不是新开一块内存给B
2. 智能指针是否线程安全
智能指针包括一个实际数据指针和一个引用计数指针这两个操作不是一个指令可以完成的因此多线程环境下是会有问题的同一个shared_ptr被多个线程读是线程安全的
同一个shared_ptr被多个线程写不是线程安全的
3. 左值和右值的概念左值
左值指既能够出现在等号左边也能出现在等号右边的变量它是可寻址的变量有持久性
左值引用引用一个对象(平时指的引用一般是左值引用)
右值右值则是只能出现在等号右边的变量一般是不可寻址的常量或在表达式求值过程中创建的无名临时对象短暂性的
右值引用C11中右值引用可以实现“移动语义”通过 获得右值引用
4. 智能指针的作用、原理、以及常用的智能指针
作用
让我们更方便的管理堆内存若使用普通指针则容易造成堆内存忘记释放、二次释放程序发生异常时内存泄露等问题等。而使用智能指针能更好的管理堆内存
原理
智能指针是一个类用来存储指向动态分配对象的指针负责自动释放动态分配的对象防止堆内存泄漏。将动态分配的资源交给一个类对象去管理当类对象声明周期结束时自动调用析构函数释放资源
常用的智能指针
i. shared_ptr采用引用计数器的方法允许多个智能指针指向同一个对象每当多一个指针指向该对象时指向该对象的所有智能指针内部的引用计数加1每当减少一个智能指针指向对象时引用计数会减1当计数为0的时候会自动的释放动态分配的资源。
ii. unique_ptr采用的是独享所有权语义一个非空的unique_ptr总是拥有它所指向的资源转移一个 unique_ptr将会把所有权全部从源指针转移给目标指针源指针被置空所以unique_ptr不支持普通的拷贝和赋值操作不能用在STL标准容器中如果你拷贝一个unique_ptr那么拷贝结束后这两个unique_ptr都会指向相同的资源造成 在结束时对同一内存指针多次释放而导致程序崩溃。
iii. weak_ptr首先 引用计数有一个问题就是互相引用形成环环形引用这样两个指针指向的内存都无法释放。所以需要使用weak_ptr打破环形引用。weak_ptr是一个弱引用它是为了配合shared_ptr而引入 的一种智能指针它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期也就是说它只引用不计数。如果一块内存被shared_ptr和weak_ptr同时引用当所有shared_ptr析构了之后不管还有没有weak_ptr引用该内存内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的在使用之前使用函数lock()检查weak_ptr是否为空指针。
iv. auto_ptr(C 11弃用)主要是为了解决“有异常抛出时发生内存泄漏”的问题 。因为发生异常而无法正常释放内存但auto_ptr有拷贝语义拷贝后源对象变得无效这可能引发内存崩溃
而unique_ptr则无拷贝语义 但提供了移动语义避免这样的错误再次发生
5、C 智能指针实现引用计数的原理
i. 智能指针将一个计数器与类指向的对象相关联引用计数器跟踪共有多少个类对象共享同一指针
ii. 每次创建类的新对象时初始化指针并将引用计数置为1
iii. 当对象作为另一对象的副本而创建时拷贝构造函数拷贝指针并增加与之相应的引用计数
iv. 对一个对象进行赋值时赋值操作符减少左操作数所指对象的引用计数如果引用计数为减至0则 删除对象并增加右操作数所指对象的引用计数
v. 调用析构函数时构造函数减少引用计数如果引用计数减至0则删除基础对象
6、C11 中四种类型转换
C中四种类型转换分别为const_cast、static_cast、dynamic_cast、reinterpret_cast
const_cast将const变量转为非const
static_cast最常用可以用于各种隐式转换比如非const转const。还可以用于类向上转换但向下转换能成功但是不安全
dynamic_cast只能用于含有虚函数的类转换用于类向上和向下转换reinterpret_cast可以做任何类型的转换但不保证转换结果是否正确