C++的面向对象特性:

1.封装:将数据和函数封装成一个叫“类(class)”的结构体,工厂模式封装了对象创建的复杂性,外界不知道如何创建和初始化对象和细节,他只要调用工厂方法就好了
2.继承:继承允许一个类(派生类)从另一个类(基类)继承属性和行为
继承有三种模式:public(基类的公有—-派生类公有,基类的保护—-派生类保护),protected(基类的公有和保护都变成保护成员),private(基类的公有和保护都变成私有),不论什么方式,基类的私有成员拥有不会被派生类继承
3.多态:不同的对象使用相同的接口来操作
运行时多态:父类的(纯)虚函数被子类继承和重写,实现其特有的功能
编译时多态:函数重载和运算符重载
4.抽象:接口和实现分离,称为抽象(纯虚函数是接口,重载是实现)

C++的构造以及析构函数和友元函数

构造函数:构造函数是一个特殊类型的成员函数,当创建类的对象时会自动调用。它主要用于初始化对象的状态
1.默认构造函数:如果没有定义任何构造函数,编译器会提供一个。如果定义了其他构造函数,但没有定义默认构造函数,则编译器不会提供默认构造函数。
2.参数化构造函数:带有参数的构造函数。
3.拷贝构造函数:用于初始化一个对象为另一个对象的拷贝。
虚函数用于实现多态,而构造函数用于初始化对象。在构造对象时,对象的类型是明确且固定的,因此多态和虚函数在这里没有意义。
析构函数:析构函数是一个特殊类型的成员函数,当一个对象不再使用时会自动调用。它主要用于释放为对象分配的资源
当创建派生类的对象时:
  1. 基类的构造函数首先被调用。
  1. 接着派生类的构造函数被调用。
3.派生类的析构函数先被调用
4.基类的析构函数再被调用
基类的析构函数为什么是虚函数?
如果基类的析构函数不是虚函数,并且你使用一个基类指针来删除一个派生类对象,那么派生类的析构函数不会被调用,这可能导致资源泄露。
友元函数:友元函数是一个不是类的成员的函数,但它能访问类的所有成员,包括私有成员
友元类:当一个类被声明为另一个类的友元时,这个类就可以访问其友元类的私有和受保护成员
友元关系不是双向且不可传递

C++的内存管理

  • 栈:执行函数时,函数内部变量的存储单元都在栈上创建
  • 堆:new分配的内存块就以堆的形式显示(new的底层实现包括malloc)
  • 全局存储区(bss):主要存放静态数据,全局变量和常量
  • 常量区:存放的是常量字符串
  • 代码区:存放程序的二进制代码
allocate 包装 malloc,deallocate包装free———-内存池
1.首先客户端会调用malloc()配置一定数量的区块,一半给程序使用,一半给内存池
2.有内存需求先看内存池有无剩余,有的话先看内存池再重新申请

虚函数和纯虚函数的区别

1.定义
虚函数通常用于提供一个可被派生类重写的方法,并为其提供默认实现。
纯虚函数用于定义一个接口或一个基类,这个基类明确表示某些方法必须由派生类来实现。
2.目的
虚函数:允许派生类提供特定于其自身的实现,而当我们通过基类指针或引用来访问对象时,可以实现多态性。
纯虚函数:派生类必须覆盖基类中的纯虚函数,否则该派生类也将变成抽象类
不能为虚函数的函数
构造函数:虚函数用于实现多态,而构造函数用于初始化对象,在构造对象时,对象的类型是明确且固定的,因此多态和虚函数在这里没有意义。
静态函数:静态成员函数是类级别的,而不是对象级别的,虚函数工作原理基于对象
友元函数:友元不是类的成员,它只是能够访问类的私有和保护成员的非成员函数。由于它不是类的成员,所以它不能是虚函数。 全局函数:全局函数不属于任何类,因此它们也不能是虚函数。

指针和引用的区别

  • 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名
  • 指针可以有多级,引用只能有一级
  • 指针在初始化后可以改变指向,但是引用不能
  • sizeof(指针)得到的是本指针的大小,sizeof(引用量)得到的是引用所指向变量的大小
  • 不存在空值引用,但存在空值指针
  • 把引用当作参数传递的时候,改变这个变量会改变实参,指针不会

区别以下指针类型

  • int *p[10] :指针数组
  • int (*p)[10]:数组指针
  • int *p(int):指针函数
  • int (*p)(int):函数指针

