自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(34)
  • 收藏
  • 关注

原创 《Effective C++ 改善程序与设计的55个具体做法》读书笔记

尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率。绝对不要重新定义继承而来的函数。绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值是静态绑定,而virtual函数——你唯一应该覆写的定西——确是动态绑定。当编写一个,而它所提供的“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“内部的friend函数”。

2023-07-06 21:54:39 65

原创 C++面试八股文:如何避免死锁?

二师兄:当进程A持有锁1请求锁2,进程B持有锁2请求锁1时,两者都不会释放自己的锁,两者都需要对方的锁,就会造成死锁。二师兄:在C++中,锁(Lock)是一种同步工具,用于保护共享资源,防止多个线程同时访问,从而避免数据竞争和不一致。二师兄:在C++11之前,C++便准层面并没有定义锁,锁的应用要依赖于平台。1.避免循环等待,如果需要在业务中获取不同的锁,保证所有业务按照相同的顺序获取锁。二师兄:从种类上分,可以分为普通锁、读写锁、递归锁等种类。2.使用超时锁,当锁超时时,自动释放锁。

2023-07-04 22:52:46 97

原创 C++面试八股文:如何实现一个strncpy函数?

是一种常见的并行计算技术,一条指令可以同时处理多个数据,所以它可以减少指令的数量,从而提高处理速度。每次可以复制超过一个字节的内容,当数据量大的时候,效率会有明显的提升。二师兄:主要用做字符串复制,将于字符从一个位置复制到另一个位置。有没有什么办法对以上的代码做一些性能上的优化?是程序员传入进来的,且无法知晓dest的长度,所以这个。比如利用指针的自增?面试官:好的,今天就到这里,请回去等通知吧。二师兄:只是听说过,没有用过。

2023-07-03 22:16:19 64

原创 C++面试八股文:技术勘误

这里的erase返回的是下一个迭代器,然后++就是下下个迭代器,跳过了下个迭代器。不知不觉,《C++面试八股文》已经更新30篇了,这是我第一次写技术博客,由于个人能力有限,出现了不少纰漏,在此向各位读者小伙伴们致歉。不仅帮助我改正了文章中的错误,也让我有机会更新和修正自己的知识储备库。同时不同编译器对标准的实现也不尽相同,这增加了C++学习者的负担。二师兄:不可以,因为构造函数在对象构造阶段调用,虚表尚未建立,所以无法调用虚函数实现多态。为了不误导更多的小伙伴,以后会不定期的出勘误文章,请各位小伙伴留意。

2023-07-01 23:33:28 49

原创 C++面试八股文:什么是空指针/野指针/悬垂指针?

二师兄:野指针突出一个野字,这个野就是状态未知的。二师兄:垂悬指针是指指针指向的内容已被释放,指针指向的对象的生命周期已结束。4.要注意长生命周期的指针不能指向短生命周期的对象。二师兄:内存泄漏是指分配的内存空间没有被正确释放的情况。1.在解引用指针之前,要判断指针是否为空。(解决空指针解引用问题)空指针不能被解引用,但是可以对空指针取地址。定义空指针,因为它时有类型的,编译器能够对它进行类型检查。),使用C++的构造和析构机制保证资源的正确申请和释放。面试官:如何解决空指针、野指针、垂悬指针带来的问题?

2023-06-30 23:31:04 67

原创 C++面试八股文:什么是构造函数?

二师兄:构造函数是一种特殊的成员函数,用于创建和初始化类的对象。二师兄:没有任何参数的构造函数(所有参数都要默认参数的构造函数也是)。C++类的构造函数的基本考点都在这里了,小伙伴本要理解这些设计及设计背后的取舍,面对面试官的拷问才能对答如流哦。),将不被初始化,如果是类类型,将执行类类型的的默认构造函数初始化变量。二师兄:类类型的初始化时一个循环的过程,如果类类型中有类类型成员,初始化方式和以上描述的一致。二师兄:不可以,因为构造函数在对象构造阶段调用,虚表尚未建立,所以无法调用虚函数实现多态。

2023-06-29 23:09:25 49

原创 C++面试八股文:知道std::unordered_set/std::unordered_map吗?

