计算机基础综合常见理论问题

C++

strcpy和memcpy的区别

​ 1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
​ 2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符”\0”才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
​ 3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

重载、重写、隐藏的区别

参考 重载、重写和隐藏三者的区别 - evenleo - 博客园 (cnblogs.com)

image-20210910140758340

指针和引用的区别

指针 引用
存的是一个地址 是原变量的别名
可以有多级 只有一级
可以为空 不能为NULL且在定义时必须初始化
在初始化后可以改变指向 在初始化之后不可再改变
sizeof指针得到的是本指针的大小 sizeof引用得到的是引用所指向变量的大小
声明和 定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量 在声明时必须初始化为另一变量

new和malloc的区别

new malloc
运算符,不重载 标准库函数,支持覆盖
除了分配,还会调用构造函数和析构函数 仅仅分配内存空间
返回的是具体类型指针 void类型指针
自动计算要分配的空间大小 需要手工计算

inline函数和宏定义的区别

image-20210820163118618

多态

C++ 多态分类及实现:

  1. 重载多态(Ad-hoc Polymorphism,编译期):函数重载、运算符重载
  2. 子类型多态(Subtype Polymorphism,运行期):虚函数
  3. 参数多态性(Parametric Polymorphism,编译期):类模板、函数模板
  4. 强制多态(Coercion Polymorphism,编译期/运行期):基本类型转换、自定义类型转换

静态多态(编译期/早绑定)

函数重载

动态多态(运行期期/晚绑定)

  • 虚函数:用 virtual 修饰成员函数,使其成为虚函数
  • 动态绑定:当使用基类的引用或指针调用一个虚函数时将发生动态绑定

虚函数实现多态的原理

(1)编译器在发现基类中有虚函数时,会自动为每个含有虚函数的类生成一份虚表,该表是一个一维 数组,虚表里保存了虚函数的入口地址

(2)编译器会在每个对象的前四个字节中保存一个虚表指针,即vptr,指向对象所属类的虚表。在构 造时,根据对象的类型去初始化虚指针vptr,从而让vptr指向正确的虚表,从而在调用虚函数时,能找到 正确的函数

(3)所谓的合适时机,在派生类定义对象时,程序运行会自动调用构造函数,在构造函数中创建虚表 并对虚表初始化。在构造子类对象时,会先调用父类的构造函数,此时,编译器只“看到了”父类,并为 父类对象初始化虚表指针,令它指向父类的虚表;当调用子类的构造函数时,为子类对象初始化虚表指 针,令它指向子类的虚表

(4)当派生类对基类的虚函数没有重写时,派生类的虚表指针指向的是基类的虚表;当派生类对基类 的虚函数重写时,派生类的虚表指针指向的是自身的虚表;当派生类中有自己的虚函数时,在自己的虚 表中将此虚函数地址添加在后面 这样指向派生类的基类指针在运行时,就可以根据派生类对虚函数重写情况动态的进行调用,从而实现 多态性。

构造函数与析构函数的区别

  1. 构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
  2. 析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)

单例模式

参考 https://www.cnblogs.com/xiaolincoding/p/11437231.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
////////////////////////// 饿汉实现 /////////////////////
class Singleton
{
public:
// 获取单实例
static Singleton* GetInstance();

// 释放单实例,进程退出时调用
static void deleteInstance();

// 打印实例地址
void Print();

private:
// 将其构造和析构成为私有的, 禁止外部构造和析构
Singleton();
~Singleton();

// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
Singleton(const Singleton &signal);
const Singleton &operator=(const Singleton &signal);

private:
// 唯一单实例对象指针
static Singleton *g_pSingleton;
};

// 代码一运行就初始化创建实例 ,本身就线程安全
Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton;

Singleton* Singleton::GetInstance()
{
return g_pSingleton;
}

void Singleton::deleteInstance()
{
if (g_pSingleton)
{
delete g_pSingleton;
g_pSingleton = NULL;
}
}

void Singleton::Print()
{
std::cout << "我的实例内存地址是:" << this << std::endl;
}

Singleton::Singleton()
{
std::cout << "构造函数" << std::endl;
}