new/delete 与 malloc/free的异同

相同点:都可用于内存的动态申请和释放
不同点:new自动计算需要分配的空间,malloc需要自己计算
new是类型安全的,malloc因为其返回void指针因此不具备安全性
new/delete会调用构造函数和析构函数,malloc/free不会调用仅仅回收空间

宏定义和typedef的区别

宏定义:定义常量和书写复杂的内容
typedef:定义类型别名

常量指针和指针常量的区别(看const的修饰对象)

常量指针:其自身可以被修改,但是指向内容不能被修改
指针常量:自身不可以被修改,指向内容可以被修改

内存泄漏和野指针和悬空指针

当程序动态分配的内存没有被正确地释放,并且该块内存不能再被程序访问时,就会发生内存泄露。
  • 野指针:指一个没被初始化过的指针,或者一个释放但是没被赋为nullptr的指针
  • 悬空指针:指向的内存已经被释放,使用这个指针可能会导致未定义的行为
可能引起的问题:
1.访问违规:导致未定义的行为(野指针可能指向任意地址,悬空的内存可能已经被分配给其他)
2.数据损坏:向野指针所指向的随机内存地址写数据可能会覆盖或破坏其他数据或代码,如果该内存已经被重新使用,通过悬空指针进行的任何写操作都可能破坏新对象的状态。
双重释放:如果试图再次释放悬空指针所指向的内存,将导致双重释放错误。
程序崩溃野指针可能会指向不允许访问的内存地址,如保护的系统内存,这会导致程序崩溃
处理这些问题的有效方法:
  • 当释放内存后,立即将指针设置为nullptr
  • 始终初始化指针,最好将其初始化为nullptr或有效的内存地址。
  • 使用智能指针(如C++中的std::shared_ptrstd::unique_ptr)可以帮助管理内存并减少这些问题的风险。

重载和重写的区别

重载:主要特点是函数名相同,参数类型和数目有所不同
重写:派生类中覆盖基类中的同名函数,基类函数必须是虚函数
虚函数:虚函数的作用是允许使用基类的指针来调用子类的函数,从而实现“多态”

public,protected,private的区别

  • public的变量和函数在类的内部外部都可以访问
  • protected的变量和函数只能在类的内部和其派生类中访问
  • private修饰的元素只能在类内访问
继承权限 public>protected>private,基类 private 成员在任何继承方式下都是不可见的
  • public 继承 + private 成员 => 不可见
  • public 继承 + protected 成员 => protected
  • protected 继承 + public 成员 => protected
  • private 继承 + protected 成员 => private
  • private 继承 + public 成员 => private

如何用代码判断大小端储存(类型转换,union联合体)

  • 大端:字数据的高字节存储在低地址中
  • 小端:字数据的低字节存储在低地址中

C++的异常处理方法

1.throw-使用throw抛出异常
2.try-catch 用来捕获和处理异常
3.exception 提供的标准异常lei

智能指针与RALL机制

RAll:通过管理资源来确保资源不再被需要的时候可以正确释放,是C++语言的重要特性
RALL应用:
1.智能指针:std::shared_ptrstd::unique_ptr就是RAII的实现,它们管理动态分配的内存资源
2.资源锁定:RAII还可以用于管理资源锁定,例如使用std::lock_guard来自动获取和释放互斥锁。
智能指针式C++11新引入的类模板,行为类似于指针,但是不需要手动申请释放资源,因此被成为称为智能指针
引入了三种智能指针:
std::unique_ptr :独占资源,同一时间只有一个拥有,一旦更换指向对象,原有的对象就会被销毁释放
std::shared_ptr :多个指针可以共享一个对象,使用了使用了引用计数(use count)技术,当计数位0的时候才释放资源
std::weak_ptr :可以解决循环引用,两个share相互引用,计数永远不会归零,当weak_ptr 指向不会增加shared_ptr计数
智能指针是类型安全的,其底层使用的引入计数是atomic的原子变量,原子变量在增自减的时候是线程安全的
auto_ptr因为不支持数组和STL再C++17被移除

继承和组合的区别

继承:是一个类从父类获取其特性和功能的过程
组合:其中一个类包含另一个类的对象作为成员,被包含的类称为成员对象

ifdef/endif 和普通if的区别

当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,在编译的时候处理的

strcpy和memcpy的区别