二师兄:两者底层使用哈希表实现,因此插入、删除和查找操作的平均时间复杂度为常数时间。值,同时还要告诉编译器如何判断两个自定义类型的对象是否相等。二师兄:这里是平均的时间复杂度。来代替仿函数,目的都是为了使得编译器知道如何计算自定义类型的哈希值。好了,今天的面试到这里就结束了,让我们期待明天面试官的表现吧~面试官:好的,今天的面试就结束了,请回去等消息吧。,真的没有那么多好问题,因为两者太像了。适用于需要有序存储和快速查找的场景,而。的时候,需要告诉编译器如何计算此类型的。适用于需要快速插入和查找的场景。

2023-06-28 22:45:28 65

原创 C++面试八股文:用过std::set/std::map吗?

红黑树是一种自动平衡的二叉树,它确保插入、删除和查找操作的时间复杂度都是。是一个有序的集合,其中的元素是唯一的,即每个元素只能出现一次。面试官:按照你的方法,可以实现从小到大的排序。这里二师兄的是标准答案,实际上当数据量特别大的时候,的确。类模板的第二个模板参数可以传入比较类型,默认比较类型是。当数据量小的时候,算法的优势没有抵消缓存的劣势,所以。面试官:最后一个问题,你觉得单纯的查询而言,是。面试官:好的,今天面试结束了,回去等通知吧。指针,解引用赋值)会造未知的错误。称为有序容器,所以对插入进去的。

2023-06-27 23:28:47 68

原创 C++面试八股文:std::deque用过吗?

的底层容器,让我们比较一下三个容器的差异:(只考虑头插和尾插,因为stack不需要随机插入)二师兄:因为使用它的场景很少,大部分需要性能、且需要自动扩容的时候使用。中还有未被元素填充的位置,则将元素填充到数组中,如果此指针指向的。今天的面试分享到这里就结束了,让我们继续期待二师兄的表现吧。从上表中看到,三种容器的插入和是删除的时间复杂度相同。,插入要分情况,如果是头插和尾插,时间复杂度为。会找到头部或尾部的指针,并通过指针找到对应的。二师兄:说实话,很少用,基本没用过。,需要随机插入和删除的时候可以使用。

2023-06-26 22:43:06 37

原创 C++面试八股文:std::array如何实现编译器排序?

二师兄:区别不是很大,原生数组(非动态数组)和std::array都在栈上开辟空间,初始化的时候需要提供数组长度,且长度不可改变。对于文中出现的技术性错误,我们会不定期的推出勘误文章。C++是一门艰深的语言,让我们和二师兄一起精进吧。面试官:如果一个类型的拷贝构造函数和拷贝赋值运算符是被删除的,可以使用。)空数组的长度应该是0,但是长度为0的。的内存分配在栈上,编译时候确定需要在栈上开辟的空间。对象,而只能传递这个对象的引用或指针。,我们可以在编译时求它所以元素的和。面试官:好的,不错,回去等通知吧。

2023-06-25 22:22:49 48

原创 C++面试八股文:std::vector和std::list,如何选择?

