自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(132)
  • 资源 (1)
  • 收藏
  • 关注

原创 【Linux系统编程:线程】 线程控制 -- 创建、终止、等待、分离 | 线程互斥与同步 | 互斥量与条件变量 | 生产者消费者模型 | 线程池 | STL/智能指针与线程安全 | 读者写者模型

本文重点:基本上很多操作系统的书上对线程的解释是:线程是进程的一个执行分支,是在进程内部运行的一个执行流。而到具体一款操作系统,我们将从是什么、为什么、怎么办三个角度来解释线程。我们先来谈线程是什么的问题?对于上图,我们当然很熟悉,现在就可以明确了,这里是一张用户级页表,内核页表就不画了,我们都知道可执行程序在磁盘中无非就是代码或数据,更准确点表述,代码也是一种数据,程序一运行,实际就会将其加载到物理内存,因为每个进程都有地址空间和页表,所以可以通过用户页表映射物理内存的方式,来找到磁盘或内存中的数据。当然

2023-12-07 13:51:29 2422 44

原创 【Linux系统编程:信号】产生信号 | 阻塞信号 | 处理信号 | 可重入函数 | volatile | SIGCHLD

通过学习信号可以理解进程与进程的一个相对关系,还能理解操作系统与进程的关系。要注意的是进程间通信中的信号量与这里的信号没有半毛钱关系,就像老婆和老婆饼。掌握 Linux 信号的基本概念掌握信号产生的一般方式理解信号递达和阻塞的概念,原理掌握信号捕捉的一般方式重新了解可重入函数的概念了解竞态条件的情景和处理方式了解SIGCHLD信号, 重新编写信号处理函数的一般处理机制。

2023-10-24 20:07:02 667 29

原创 【Linux系统编程:进程间通信】匿名管道 pipe | 命名管道 fifo | system V --- 共享内存 shmget & shmctl & shmat & shmdt,消息队列,信号量

在日常生活中,通信的本质是传递信息,但具体在程序员角度,通信的本质是传递数据。进程间通信就是进程之间互相传递数据,那么进程间能直接相互传递数据吗?—— 不能,因为进程具有独立性,所有的数据操作,都会发生写时拷贝。父子进程都不能传递,更别谈两个进程毫无关系。所以两个进程要通信一定要通过中间媒介的方式来进行通信,所以必须先想办法让不同的进程看到同一份公共的资源,这里所谓的公共的资源就是系统通过某种方式提供的系统内存。

2023-10-12 19:49:24 562 14

原创 【C++进阶:二叉树进阶】二叉搜索树的操作和key模型、key/value模型的实现 | 二叉搜索树的应用 | 二叉搜索树的性能分析

从这里开始 C++ 的语法就告一段落了。二叉树在前面的数据结构和算法初阶中就讲过,本文取名为二叉树进阶是因为:我们之前说过,普通二叉树单纯的存储数据价值不大,因为仅仅是单纯的存储数据,你不如去使用顺序表和链表这样友善的多。它一定要套一种应用场景,二叉树的其中一个重要的应用场景就是我们本文中要学习的二叉搜索树。二叉搜索树又称搜索二叉树或二叉排序树 (如果你对下图的二叉搜索树中序遍历,你会发现它是有序并且升序的),我们当前学习的搜索二叉树一般是不允许修改的,不过下面我们会学习搜索二叉树的 key_value 模

2022-08-27 17:18:27 1542 4

原创 【Linux系统编程:基础IO 下】dup2 实现输出重定向、输入重定向、追加重定向 | 理解磁盘 | 理解文件系统中inode的概念 | 软硬链接|制作打包与使用动静态库

这里先接着《基础IO 上》中的缓冲区的内容作些补充,这里主要补充 dup2 接口。close(1);return 1;return 0;}close 1 后,1 就不再表示显示器文件,而 open log.txt 后,1 就表示 log.txt 文件,所以 fprintf 并不会往显示器上输出,而是会往 log.txt 里输出,可是 log.txt 中没有内容。...

2022-08-07 08:10:32 3043 31

原创 【Linux系统编程:基础IO 上】简单复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux下,一切皆文件 | 重定向原理

关于进程,我们还要再往下学习进程间通信、进程信号,但是在这之前,我们先学习基础IO,这篇文章穿插在进程中并不奇怪,因为它有着承上启下的作用。文件的宏观理解那么文件在哪呢?——广义上理解,键盘、显示器等都是文件,因为我们说过“Linux下,一切皆文件”,当然我们现在对于这句话的理解是片面的;狭义上理解,文件在磁盘上,磁盘是一种永久存储介质,不会受断电的影响,磁盘也是外设之一,所以对文件的所有操作,都是对外设的输入输出,简称IO(Input、Output)。文件的组成。.........