strcpy只能复制字符串,遇到反斜杠0结束
memcpy可以复制任意内容,且需要指定长度

什么是一致性哈希

是一种哈希算法,就是在移除或者增加一个结点的时候,能尽可能小的改变已存在key的映射关系
其基本思想就是用相同的hash算法将数据和节点都映射到图中的环形哈希空间中

C++从代码到可执行程序经历了什么?

1)预编译:处理源代码文件中的以“#”开头的预编译指令
2)编译:把源代码翻译成汇编语言代码,还可以处理一些如inline这种关键词
3)汇编:把汇编代码转变成机器二进码文件
4)连接:把不同源文件产生的目标文件进行连接,从而形成一个可执行程序,连接分为静态连接和动态连接
静态连接:在编译链接可执行文件时,链接器从库中复制这些函数和数据并和模块组合起来创建最终的可执行文件,静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西, 在执行的时候运行速度快
动态连接:程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件,优点就是更新方便,只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍

NULL和nullptr的区别

在早期的C++中,NULL常被定位为整数0,
c++11新增的nullptr专门用于表示指针的空值,可以明确区分整型和指针类型,根据环境自动转换成相应的指针类型

友元函数和友元类

一个不同的函数或者另一个类中的成员函数可以访问类的私有成员和保护成员
  • 友元关系不能被继承
  • 友元关系是单向的,不具有交换性,类B是类A的友元,类A不一定是类B的友元
  • 友元不具有传递性:若类B是类A的友元,类C是B的友元,类C不一定是类A的友元

内联函数与lambda(匿名函数)

内联函数:就是在编译的时候把函数代码直接插入到调用者代码中
1.利用lambda表达式可以定义匿名函数
2.用作函数的参数,如排序算法中
3.变量捕获,捕获可以按值或按引用进行

STL11:怎么定义方块的,往下

1.左值引用和右值引用
左值引用:表示一个具体的对象,可以对其进行修改,左值引用通过使用&符号来声明。
右值引用:右值引用通常用于实现移动语义,是对临时值、右值或将要被移动的对象的引用,它通过使用&&符号来声明

设计模式:

1.工厂模式:工厂模式是定义了一个创建对象的抽象方法,由子类决定要实例化的类,一个工厂对应一个产品,创建产品就新建工厂,不需要修改工厂类
  • 简单工厂模式:只有一个工厂类来创建对象,要增加产品必须修改工厂类
2.代理模式:代理对象和被代理对象实现相同的接口,代理对象会调用被代理对象的方法,代理模式可以在不修改被代理对象代码的情况下,为对象增加额外的行为。
Nginx的反向代理功能,可视为一种特殊的远程代理。它隐藏了真实的服务器地址,为客户端和服务端之间提供一个中间层,用于处理HTTP请求和响应。这样,客户端不直接与后端服务器通信,而是与Nginx通信。Nginx再把请求转发给后端服务器并将响应返回给客户端。
3.单例模式:保证只有一个实例化对象,且提供一个全局访问点
饿汉模式:在程序启动或单例类被加载时就会创建实例,线程安全【std::call_once】
懒汉式:只有在实际使用时才会创建实例,需要锁机制来确保多线程下的环境安全
4.策略模式:定义了一系列算法,将算法封装起来,这些算法可以相互交换,不影响到用户
  • 抽象策略角色:通常情况下使用接口或者抽象类去实现
  • 具体策略角色:包装了具体的算法和行为
  • 环境角色:内部会持有一个抽象角色的引用,给客户端调用
5.观察者模式,建立一个一对多的依赖,当一个对象发生改变时,多依赖都收到通知,在一个股票交易系统中,当股票价格改变时,需要通知所有订阅了该股票的投资者
  1. 主题(Subject): 这是被观察的对象。它通常维护一个观察者列表,提供注册(添加)和注销(删除)观察者的方法,并在有必要的时候通知所有注册的观察者。
  1. 观察者(Observer): 定义了一个更新接口,用于在得到主题更改通知时更新自己。

哈夫曼树:

给定N个权值作为N个叶子节点,构造一棵二叉树,若该树的带权路径长度到达最小,则称这样的树是哈夫曼树。
哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

Const和static:

const:当一个变量被声明为 const,这意味着它的值不能被修改
基本类型:对于基本数据类型(如 int, float, char 等),声明为 const 的变量的值不能更改
成员函数:const 修饰符可以用来表示成员函数不会修改该类的成员变量
const成员变量必须在构造函数的初始化列表中进行初始化
1.int * const p1 = &y; 修饰指针
2.const int * p2 = &y; 修饰int
  • const 置于 的左边,它就修饰指针所指向的数据(即数据是只读的)。
  • const 置于 的右边,它就修饰指针本身(即指针是只读的,不能被重新赋值)。
static:static 关键字主要用于声明类的静态成员
局部变量: 当局部变量被声明为 static,它不会在函数调用结束时被销毁,而是会保持其值,直到程序结束
类的成员:类的所有对象都共享同一个静态成员的实例
成员函数: 类的静态成员函数只能访问该类的静态成员,不能访问非静态成员

inline和define的区别

1.编译时机
  • inline是一个编译器建议,一般在编译的时候使用,并告诉编译器将函数内容嵌入到调用处
  • define是个预处理的宏,一般是在预处理阶段使用,将宏定义处的文本替换为预处理器指定的内容
2.语法
  • inline有参数和返回值
  • #define没有参数和返回值
inline 是一个“请求”或建议,让编译器考虑内联优化,但最终决定还是由编译器来做。
内联函数是一个编译器优化技术,不仅仅限于使用 inline 关键字的函数。

struct和Union的区别

1.数据存储方式
struct:结构体依次存储,每个成员都有自己的内存空间,共同组成了结构体
union:联合的所有成员共享同一块内存空间
2.内存占用
struct结构体大小等于成员组合大小
union占用等于最大成员的大小
3.用途
struct用于表示包含多个相关字段的数据结构
union主要用于节省内存

linux 常用命令,如何查看进程,如何查看内存

常用命令:
cat:显示文件内容
查看进程:
ps:显示当前用户的进程 ps aux:显示用户所有进程
pgrep:根据名称搜索进程
pkill:根据名称杀死进程
查看内存:
free:显示系统的总内存,已使用的内存
vmstat:提供进程,内存等信息
VIM删除:
1.dd删除
2.%d 3删除第三行
 

Volatile关键字

编译器在遇到volatile时变量不应该执行某些优化。以确保每次访问该变量都会从其存储位置重新读取其值
1.应用于硬件中断:确保从地址读取数据,不使用缓存值
2.多线程编程中使用:使用Volatile可以保证每个线程在访问变量前会从内存读取其值,但是不能代替锁,因为不能保证其原子性
3.轮询和状态查询:确保最新值被读取

深拷贝和浅拷贝

浅拷贝仅复制对象的成员值。对于基本数据类型成员(如int、float等),这是简单的值复制。但是对于指针或引用类型成员,只是复制指针或引用的值,而不是它们所指向的内容。结果是原始对象和复制对象会共享相同的内存地址,浅拷贝可能会导致双重释放、内存泄漏或悬挂指针等问题。
(默认的构造函数是浅拷贝,在手动管理内存的时候,如果A和B是使用的浅拷贝会使AB指向一块内存,都被析构的时候会导致双重释放错误)
深拷贝不仅复制对象的成员值,还复制指针或引用成员所指向的内容,结果是创建了对象的一个完整副本,包括其动态内存内容。原始对象和复制对象之间不会共享任何内存地址。
引用拷贝实际上并不涉及真正的对象复制。它只是创建了一个新的引用或指针,指向原始对象,原始对象和引用拷贝共享相同的内容和内存地址
(某些内存管理技术(例如智能指针如C++中的std::shared_ptr)中,引用拷贝被用作引用计数的基础。每次对象被引用拷贝时,引用计数增加。当计数减少到零时,资源被释放)

it++与++it的区别

  1. 语义上的差异
      • ++it(前缀增加):首先增加迭代器或变量,然后返回增加后的值。
      • it++(后缀增加):返回迭代器或变量的当前值,并在返回后增加它。
  1. 性能上的差异
      • 对于基本数据类型,两者之间几乎没有性能差异。
      • 对于复杂数据类型和对象(如 STL 的迭代器),it++通常需要创建原始迭代器的临时副本以返回其原始值,而++it只是简单地增加了迭代器并返回它,这使得++it稍微更加高效。
 
 
Camellia
Camellia
明天会更好吗?🍚
公告
type
status
date
slug
summary
tags
category
icon
password
这里是一个个人博客
用于记录和分享生活