它的底层是单向链表,引入它的主要目的是为了达到手写链表的性能。指针指向的对象,而不需要移动元素的位置,所以不会导致迭代器失效。这里二师兄回答的倒是没有毛病,但是没有考虑到缓存问题。底层采用数组存储数据,所以它的空间局部性更好,对缓存更友好(被称为双向链表,和C中手写双向链表本质上没有大的区别。二师兄:并不会,因为在任意位置添加和删除元素只需要改变。在随机插入数据不会导致数据的搬移。二师兄:不是,GCC中的实现只有在。二师兄:与手写双向链表不同的是,

2023-06-24 18:44:51 153

原创 C++面试八股文:std::vector了解吗?

的实现和其他实现容器的实现不一致。时,需要开辟一块更大的动态数组,并把旧动态数组上的元素搬移到当前动态数组,然后销毁旧的动态数组。删除数据时,被删除数据后面的数据依次向前移一位。这会导致被删除数据之后的迭代器失效。在GCC中,如果插入的元素是右值,两者都会。二师兄:这个值在不同的编译器上不是固定的。面试官:你知道新开辟的动态数组的容量是就数组的多少倍比较合适?记录当前元素的数量和当前动态数组的容量。二师兄:有的,如果知道数据的大概量,我们可以使用。被用尽而导致的多次的数据搬移,从而提升。

2023-06-23 23:34:47 78

原创 C++面试八股文:override和finial关键字有什么作用?

二师兄:大多数人认为,虚函数的调用会先通过虚指针跳到虚函数表,然后通过偏移确定函数真实地址,再跳转到地址执行,是间接调用导致了性能损失。)是指在继承层次结构中,如果两个不同的子类B和C继承自同一个父类A,而又有一个类D同时继承B和C,这种继承关系被称为菱形继承。关键字告诉编译器,这个函数一定会重写父类的虚函数,如果父类没有这个虚函数,则无法通过编译。),在调用函数的时候,会通过虚指针转到虚表,并根据虚函数的偏移得到真实函数地址,从而实现多态。二师兄:虚函数允许在基类中定义一个函数,然后在派生类中进行重写(

2023-06-22 23:40:53 52

原创 C++面试八股文:用过STL吗?

首先是分配器,主要是为容器分配管理内存的。其次是迭代器,是容器和算法的桥梁。再次是仿函数,一般用作算法中,适配器提供各种粘合剂的功能,把不同部件之间的接口连接起来。二师兄:分配器主要用于内存的分配与释放。中做很多事情,比如我们可以统计申请和释放内存的总量,可以申请一块大内存做内存池等等。二师兄的C++面试之旅,明天继续。二师兄:STL中容器的数量比较多,按照类型可以分为顺序容器和关联容器。,通过调用容器的接口,实现适配器所需的功能。二师兄:(每天都用好吗。

2023-06-21 23:42:30 58

原创 C++面试八股文:static_cast了解一下?

二师兄:隐式转换的优势是代码简洁。但是有很大缺陷,有些情况隐式转换的结果和程序员的意图不一致,会导致难以发现的问题。二师兄的C++面试之旅,明天继续。既然已经有C风格的类型转换,C++11为什么还要引入新的类型转换关键字?二师兄:C++支持C风格的类型转换,并在C++11引入新的关键字规范了类型转换。隐式转换是指在表达式中自动进行的类型转换。用途最广泛,除了后面三种类型转换外,其他的类型转换都能使用。二师兄:主要有三点,更安全、更灵活、可读性更好。二师兄:C++11引入四种新的类型转换,分别是。

2023-06-20 23:20:38 57

原创 C++面试八股文:什么是智能指针?

二师兄:Foo中有一个智能指针指向Goo,而Goo中也有一根智能指针指向Foo,这就是循环引用,我们可以使用weak_ptr来解决这个文通。二师兄:智能指针是C++11引入的类模板,用于管理资源,行为类似于指针,但不需要手动申请、释放资源,所以称为智能指针。二师兄:是因为如果返回的裸指针被释放了,智能指针持有的资源也失效了,对智能指针的操作是未定义的行为。的原子变量,原子变量在自增自减时是线程安全的,这保证了多线程读写智能指针时是安全的。面试官:好的,今天的面试结束了,请回去等通知吧。

2023-06-19 23:01:23 132

原创 C++面试八股文:std::string是如何实现的?

针对字符串比较少的情况,一般编译器会做一些优化,你知道如何优化的吗?今天二师兄的表现不错,除了最后一个问题,基本上都答上来了。针对字符串比较少的情况,一般编译器会做一些优化,你知道如何优化的吗?的内容的长度,以此长度申请一块新内存,然后把当前字符串的内存和。内部维护一个指针,这个指针指向真正的字符串的位置。的时候一般会预先申请一块大的内存,这块内存的长度是。时,在内存中申请一块内存,这块内存的起始地址保存在。面试官:好的,今天的面试结束了,请回去等通知吧。,当添加的字符串的长度加上当前的字符串长度小于。

2023-06-18 19:25:53 55

原创 C++面试八股文:聊一聊指针?

二师兄:回到问题,我觉得指针的本质就是内存地址。虽然指针指向一块内存地址,但它同时也是一个变量,也可以对指针取地址。如果有一块容量是1G的内存,假设它的地址是从。),如果把这个内存地址存起来,那么指向这个内存地址的变量的类型就是二级指针(抛开内存的利用率不说,这是一个非常美妙的实现!二师兄:操作系统会再分配一块内存,这块内存的大小是。的实现方式,不同的厂商实现的方式不尽相同。的信息,也保存了这块内存的大小(16)。的时候怎么知道这块内存的大小的呢?的时候怎么知道这块内存的大小的呢?

2023-06-17 21:59:40 48

原创 C++面试八股文:什么是左值,什么是右值?

一次移动(当元素特别多时,移动的成本相对于拷贝基本可以忽略不记),一次析构。二师兄:移动构造是通过移动构造函数实现的,当类有资源需要管理时,拷贝构造会把资源复制一份,而移动构造偷走了原对象的资源。时,为了保证程序的正确性,编译器可能会采用拷贝构造的方式实现移动构造,从而导致效率降低。这里的确是通过static_cast实现的,讲左值强行转换成右值,用来匹配移动语义而非拷贝。是左值是和右值是匹配不同的实现,完成返回不同类型引用的目的。当传入的参数t的类型时右值时,由于引用折叠还是右值,此时的。

2023-06-16 23:48:59 56

原创 C++面试八股文:了解auto关键字吗?

这个特征可以避免变量未初始化就使用所导致的未定义的行为。第二点就是减少五吴意义的代码,如迭代器的类型、二师兄的表现不错,求各位小伙伴一个赞不过分吧。关键字是否存在都不影响程序正确性,所以很少有程序员在在C++98/03使用。C++标准委员会在C++11标准的制定中想到了被冷落的。关键字,并在C++11新标准中赋予了它新的能力。面试官:好的,今天的面试就到这里,回去等通知吧。大伙应该能看出来,在C++98/03中的。今日二师兄的表现很好,基本上回答出了。二师兄:嗯,了解一些(我很熟悉)。

2023-06-15 23:07:00 43

原创 C++面试八股文:在C++中,有哪些可执行体?

二师兄:类的静态方法和函数基本一致,只不过此静态方法属于整个类所有。函数、函数指针、类的静态方法、类的成员方法、仿函数、lambda表达式。二师兄:哦,这个标准也是在C++11引入的,它的对象可以把以上所说的可执行体保存起来。让我们一起期待明天二师兄的表现吧。从性能上讲,函数、类的静态/成员方法(非虚方法)、仿函数和。面试官:那你知道以上可执行体之间的性能有何差别吗?二师兄:仿函数是一个类或结构体,重载了。二师兄:函数指针是指向函数的指针。二师兄:额,性能应该差不多吧。面试官:好的,回去等通知吧。

2023-06-14 23:32:58 36

原创 C++面试八股文:什么是RAII?

二师兄:主要的特点是,在对象初始化时获取资源,在对象析构时释放资源。这种技术可以避免资源邪路或内存泄漏,提高程序的健壮性和可维护性。二师兄:主要可以管理动态分配的内存而不需要手动申请和释放,管理锁不需要手动加锁和解锁,管理句柄不需要手动打开和关闭。好了,今日份面试到这里就结束了。关注我,带你走进二师兄的C++面试生涯。面试官:你知道有哪些C++标准库中已经存在的类型使用了。二师兄:应该是在构造的时候锁定,在析构的时候解锁。今天的面试到此结束,回去等通知吧。关注我,带你21天“精通”C++!

2023-06-13 23:25:53 62

原创 C++面试八股文:了解位运算吗?

二师兄:不同编译器处理的方法不同。二师兄:使用这个数与这个数-1按位与,如果结果是0,则这个数是2的整数次方,否则不是。最后一个问题,如果讲一个数的某一位取反?想到了取反,我们就想到了异或。今日二师兄的表现还不错,除了最后一问,其他都答上来了。二师兄:可以使用异或操作,原理是一个数异或两次同一个数,结果等于原值。二师兄:优势主要有两点:1.速度快。面试官:如何使用位运算交换两个数,而不能申请额外的空间?关注我,带你走进二师兄的跌宕起伏的C++面试生涯。如何将一个数的某一位置成0/置成1,或取反?

2023-06-12 22:19:12 31

原创 C++面试八股文:在C++中,你知道哪些运算符?

这里箭头运算符的调用者肯定是左值(想想看为什么),但是号运算符的调用者可不一定是左值。所以箭头运算符的结果一定是左值,当点运算符的调用者是左值时,返回值时左值,当点运算符的调用者是右值时,返回值时右值。在C++的标准中,大部分的运算符两侧的表达式的求值顺序是不确定的。所以不要对有符号的类型进行按位操作,最好的情况是正好当前编译器的实现和你的预期吻合,当前平台运行无异常。除了二师兄的回答外,前置++的效率要高于后者,因为前者不需要缓存值,以用来返回。面试官:好的,今天的面试结束了,回去等通知吧。

2023-06-11 22:08:16 39

原创 C++面试八股文:了解sizeof操作符吗?

这里在C中是0,在C++中是1。C++标准规定,不同的对象不能拥有相同的内存地址。如果空类大小为0,类的对象数组中的每个对象都拥有了相同的地址,这显然是违背标准的。好了,今日份面试到这里就结束了,小伙伴们,对于今天二师兄的面试,能打几分呢?如果不是虚函数,则结果是1,如果是虚函数,则大小是4或者8。这是一个很大的话题,咱们今天聊不了太多。二师兄:第一个返回4或8,因为它是个指针,第二个是个数组,不过末尾有个。应该需要以8位对齐吧。那你知内存对齐的原则是什么,为什么要内存对齐?面试官:好的,回去等通知吧。

2023-06-09 23:11:11 30

原创 C++面试八股文:C++中,设计一个类要注意哪些东西?

如果一个静态成员变量依赖于另一个静态成员变量的值,要确保第二个静态化成员先被初始化,否则程序可能会出现未定义的行为。移动构造和移动赋值不需要把所有的数据重新拷贝一遍,而是霸占了被移动对象的数据的所有权。),当用户通过父类对象访问子类的方法时,通过查询虚表中对应的方法的地址,并跳转到此地址执行间接访问对象的方法。所以多态是有一点点运行时开销的。类的成员方法的引用限定符是 C++11 中引入的一种新特性,用于指定成员方法的参数是左值引用还是右值引用。二师兄:多态的是通过父类的指针或引用指向子类的对象实现的。

2023-06-08 23:16:19 77

原创 C++面试八股文:C++中,函数的参数应该传值还是传引用?

二师兄:因为传值相当于拷贝构造,当变量的类型尺寸大于16字节时,拷贝构造所消耗的时间大于解引用。如果是入参,主要考虑参数类型的大小,来决定传值还是传引用。一般参数类型大小大于16字节(64位操作系统)时,传引用效果要好于传值。但是在C++中不推荐使用指针,因为指针需要判断是否位空,而引用则无需担心。好了,今日份面试到这里就结束了,小伙伴们,对于今天二师兄的面试,能打几分呢?需要注意的是,这些约定可能因编译器和平台的不同而不同。面试官:C++中,函数的参数应该传值还是传引用?面试官:好的,回去等通知吧。

2023-06-07 22:27:56 122

原创 C++面试八股文:static和const的关键字有哪些用法?

关键字主要用在以下三个方面:1.用在全局作用域,修饰的变量或者函数为静态的,限制在本文件内使用。3.内类修饰成员函数和成员变量,此函数或变量由类持有,而非类的对象持有。修饰的函数是否在编译器求值要取决于传入的参数是不是编译器确定的,这属于元编程的范畴。需要注意的是,consteval和constinit关键字是C++20引入的, 小伙伴们可以在编译时加入。当使用consteval修饰函数和变量时,如果不能在编译时求值,则编译错误。关键字只能用于静态变量的初始化,不能用于动态变量的初始化。

2023-06-06 22:53:24 67

原创 C++面试八股文:如何在堆上和栈上分配一块内存?

而new一般伴随三个动作,向操作系统申请一块内存,并执行类型的默认构造函数,然后返回类的指针。我们都知道new和delete成对出现,new[]和delete[]也是成对出现,那么我想问,如果使用new[]创建的对象用delete释放了会发生什么?我们都知道C++中的内存管理是一个比较麻烦的事情,现在有个需求,需要在程序中记录主动申请的内存和主动释放的内存,以确保没有发生内存泄漏。面试官:(笑)好吧,最后一个问题,咱们上面一直在讨论堆中的内存的分配和释放,请问一下,如果在栈上分配一块固定的内存?

2023-06-05 22:10:26 211

原创 C++面试八股文:struct、class和union有哪些区别?

当我们给us赋值1时,如果是大端,内存中的数据应该是这样的:0x00,0x01,这时候如果取uc的值,只能取到0x00。如果是小端,内存中的数据应该是0x01,0x00,此时取到的uc的值是0x01,返回1(true)。struct默认的成员是public的,而class的默认成员是private的。union实例的所有成员共享一块内存,这块内存的大小等同于union中成员尺寸最大的一个。对于一个IP地址,要么是IPV4要么是IPV6,IPV4和IPV6是互斥的,那么肯定有一块空间是被浪费了。

2023-06-04 17:51:15 47

原创 C++面试八股文:memset、memcpy和strcpy的区别是什么?

strcpy存在另一个问题,那就是源字符串的长度可能大于目标区域的长度,导致目标区域内存被超写,造成不可预知的错误。当然也可以通过使用strncpy函数传入目标区域的大小-1,如果源字符串的长度小于目标区域的大小-1,则全部拷贝,否则最多只拷贝目标长度-1的内容,保证目标字符串以0结尾。memcoy函数需要注意的点是,函数传入的源位置和目标位置不能有重叠,否则这种操作引发的结果无法预知。memset函数常用于POD类型对象的初始化,一般第二个参数都是0,第三个参数是这段内存的长度。

2023-06-03 23:20:43 115

原创 C++面试八股文:指针占用多少个字节?

指针在C语言中应用普遍,但是随着现代C++的普及,RAII、智能指针、容器的普遍运用,裸指针在C++项目中被使用的却越来越少。但是指针是一个非常基础的概念,是C和C++的基石之一,必须掌握它的所有特性。因此,在编写程序时,需要注意指针的大小和操作系统的位宽,以确保程序的正确性。在32位系统中,指针通常占用4个字节,而在64位系统中,指针通常占用8个字节。这是因为在32位系统中,内存地址可以用32位二进制数表示,而在64位系统中,内存地址可以用64位二进制数表示。面试官:好的,回去等通知吧。

2023-06-03 00:07:05 123

原创 C++面试八股文:C++中指针、引用、解引用和取地址有什么不同?

总的来说,指针和引用都可以用来访问和修改变量的值,但它们的语法和语义有所不同。解引用和取地址是指针和引用的基本操作,它们可以用来访问和修改变量的值以及传递变量的地址给函数。指针可以通过&运算符获取变量的地址,也可以通过*运算符解引用指针来访问该地址处的值。引用可以看作是指针的一种简化形式,它不需要使用*运算符来解引用,也不需要使用&运算符来获取地址。小二:当然不行,虽然底层实现可能是常量指针,但它只是对象的别名,行为和对象的行为一致。小二:可以,取到的地址和对引用绑定的对象取到的地址相同。

2023-06-01 18:18:26 267

原创 C++面试八股文:C和C++有哪些区别?

小二:C是面向过程的编程语言,而C++不仅是面向过程的编程语言,还是面向对象的编程语言。小二:C++号称是C的超集,其实大部分时间这句话是对的,但还有一些细微的点C++是不能兼容C语言的,如balabalabala...实际上这句话是有问题的,严格的说,C语言和C++有很多交集,这部分交集占了C语言的大部分,占了C++的小部分。如C语言中的main函数的返回值可以是任何类型,但在C++中main函数的返回值只能是int类型。如空结构体在C语言中的大小是0,而在C++中的大小是1。小二:C++是C的超集。

2023-06-01 15:56:29 100

空空如也

空空如也

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

TA关注的人

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