2022-07-20 08:43:56 1720 34

原创 【C++进阶:多态】多态的构成条件 | 虚函数的重写 | 抽象类 | 多态的原理 | 多继承的虚函数表

本文我们将学习面向对象三大特性中的最后一个特性 —— 多态。需要声明的是,多态一文包括继承一文中所演示的测试用例都是按我的编译器 VS2017 来演示的,我们会演示底层细节,如果你换一个编译器,包括 VS 系列,可能展示出来的效果会有一点变动,因为 C++ 并没有规定具体的实现细节。顾名思义,多态就是多种形态,具体就是去完成某种行为时,不同的对象去完成时会产生不同的状态。举个粟子,比如买票这种行为,当普通人去买票时,是全价买票;当学生去买票时,是半价买票;当军人买票时,是优先买票。再举个粟子,为了争夺在线支

2022-07-11 07:54:09 1142 39

转载 C++ 对象的内存布局

07年12月,我写了一篇《C++虚函数表解析》的文章,引起了大家的兴趣。有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为,在上一篇文章中,我用了的示例都是非常简单的,主要是为了说明一些机理上的问题,也是为了图一些表达上方便和简单。不想,这篇文章成为了打开C++对象模型内存布局的一个引子,引发了大家对C++对象的更深层次的讨论。当然,我之前的文章还有很多方面没有涉及,从我个人感觉下来,在谈论虚函数表里,至少有以下这些

2022-07-10 08:38:09 630 4

转载 C++ 虚函数表解析

C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。当然,相同的

2022-07-10 08:24:40 370

原创 【C++进阶:继承】C++为什么要引入继承 | 继承概念及定义 | 基类和派生类对象赋值转换 | 继承中的作用域 | 派生类的默认成员函数 | 继承与友元/静态成员 | 复杂的菱形继承及菱形虚拟继承

在 C++ 初阶专栏 ➡ 类和对象一文中,我们提出了面向对象的三大特性 —— 封装、继承、多态。但在 C++ 初阶专栏中涉及到的只是封装,而这里我们直接以封装和继承作为 C++ 进阶专栏中的敲门砖。我们说过 C++ 是大佬从 C 发展出来的,最开始的 C++ 叫做 C With Class,就是在 C 的基础上增加了类。经过 C++ 初阶的学习,我们知道了 C++ 中类,就是为了对标并解决 C 的缺陷,比如构造、析构等。..................

2022-07-04 07:17:13 1507 43

转载 优先使用对象组合,而不是类继承

《极限编程》(Extreme programming)的指导原则之一是“只要能用,就做最简单的”。一个似乎需要继承的设计常常能够戏剧性地使用组合来代替而大简化,从而使其更加灵活。因此,在考虑一个设计时,问问自己:“使用组合是不是更简单?这里真的需要继承吗?它能带来什么好处?”...

2022-07-03 09:23:28 711

原创 【Linux进程控制】进程创建 | 进程终止 | 进程等待 | 进程替换

本文主要学习理解 fork 的返回值、写时拷贝的工作细节、为什么要存在写时拷贝;进程退出码、进程退出的场景及常见的退出方法、对比 man 2 _exit 和 man 3 exit;进程终止、操作系统怎么进行释放资源、池的概念;进程等待的价值、进程等待的方法 wait 和 waitpid(常用)、int* status、阻塞和非阻塞、如何理解等待、WIFEXITED、WEXITSTATUS、WTERMSIG;什么是进程替换 && 为什么要进程替换、替换原理、7个exec系列的替换函数、模拟shell解释器;

2022-06-17 10:00:00 2451 56

原创 【Linux进程概念——下】验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列

文章目录【写在前面】一、程序地址空间二、进程地址空间三、【写在前面】一、程序地址空间这个概念比较重要,之前我们也涉及过 —— 我们在语言上定义的各种变量等在内存中的分布情况,如果没有听说过,那么你的 C/C++ 是不可能学好的。其实对于这块,我们一直是有误区的。二、进程地址空间三、...

2022-05-30 08:07:22 1223 38

原创 【Linux进程概念——上】冯 • 诺依曼体系结构 | 操作系统 | 进程 | fork | 进程状态 | 优先级