Singleton::~Singleton()
{
std::cout << "析构函数" << std::endl;
}
////////////////////////// 饿汉实现 /////////////////////

计算机网络

TCP与UDP的区别

TCP UDP
面向连接 无连接的
提供可靠的服务 UDP尽最大努力交付
面向字节流 面向报文
有拥塞控制 没有拥塞控制
TCP连接只能是点到点的 UDP支持一对一,一对多,多对一和多对多的交互通信
TCP首部开销20字节 UDP的首部开销只有8个字节

TCP三次握手

image-20210820110104796

TCP四次挥手

image-20210820110017508

TCP如何实现可靠传输

image-20210819232117290

计算机网络模型

image-20210820103407379

操作系统

进程和线程的区别

进程 线程
资源分配基本单位 资源调度的基本单位
有自己的独立地址空间 共享所属进程的地址空间
在进程切换时,涉及存储器管理方面的操作,开销比线程大
线程依赖进程而存在
进程是拥有系统资源的一个独立单位 线程自己基本上不拥有系统资源,只有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)

堆和栈的区别

由开发者控制 编译器自动管理
堆内存可以达到4G的空间 默认的栈空间大小是1-2M
堆频繁new/delete会产生大量碎片(重点是如何解决?如内存池、伙伴系统等) 不存在碎片问题
堆向上生长(向着内存地址增加的方向) 栈向下生长(向着内存地址减小的方向增长)
堆都是动态分配的 静态分配和动态分配
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持,效率更高

死锁

多个并发进程中,每个进程持有某种资源又等待其他进程释放它们现在保持着的资源,没改变这个状态之前不能向前推进,这组进程就产生了死锁。

处理方法

  • 鸵鸟策略

    • 死锁影响不大,或者概率很低,可以忽略死锁的情况。
  • 死锁预防 (破坏产生死锁的四个条件)

    • 破坏互斥:允许某些资源被多个进程同时访问
    • 破坏占有并等待:资源预先分配,或者申请资源前先释放占有的资源
    • 破坏非抢占:允许强制抢占
    • 破坏循环等待条件:对所有资源同一编号,所有进程对资源的请求按序号递增的顺序提出
  • 死锁避免

    • 银行家算法:动态检测资源分配状态,处于安全状态才能进行资源分配。(安全状态:即使所有进程突然请求所需的资源,也能存在某种资源分配顺序,让每一个进程运行完毕)
  • 死锁解除

    • 利用抢占:挂起进程并抢占其资源(要避免挂起时间过长而饥饿)
    • 利用回滚:让某些进程回退到可以解除死锁的情况,自动释放资源
    • 杀死某些进程知道死锁解除
  • 死锁的检测

    • 检查有向图是否存在环
    • 使用类似死锁避免的检测算法

线程是越多越好吗

$最佳线程数 = CPU核心数 (1/CPU利用率) = CPU核心数 (1 + (I/O耗时/CPU耗时))$

算法与数据结构

B树和B+树的区别(B树是一种平衡多路搜索树)

B+树中只有叶子节点会带有指向记录的指针(ROWID),换句话说,就是非叶子节点不会带上 ROWID,一个块中可以容纳更多的索引项,可以降低树的高度;B树所有节点都带有

B+树中所有叶子节点都通过指针连接在一起,范围扫描性能要好一点;B树不会,B树需要在叶子节点和内部节点之间不停地移动。

B树的应用场景

文件系统和数据库

(了解哪些算法)冒泡排序,选择排序,快排

冒泡:比较相邻的元素。升序的话就是,如果第一个比第二个大,就交换位置

选择:最开始在未排序的序列里面找到最小元素,存放到排序序列的起始位置,然后,再从未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推

快排:随机挑一个数作基准,小于基准的放在前,大于基准的放在后。把序列分为较小区和较大区,再分别对小数与大区进行排序。

归并排序:把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。可从上到下或从下到上进行。

堆排:从堆顶把根卸出来放在有序区之前,再恢复堆。

双向链表插入节点

http://www.360doc.com/content/17/0427/12/30427643_649056362.shtml

image-20210819231749802

平衡二叉树

任一节点对应的两棵子树的最大高度差为1



----------- 本文结束 -----------




0%