企业网站的推广阶段北京网站开发品牌
web/
2025/10/5 2:48:01/
文章来源:
企业网站的推广阶段,北京网站开发品牌,怎么制作悬赏平台app,wordpress快速配图目录 面向过程思想和面向对象思想类的定义引入类的关键字类定义的两种方式类的访问限定符类的作用域类大小的计算封装 this指针类的6个默认成员函数构造函数初步理解构造函数深入理解构造函数初始化列表单参数构造函数引发的隐式类型转换 析构函数拷贝构造函数赋值运算符重载运… 目录 面向过程思想和面向对象思想类的定义引入类的关键字类定义的两种方式类的访问限定符类的作用域类大小的计算封装 this指针类的6个默认成员函数构造函数初步理解构造函数深入理解构造函数初始化列表单参数构造函数引发的隐式类型转换 析构函数拷贝构造函数赋值运算符重载运算符重载赋值运算符重载 取地址及const取地址操作符重载const成员函数取地址及const取地址操作符重载 static成员友元友元函数友元类 内部类什么是类与对象 面向过程思想和面向对象思想
c作为一门高级语言相较于c语言其引入了类和对象的概念是一门面向对象的语言而c语言是一门面向过程的语言。c语言作为一门面向过程的语言面对问题更关注解决问题的过程将问题拆解为一个个小步骤逐个解决。而c作为一门面向对象的语言面对问题时更注重问题的对象靠对象之间的交互解决问题。
类的定义
class _jiunian
{
public://……
protected://……
private://……
};class是定义类的关键字_jiunian是类的名字凭喜好自己取大括号中可以定义函数变量自定义类型。可以通过类访问限定符限制成员的访问条件。注意末尾分号不能省
引入类的关键字
引入类最为常见的关键字是class和struct两者的区别是struct定义的类默认情况下不使用访问限定符成员是公有的public即类的内部和类的外部都可以访问而class定义的类默认情况下成员是私有的private兼容c语言即只有类的内部才可以访问。
类定义的两种方式
常见的类的定义方式有两种一种是所有的成员全部完整定义在类的内部一种是对于成员函数这种代码量较大的部分采用声明定义分离只将声明留在类中再将类定义在头文件中。对于函数的定义我们将其定义在源文件中引入头文件到源文件中就行。这样做的好处就是类中的函数一行一个找接口方便而不是一个函数一大行找个函数接口鼠标滚边天找不到。所以成员函数声明定义分离的定义方式是比较推荐的。
类的访问限定符
访问限定符可以限制成员的访问条件。访问限定符的效果持续到下一个访问限定符出现位置为止如果之后没有访问限定符出现就持续到结束位置。访问限定符有三种分别是public、protected、private。public的效果是使成员变为公有即类内类外都可以访问。protected和private则可以使成员变为私有即只有类内可以访问类外不可以访问。而其两者之间也有区别当存在继承关系时子类可以访问父类中的protected标识的成员而不能访问父类中的private表示的成员。
类的作用域
c中引入了作用域的概念所有类的成员都得在类的作用域中。如果出现之前所说的函数的声明和定义分离的情况那么在类外定义函数时有加上作用域解析符表明是哪个类域的。
类大小的计算
类的成员有很多种但其实实际存储的只有类的成员变量。成员变量又称为类的属性成员函数又称为类的方法。由这样的名字我们就能明白因为对于同类型的类来说它们之间的不同只有类的属性也就是成员变量。类的方法也就是成员函数都是相同的相同的东西反复存储未免浪费空间所以这些成员函数实际存在公共的代码段即使定义多个相同类型的类成员函数也就只有一组供这些类共同使用。刨去成员函数对于这些类所占用的空间的计算方法与c语言中的结构体一样这里不做过多赘述。需要注意的是类的定义不会占用空间只用真正定义了类类型的变量类才能真正实体化。即使是空类也是会占用一个字节的空间来占位的。
封装
类实际上就是对对象封装的结果通过封装隐藏对象的属性以及细节控制对外开放的接口数量与对象进行交互。
this指针
class _jiunian
{
public:_jiunian(int a, int b, int c){_a a;_b b;_c c;}void reset(int a, int b, int c){_a a;_b b;_c c;}
private:int _a;int _b;int _c;
};int main()
{_jiunian x(1, 1, 1), y(2, 2, 2);x.reset(1, 2, 3);y.reset(1, 2, 3);return 0;
}正如笔者之前所说的一样类的成员函数只保存一份且存在公共代码段所以无论是x.reset还是y.reset调用的都是同一个函数那对于只传了数值而没有传递地址的函数是怎么识别要操作的类是哪一个的呢实际上c编译器给每一个非静态成员函数隐式传递了一个this指针这个指针是 类类型*const 类型即指向类类型变量其本身不能改变的指针变量。需要注意的是this指针不能被赋值因为被const修饰了this指针只能再成员函数内部使用写在除此之外的任何地方都会报错this本身也是c中的一个关键字不能将变量起这个名字this指针不会存在对象中this指针在类的成员函数被调用时由编译器自动识别传参this指针在现在的编译器优化下为达到最佳速度一般是存在寄存器中因为要反复调用使用空指针调用类的成员函数时this指针也可以为空不过这时函数中倘若出现对this指针解引用的操作就会报错在成员函数中对this指针解引用的操作可以将this省略直接写成员的名字。
类的6个默认成员函数
在类中有6个如果我们没有显式定义编译器也会自动生成的函数。这些成员函数称为默认函数。
构造函数
class jiunian
{
public:jiunian(){_a 1;_b 1;_c 1;}jiunian(int a, int b, int c){_a a;_b b;_c c;}
private:int _a;int _b;int _c;
};int main()
{jiunian x;//不可以写成jiunian x()这样会被当成函数的声明jiunian y(1, 2, 3);return 0;
}初步理解构造函数
构造函数是一个特殊的成员函数他直接由类名来命名且没有返回值它在创建类类型对象时被编译器自动调用保证每个成员变量都有一个合适的初始值构造函数本身并不会起到构造对象的作用构造对象是由系统开辟空间构造的构造函数本身只起到对类类型对象的成员变量进行初始化的作用。构造函数本身也支持函数重载。较为常见的是重载两个函数一个无参一个有参这样可以在类类型对象创建时选择自定义对象的成员变量初始值也可以直接创建对象这时对象的成员变量就是设置好的默认值当然我们也可以直接通过全缺省函数完成这一操作。当我们没有显式创建构造函数时编译器会默认自己生成一个默认构造函数以供创建类类型对象时使用编译器生成的构造函数是默认构造函数默认构造函数是指在没有任何参数时会调用的函数无参构造函数或者全缺省构造函数都是默认构造函数并不是只有编译器自己生成的才叫默认构造函数默认构造函数只能有一个。那么说到这里既然编译器自己会生成默认构造构造函数我们是不是就不用自己显式定义了呢其实不是虽然编译器会自己生成但它也不会智慧到会猜得到我们想要将成员变量设置成什么数所以我们还是有需要手动设置的情况。具体来说编译器自动生成的默认构造函数不会对内置类型c自带的类型像int、double、short这种进行初始化不初始化就是随机值因为编译器自己也不会知道究竟要初始化成什么才好这只能由我们自己来设置。但倘若是自定义类型编译器生成的默认构造函数会自动调用自定义类型自己的构造函数来进行初始化毕竟只能靠这个来初始化编译器不会有任何质疑。当然内置类型不会默认初始化我们又懒得显式定义时我们可以在声明变量时加上缺省值这样不用显式定义也能完成对内置类型的初始化。总的来说如果一个类中的成员变量都是自定义类我们就不用自己写构造函数编译器自己生成的就能解决若一个类中的成员变量中有内置类型就需要我们自己写构造函数或者给出缺省值来解决。
深入理解构造函数
通过初步理解构造函数其用法我们已经了然于胸但对于构造函数本质的理解还是不够的。
初始化列表
我们在构造函数的函数名和参数的后面可以加上一个冒号后面是以逗号分隔的成员变量列表每个变量后面可以加上括号括号中是想要初始化的值通过初始化列表。我们也能完成对类的成员变量的初始化操作。
class jiunian
{
public:jiunian():_a(2),_b(2),_c(2)//初始化列表{_a 1;_a 2;_b 1;_c 1;}jiunian(int a, int b, int c){_a a;_b b;_c c;}
private:int _a 0;int _b 0;int _c 0;
};int main()
{jiunian x;return 0;
}又是初始化列表又是构造函数又是缺省值的只是初始化一下类却有这么多种方法倘若我既设置缺省值又给出初始化列表在构造函数中又给出赋值最后函数会如何处理呢。跑一下代码后我们会发现最后类中的成员变量的值是以类中的赋值为准。这是为什么呢事实上构造函数内的初始化并不是真正意义上的初始化它只是一种赋值进一步说是给成员变量赋初值就像我上面的代码中一样我可以对同一个变量多次赋初值倘若是初始化这种操作是不行的因为变量只能初始化一次。而初始化列表才是真正的初始化倘若我们在初始化列表对同一个变量多次初始化编译器便会报错。而缺省值所要真正传递给的对象其实是初始化列表在我们没有给出初始化列表的值会接受缺省值进行初始化。下面对类类型对象创建时构成进行详细解释。在类类型对象创建时会优先使用构造函数的初始化列表对类类型对象进行初始化若有成员变量没有在初始化列表中出现就会将改成员变量的缺省值传给初始化列表进行初始化如果有缺省值的情况下。如果没有在初始化列表中出现也没有缺省值这时就会分为两种情况进行处理一种是该变量是内置类型编译器不会对内置类型进行初始化一种是它是自定义类型编译器会自动调用它的构造函数进行初始化。这时我们就明白对于内置类型和自定义类型的处理在初始化列表阶段就已经完成。在初始化列表结束之后才会进入构造函数内部对成员变量进行初赋值所以成员变量的初始值最终是由构造函数的内部决定的构造函数内部没有初赋值就看初始化列表初始化列表没有就看缺省值都没有那就是随机值了。 初始化列表的注意事项 1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次) 2. 类中包含以下成员必须放在初始化列表位置进行初始化 1引用成员变量 2const成员变量 3自定义类型成员(且该类没有默认构造函数时) 我们可以发现在初始化列表中对于自定义成员来说有默认构造函数不写的话会自己调用没默认构造函数则必须要写总的来说自定义成员必定会在初始化列表完成初始化。对于成员变量能在初始化列表初始化就尽量在初始化列表初始化。因为对于内置类型来说在构造函数内部初赋值实际上是先初始化再赋值因为即使编译器在初始化列表对没有显式初始化的内置成员变量不会进行初始化但它本质还是被创建了出来只不过是随机值。而如果在初始化列表初始化就是直接初始化效率更高。对于自定义类型就更是如此了自定义类型必定在初始化列表初始化这时再在构造函数内部赋值还要调用赋值函数占用不少资源很亏。所以综上尽量使用初始化列表但这并不是意味着我们可以无脑使用初始化列表有些情况初始化列表无法完成初始化比如
class jiunian
{
public:jiunian():_a(2),_b(2),_c(2){int* ptr (int*)calloc(4, sizeof(int));if (ptr nullptr){perror(jiunian:calloc);return;}_d ptr;}
private:int _a;int _b;int _c;int* _d;
};int main()
{jiunian x;return 0;
}这种要为指针动态开辟空间初始化的成员变量就不能直接用初始化列表初始化当然这只是一种简单场景实际应用还会有个各种情况我们应该灵活应对。最后还有一个初始化列表的小坑要说就是初始化列表的初始化顺序并不是按照初始化列表的顺序来进行的是成员变量在类中的定义顺序来决定的。简单来说就是如果a变量在类中的定义位置在b变量之前那在初始化列表中即使b的初始化写在a之前也是a先初始化这点要注意避免出现以下问题
class shinku
{
public:shinku(){std::cout shinku std::endl;}
private:int _a;
};class jiunian
{
public:jiunian():_b(2),_a(_b),//_a先定义_b此时是随机值_a被定义成随机值_c(2){}
private:int _a;int _b;int _c;
};int main()
{jiunian x;return 0;
}单参数构造函数引发的隐式类型转换
在C语言中对内置类型之间的运算存在隐式类型转换在c中对于类类型对象也有这种操作比如以下的代码是可以跑的起来的
class jiunian
{
public:jiunian(int a){_a a;}
private:int _a;
};int main()
{jiunian x 1;return 0;
}上面这组代码能跑的起来就是因为1被隐式类型转化成了类类型对象再由转换而来的临时变量给x拷贝构造而来但编译器实际上并不是这么实现的因为这个临时变量由1隐式转换而来在拷贝构造给x后就被销毁了编译器会觉得很浪费直接优化成jiunian x(1),这样就只有一次调用构造函数节省资源虽然最终不是这样实现的但这种优化还是因为有隐式类型转换才得以存在。在c中的类只有当其构造函数可以只接受一个参数时才会引发隐式类型转换只有一个参数或者除了第一个参数外都有缺省值或者是全缺省毕竟要是可以接收多个参数的话只用一个数怎么也引发不了。如果想要防止这样的隐式类型转换可以在构造函数的前面加上explicit来限制这种隐式类型转换。
析构函数
class jiunian
{
public:jiunian(int a, int b):_a(a),_b(b){int* ptr (int*)calloc(4, sizeof(int));if (ptr nullptr){perror(jiunian:calloc);}_c ptr;}~jiunian(){_a 0;_b 0;free(_c);_c nullptr;}
private:int _a;int _b;int* _c;
};int main()
{jiunian x(1, 1);return 0;
}析构函数在对象销毁时调用析构函数没有返回值名字与类名一致但前面要加上~以和构造函数区分析构函数没有参数毕竟销毁类也不需要什么参数this指针还是要的但也不用自己写出。析构函数在类对象销毁时自动调用与构造函数一样虽然析构函数在函数销毁时调用但析构函数本身不会起到销毁对象的作用只会对类中的资源进行清理。析构函数在我们没有显式定义时编译器会自动生成自动生成的析构函数对于类中的内置类型对象不会进行处理因为内置类型在程序结束时由系统回收处理就行而对于自定义类型则会自动调用类的析构函数来进行资源清理。析构函数一般不用自己显示定义但当类的构造函数向系统申请了资源时务必要显示定义析构函数系统自动生成的析构函数不会识别释放你申请的资源不写会造成内存泄漏。
拷贝构造函数
class jiunian
{
public:jiunian(int a, int b):_a(a),_b(b){int* ptr (int*)calloc(4, sizeof(int));if (ptr nullptr){perror(jiunian_i_i:calloc);return;}_c ptr;}jiunian(jiunian x){std::cout jiunian std::endl;_a x._a;_b x._b;int* ptr (int*)calloc(4, sizeof(int));if (ptr nullptr){perror(jiunian_j:calloc);return;}_c ptr;memcpy(_c, x._c, sizeof(_c));}~jiunian(){_a 0;_b 0;free(_c);_c nullptr;}
private:int _a;int _b;int* _c;
};
拷贝构造函数刨去拷贝本质还是构造函数。拷贝构造函数是构造函数的一种重载形式。拷贝函数被用于用已有的类类型对象创建新的类类型对象时由编译器自动调用使用。
jiunian x(1, 1);
jiunian y(x);
jiunian y x;使用这两种方法都可以对y进行拷贝初始化第一种是直接传参调用一种是用一个类类型对象初始化另一个。此外我们还应注意的是事实上在c中在函数传参时参数是会被拷贝一份传进函数中的这些局部变量会在函数结束时销毁所以类在函数传参时也会调用拷贝构造函数这也是为什么我们不能在拷贝函数传参时直接传值调用的原因如果对拷贝构造函数传值调用就会先去调用拷贝构造然后因为要传值所以又会调用这会引发无穷递归没完没了了所以不行。拷贝函数如果我们不去显式定义编译器会自动生成。编译器自动生成的拷贝构造是浅拷贝又称值拷贝会对拷贝的对象按存储空间逐字节。这样的拷贝方式对大部分情况都很合适所以大部分情况拷贝函数可以不用写但是老样子对于需要向系统申请空间的类拷贝函数需要自己写不然会造成两个同类型的类中的指针指向同一块空间程序结束类类型对象销毁时重复释放同一块空间造成报错。
赋值运算符重载
运算符重载
class jiunian
{
public:jiunian(int a, int b):_a(a),_b(b){}jiunian(jiunian x){std::cout jiunian std::endl;_a x._a;_b x._b;}bool operator(jiunian y){if (_a y._a _b y._b){return true;}return false;}~jiunian(){_a 0;_b 0;}
private:int _a;int _b;
};int main()
{jiunian x(1, 1);jiunian y(2, 2);if (x y)std::cout work std::endl;return 0;
}c为了增加类相关的代码的可读性引入了运算符重载运算符重载本质是一种特殊的函数关键字是operator接要重载的运算符注意中间没有空格。函数定义的格式是返回值类型 operator操作符(参数列表)。对于运算符重载函数需要注意的是 1不能通过连接其他符号来创建新的操作符比如operator 2重载操作符必须有一个类类型参数 3用于内置类型的运算符其含义不能改变例如内置的整型不 能改变其含义 4作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this 5.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
对于运算符重载函数遵循以上规定就能对于大部分双目操作符进行定义但对于单目操作符或–这种前置后置有细微差别的操作符该怎么去定义呢 jiunian operator(){_a;_b;return *this;}jiunian operator(int){jiunian* const ret this;_a;_b;return *ret;}对于前置和后置想要实现他们之间细微的不同毫无疑问要靠函数重载但怎么重载成了问题毕竟函数名一样函数参数也一样参数还只有一个this指针根本没法重载这时c特别规定在后置函数的参数中加入int类型参数以跟前置区分这个int类型参数本身在函数中没有任何作用所以可以省略参数名。
赋值运算符重载 jiunian operator(jiunian y){if(this y){_a y._a;_b y._b;}return *this;}与运算符重载函数不同的是他是类的6个默认函数之一即使不自己显式定义编译器也会自己生成。因为是对已有的类类型对象进行操作不必考虑局部变量销毁产生野引用的问题放心大胆的将参数和返回值都设置成引用减少拷贝构造函数的调用节省资源。if语句检查是否是自己给自己赋值进一步优化函数。因为内置类型的运算符会返回赋值后的结果这里的重载也是同理。需要注意的是由于赋值运算符重载是类的6个默认构造函数所以如果要显式定义务必在类内定义其他的运算符重载是可以在类外实现成员变量访问冲突也可以通过友元函数解决因为如果在类外面定义即使将类成员变量设置成公有此时类内因为没有显示定义会默认生成一个这样在函数调用时会发生冲突所以不行。编译器默认生成的赋值重载函数与拷贝构造函数有些类似都是按存储空间逐字节拷贝因为两者本质都是拷贝但拷贝构造是先初始化再拷贝赋值运算符重载是直接拷贝。既然是拷贝当然就要注意申请空间的情况务必显式定义处理这种情况。当我们看见类和类之间用相连也别一棒子打死说就是赋值运算符重载也可以是拷贝构造的。 jiunian x(1, 1);jiunian y(x);//拷贝构造jiunian z x;//拷贝构造jiunian a(1,1);jiunian b(2, 2);x y;//赋值运算符重载要看清是初始化还是赋值。
取地址及const取地址操作符重载
const成员函数
bool operator(jiunian y) const//const加在后面
{if (_a y._a _b y._b){return true;}return false;
}将const修饰的“成员函数”称之为const成员函数const修饰类成员函数实际修饰该成员函数隐含的this指针表明在该成员函数中不能对类的任何成员进行修改。关于const成员函数有几个经典问题 1const对象可以调用非const成员函数吗 2 非const对象可以调用const成员函数吗 3const成员函数内可以调用其它的非const成员函数吗 4非const成员函数内可以调用其它的const成员函数吗 对于const我们只需要明白一个原则权限可以平移缩小但不能放大。const对象意味着不能改变而非const成员函数可能会改变const对象所以不能调用。非const对象意味着可以改变但不变也不会有问题所以可以调用const成员函数。const成员函数意味着不能改变参数而非const成员函数可能会改变参数所以不能。非const成员函数意味着能改变参数但也可以不改变所以能。
取地址及const取地址操作符重载
Date* operator(){return this ;
比特就业课
}const Date* operator()const{return this ;}代码如上这两个默认成员函数很少会自己显式定义极特别情况才会比如别人想要取地址你调皮就不想让别人取一般编译器默认生成的就够用了。
static成员
class jiunian
{
public:jiunian(){_a 1;_b 1;}static int print(){std::cout print std::endl;}
private:int _a;int _b;static int _c;
};int jiunian::_c 0;int main()
{jiunian x;x.print();return 0;
}声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的 成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化。静态成员变量有以下特性 1静态成员为所有类对象所共享不属于某个具体的对象存放在静态区所以不在类里面定义 2静态成员变量必须在类外定义定义时不添加static关键字类中只是声明不能给缺省值因为不走初始化列表 3类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问静态成员函数可以不通过对象访问直接用作用域解析符访问 4静态成员函数没有隐藏的this指针不能访问任何非静态成员非要访问也能自己传 5静态成员也是类的成员受public、protected、private 访问限定符的限制初始化时可以破一次例
友元
using namespace std;class jiunian
{friend ostream operator(ostream _cout, const Date x);friend istream operator(istream _cin, Date x);
public:jiunian(){}private:int _a;int _b;int _c;
};
ostream operator(ostream _cout, const Date x)
{_cout d._a - d._b - d._c;return _cout;
}
istream operator(istream _cin, Date d)
{_cin d._a;_cin d._b;_cin d._c;return _cin;
}
int main()
{jiunian a;cin a;cout a endl;return 0;
}友元为类外的函数和类提供了一种访问类内部成员的方式但友元破坏了封装增加了耦合度所以友元不能频繁使用。
友元函数
友元函数可以直接访问声明过的类的私有和保护成员声明时记得加关键字friend友元函数定义在类外不能属于任何类。关于友元函数应该注意 1友元函数可访问类的私有和保护成员但不是类的成员函数 2友元函数不能用const修饰是指放在后面修饰this指针放在前面修饰返回值还是可以的 3友元函数可以在类定义的任何地方声明不受类访问限定符限制 4一个函数可以是多个类的友元函数 5友元函数的调用与普通函数的调用原理相同
友元类
和友元函数一样友元类可以直接访问声明过的类的私有和保护成员但是有以下几点需要注意 1友元关系是单向的不具有交换性。例如A是B的友元意味着A可以访问B的私有或者保护成员但不意味着反过来B是A的友元B也不能访问A的私有或者保护成员。 2友元关系不能传递.例如A是B的友元C是A的友元但C不是B的友元。 3友元关系也不能继承。
内部类
class jiunian
{
public:class thx{public:thx():_x(1){b 1;//不用作用域解析符也不用类类型对象直接访问}private:int _x;};
private:int _a;static int _b;
};int jiunian::_b 0;int main()
{jiunian m;jiunian::thx n;//作用域解析符,thx被包在jiunian的类域中了return 0;
}一个类定义在另一个类的内部这个类就叫内部类。内部类是一个独立的类不属于外部类无法通过外部类类型对象去访问内部类外部类类型对象的存储空间大小的计算也不会加上内部类外部类对内部类没有任何访问权限上的优越。但内部类是外部类的友元类内部类可以通过外部类的对象参数来访问外部类中的所有成员但是外部类不是内部类的友元。内部类有以下几点要注意 1内部类可以定义在外部类的public、protected、private都是可以的。 2注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名。 3sizeof(外部类)外部类和内部类没有任何关系。 4不能再内部类中定义外部类的类型对象会报错说不允许使用不完整的类型因为外部类的定义还没有结束编译器无法为内部类的外部类类型对象计算大小。 5定义内部类类型对象时不要忘记用作用域解析符。
什么是类与对象
类是面向对象编程中对具有相同属性和行为的一组事物的抽象描述它定义了数据成员属性和成员函数行为是创建对象的模板而对象是类的具体实例依据类的定义在内存中被创建拥有类所规定的属性和行为且每个对象的属性值可以不同能通过调用成员函数来执行特定操作。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87112.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!