从此篇开始,就开始学习 Linux 系统部分 —— 进程,在正式学习 Linux 进程之前,我们需要铺垫一些概念,如冯诺依曼体系结构、操作系统的概念及定位、进程概念,我们会先铺垫理论,再验证理论。其次对于某些需要深入的概念我们只是先了解下。本文中的 fork 只会介绍基本使用,以及解答 fork 为啥会有 2 个返回值、为啥给子进程返回 0,而父进程返回子进程的 pid;而对于用于接收 fork 返回值的 ret 是怎么做到 ret == 0 && ret > 0、写时拷贝、代码是怎么做到共享的、数据是怎么

2022-05-17 16:16:57 3933 66

原创 【Linux环境变量与命令行参数】常见环境变量 | 环境变量的全局属性 | 命令行参数

对于环境变量,主要介绍基本概念及三四个环境变量 —— PATH、HOME、SHELL、HISTSIZE,其中 PATH 作为 “ 敲门砖 ”,我们会更详细讲解;理解环境变量的全局属性 —— 环境变量是可以被子进程继承(注意区分 C++ 里的继承);环境变量的组织方式。其次会介绍命令行参数 —— main 函数的参数。注意学习了本文的知识,并不代表己经掌握了环境变量,因为还有很多关于环境变量使用的技巧及常见的环境变量没有过多的介绍,后面有需要用到环境变量的地方在展开。

2022-05-17 09:52:18 2088 33

原创 【C++初阶:模板进阶】非类型模板参数 | 模板的特化 | 模板分离编译

模板的进阶会涉及模板的一些更深入的知识。在此之前,我们可以看到模板在 C++ 中是随处可见的,它能支持 C++ 泛型编程,模板包括函数模板和类模板,注意,有些人可能会说模板函数和模板类,但严格来说这种说法是错误的。实际中类模板要比函数模板用的场景多,比如说 STL 中的 vector、list、stack 等是类模板;algorithm 中的 sort、find 等是函数模板。

2022-05-06 09:42:01 1226 26

原创 【C++初阶:STL —— stack and queue】stack/queue的介绍及使用 | stack/queue/priority_queue的深度剖析及模拟实现 | 适配器模式 | 仿函数

虽然 cplusplus 把 stack 和 queue 归类到了 Containers 下,但是严格来说 stack and queue 不再是容器了,而属于容器适配器 or 容器配接器,适配器做的功能是转换 —— 它不是直接实现的,而是由其它容器封装转换实现的,在下面的模拟实现我们会细谈。它做为容器适配器,它与容器有一个具大的差别之一就是它没有迭代器,不是说它不能实现迭代器,而是没有必要实现迭代器,因为它如果实现了迭代器,就没法保障 stack “Last In First Out” 和 queue

2022-05-02 09:24:38 1610 27

原创 【C++初阶:STL —— list】list的介绍及使用 | list的深度剖析及模拟实现 | list与vector的对比

在学完 list,大家对 STL 中的迭代器的认知会进一步提高。list 用的虽然不多,但是它的底层有很多经典的东西,尤其是它的迭代器。list 的结构对我们来说应该问题不大,因为在《数据结构》时我们就已经了解过链表了,它的结构是一个带头双向循环链表,之前我们也实现过。对于 list 没有 reserve 和 resize,因为它的底层不是连续的空间,它是用一个申请一个,不用一个【写在前面】在学完 list,大家对 STL 的认知可以更加深刻。list 用的虽然不多,但是它的底层有很多经典的东西,尤其

2022-04-27 08:11:28 1083 31

转载 STL 的string类怎么啦?

上个周末在和我的同学爬香山闲聊时,同学说到 STL 中的 string 类曾经让他备受折磨,几年前他开发一个系统前对 string 类还比较清楚,然后随着程序的复杂度的加深,到了后期,他几乎对 string 类失去了信心和信任,他觉得他对 string 类一头雾水。老实说,我几年前也有同样的痛苦(就是当我写下 《 标准C++类string的Copy-On-Write技术 》 之前的一段时间)。那时,我不得不研究那根本不是给人看的 SGI 出品的 string 类的源码,代码的可读性几乎为零,而且随着了解越深

2022-04-25 10:15:00 498 3

原创 【C++初阶:STL —— vector】vector的介绍及使用 | 迭代器失效问题 | vector的深度剖析及模拟实现

相比于 string,vector 的使用更加容易上手的,且它的接口比 string 要少上许多,再加上我们已经学过类似的 string,并且在数据结构篇的顺序表就已经触及过了。vector 在实际中也非常的重要,在实际中我们熟悉常见的接口就可以了。在 v【写在前面】一、vector的介绍及使用💦 vector的介绍💦 vector的使用二、vector的深度剖析及模拟实现💦 std::vector的核心框架接口的模拟实现💦 使用memcpy拷贝问题💦 对bit::vector核

2022-04-22 10:57:50 2830 26

原创 【Linux工具】:软件包管理器yum | 编辑器vim | 编译器gcc/g++ | 调试器gdb | 自动化构建工具make/Makefile | Linux小程序:进度条 | git命令行

我们学习 Linux 主要是在上面做开发的 —— 写代码、编译代码、调试代码、自动化构建项目、上传 github/gitee。在这里我们一共会介绍 5 - 6 个工具 —— 软件包管理器 yum、文本编译器 vim、代码编译器 gcc/g++、调试器 gdb、自动化构建项目 make/Makefile、以及 Linux 上的 git 的命令行。......

2022-04-12 10:05:39 2522 9

原创 【C++初阶:STL —— string】string类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现

到这里我们就要学会能自己能看文档了,因为就这个 string 类来说就有一百多个接口函数,那肯定记不住呀,我们平时用的大概就二十个,一般我们都是学习它比较常用的,其它的大概熟悉它们有哪些功能,真要用到时再去查文档。可以看到其实 string 就是一个管理字符数组的顺序表,因为字符数组的使用广泛,C++ 就专门给了一个 string 类,由于编码原因,它写的是一个模板。针对 string,一般情况它有三个成员 —— char* _str、size_t _size、size_t _capacity,我们在下面模

2022-03-29 07:00:00 2460 6

原创 华为云产品齐全,助力中小企快速成长

【写在前面】2021 年伴随着全国疫情的大暴发,市场经济低迷,不少中小企业受到了威胁。近几个月,政府的严格管控,疫情得到了有效控制,压抑许久的中国经济开始复苏,甚至会在 2022 年爆发。财政部部长刘昆于 2021-12-27 日表示,2022 年将实施更大力度减税降费,增强市场主体活力,2021 年,我国优化和落实减税降费政策预计全年达到 1 万亿元,2022 年,要严格落实国家出台的各项减税政策,加强对中小微企纾困帮扶。...

2022-03-11 10:08:32 587 2

原创 【C++初阶:模板初阶】函数模板 | 类模板

之前在学数据结构时就说过,C 语言没有实现数据结构的库,非常的麻烦,为什么 C 语言没有这样的库呢,因为它不支持泛型编程。所以在 C++ 中它支持了泛型编程,支持了模板 —— 函数模板、类模板。这里只是模板的入门,只为了先入门 STL。后面 C++ 初阶会对模板进行进阶的学习。

2022-03-08 10:02:28 653 4

原创 【C++初阶:STL简介】

STL (standard template libaray - 标准模板库):是 C++ 标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。JAVA 中也有对应的语法,叫做集合类。

2022-03-05 13:49:54 2100 4

原创 【C++初阶:内存管理】C/C++内存分布及管理方式 | new/delete实现原理及operator new和operator delete函数

C/C++ 的内存管理跟 JAVA 这些语言是不同的 —— JAVA 的程序不是直接跑在操作系统上的,JAVA 是在 JVM 虚拟机上运行的;C/C++ 的程序是直接跑在 OS 上的,这也是为什么我们学习 C/C++ 要学习内存管理的原因,所以 C/C++ 的学习者需要对系统了解的更深,而对于系统的知识更多的会在 Linux 系统编程的阶段去学习。这里我们会学习 new/delete 的使用,以及 new/delete 的底层原理。一...

2022-02-27 10:33:26 1149 6

原创 【Linux权限】Shell命令及运行原理 | Linux权限管理 | 粘滞位

Linux 严格意义上说的是一个操作系统,我们称之为核心 (kernel),也就是内核,但是一般用户,不能直接使用 kernel,而是通过 kernel 的外壳程序,也就是所谓的 shell 来与 kernel 沟通。

2022-02-22 10:13:35 4193 40

原创 【Linux基本指令】os概念及定位 | 基本指令 | 热键

这里不会把所有的指令及选项在入门阶段全部讲出来,当然也不可能全部记住。大量的实例证明一开始死记的人大多数多记不住,这里只讲一些指令的基本功能和一些重要的选项,其余的以后遇到了再实践。在讲每条指令时其中会穿插一些别的概念,其次这里会大概了解下 os 。

2022-02-19 09:49:25 5322 19

原创 【C++初阶:类和对象(下篇)】初始化列表 | static成员 | 友元

这篇文章是对类和对象的一个补充

2022-02-15 15:19:21 1142 4

原创 【C++初阶:类和对象(中篇)】构造函数 | 析构函数 | 拷贝构造函数 | 赋值运算符重载

这篇文章是类和对象的核心知识如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成下面 6 个默认成员函数。

2022-02-12 09:47:44 1070 1

原创 【C++初阶:类和对象(上篇)】类的定义 | 类的访问限定符及封装 | 类的作用域 | 类的实例化 | 类对象模型 | this指针

面向过程和面向对象是一个比较复杂的问题,不是三两句话就能解释清楚的,所以在此文并不细谈,等后面各方面的知识充分起来再细谈。这篇文章是类和对象的入门。C 语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。C++ 是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。注意 C++ 并不是纯面向对象的语言,在前面就说过 C++ 的产生是为了更好的 C,所以 C++ 既有面向过程也有面向对象,可以混合编程

2022-02-05 12:33:29 499 2

原创 C语言入门必做踩坑题《篇八》

第 1 题???? -27 在内存中的存储形式是以下哪一种 ___A. 0001 1011B. 1110 0100C. 1110 0101D. 1010 0111???? 分析:数据在内存中是以二进制的形式进行存储的,二进制有 3 种表示形式:原码、反码、补码,且在内存中存储的是补码,且有正负数之分 —— 如果为正,高位为0;如果为负,高位为11️⃣ 正数的原码、反码、补码相同2️⃣ 负数:     原码 —— 根据数值直接写出它的二进制序列     反码 —— 原

2022-01-29 08:55:20 944

原创 【C++怎么入门】C++是什么及发展史 | 重要性(工作领域、校招领域) | 怎么学

终于迈入 C++ 的大门了,之前用 C 实现数据结构和算法就写到排序,剩下的数据结构豌豆将会使用 C++ 实现,同时上手 Linux 编程,好吧!我已经迫不及待了

2022-01-22 14:03:01 1195 4

原创 【C++初阶:入门总结】命名空间 | 缺省参数 | 函数重载 | 引用 | 内联函数

点到为止,并不深入。其次建工程这里就不说了,在之前的基础上 —— 文件名.cpp 就可以了。 C 语言有 32 个关键字,而 C++ 有 63 个关键字,C 语言的关键字在 C++ 中继续可以使用。在本章中不对关键字进行详讲

2022-01-17 09:37:09 670 9

原创 C++初阶 —— 引用

引用不是新定义一个变量,而是给已存在变量取了一个别名,语法理解上程序不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"

2022-01-14 10:27:54 1215 12

原创 C++初阶 —— 函数重载

【写在前面】自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是 “谁也赢不了!”,后者是 “谁也赢不了!”

2022-01-11 13:51:11 826 11

原创 C++初阶 —— 缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参

2022-01-09 10:20:54 1140 12

原创 C++初阶 —— 命名空间

对比 C 语言,一般 C++ 每增加一个语法都是为了解决一些 C 语言做不到的事或者是 C 语言做的不好的地方在 C/C++ 中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace 关键字的出现就是针对这种问题的。

2022-01-08 13:15:46 752

原创 Linux入门篇

一、Linux 背景介绍???? 发展史???? 开源???? 官网???? 企业应用现状???? 发行版本二、搭建 Linux 环境???? Linux 环境的搭建方式???? 购买云服务器三、使用 XShell 远程登陆到 Linux???? 关于 Linux 桌面???? 下载安装 XShell???? 查看 Linux 主机 ip???? 使用 XShell 登陆主机???? XShell 下的复制粘贴???? 服务器如何共享

2021-12-30 09:00:32 742 10

原创 数据结构和算法之排序总结

一、排序的概念及应用???? 排序的概念???? 排序的运用???? 常见的排序算法二、常见排序算法的实现???? 插入排序1、直接插入排序2、希尔排序???? 选择排序1、直接选择排序2、堆排序???? 交换排序1、冒泡排序2、快速排序???? 归并排序???? 非比较排序1、计数排序2、基数排序???? 文件排序 (拓展)???? 性能测试三、排序算法复杂度及稳定性分析四、概念选择题

2021-12-28 11:06:29 6191 4

三子棋大战人工智能,是男人就赢一把

代码是使用C语言写的,电脑的行为分为3种,且优先及是:一招制敌 > 以守为攻 > 好自为之;适合的人群:大一、大二学生或C语言初学者找不到练手的人群

2021-08-05

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除