C++基础-类和对象(下)

文章目录

  • 前言
  • 一、构造深入
    • 1.初始化列表
    • 2.隐式类型转换
      • 1.隐式类型转换
      • 2.explicit
    • 3.委托构造
  • 二、类的静态成员
    • 1.静态成员声明
    • 2.静态成员定义
    • 3.静态成员特性
  • 三、重载运算符和类型转化
    • 1.关系及算数运算符重载
    • 2.递增递减运算符重载及如何区分
    • 3.赋值运算符重载
    • 4.重载输入输出运算符
      • 1.重载输入运算符
      • 2.重载输出运算符
    • 5.函数匹配与重载运算符
  • 四、类的其他细节
    • 1.const成员函数
    • 2.内部类
      • 内部类的特性
    • 3.匿名对象
    • 4.拷贝时编译器的优化
    • 5.对封装的进一步认识
  • 总结

前言

对于任何C++的类来说,构造函数都是其中重要的组成部分,我们已经在上篇介绍了类的一些基础知识,这篇我们将继续了解类的一些其他功能。并对之前讲解过的内容(如构造函数)进行一些更加深入的讨论。

一、构造深入

1.初始化列表

有时我们可以忽略数据成员初始化和赋值的差异,但并非总是这样。如果成员是const或者是引用的话,我们必须将其初始化。类似的,当成员属于某种类类型且该类没有定义默认构造函数时,也必须将这个成员进行初始化。

class STU
{
public://错误,my_id和rid必须要被初始化STU(int i){id = i;//正确my_id = i;//错误:不可以给const赋值rid = i;//错误:rid没有被初始化}
private:int id;const int my_id;int& rid;
};

在这里插入图片描述
我们初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值!!!

	STU(int i):id(i),my_id(i),rid(id){}//显示的初始化引用和const成员

上面就是我们的初始化列表。
初始化列表: 以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
注意:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 引用成员变量,const成员变量和自定义类型成员(且该类没有默认构造函数时)必须放在初始化列表位置进行初始化
  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
class A
{
public:A(int i):a(i),b(a){}void print(){cout << "A:" << a << "  B:" << b << endl;}
private:int b;int a;
};
int main()
{A a(10);a.print();return 0;
}

上面代码的结果是什么样的呢?是10,10吗?
在这里插入图片描述
从运行结果可以看出,显然不是上面的10,10。为什么有随机值呢?
根据我们上面的第四条,我们先声明的b,初始列表会按照声明顺序进行赋值,和初始化列表中的先后次序无关,所以还是先对b进行赋值,b的值来自a,此时a为随机值,所以b现在也为随机值,为b赋值后轮到a,此时a的值来自i,所以a是10.

2.隐式类型转换

1.隐式类型转换

在C++中,我们的内置类型存在隐式转换,同样的,我们的类存在这样的隐式转换。如果构造函数只接受一个实参,则它实际上定义了此类型的隐式转换机制。有时我们把这种构造函数称为转换构造函数。

class A
{
public:A(int i = 0):_a(i){cout << "A()" << endl;}void print(){cout << "A:" << _a << endl;}
private:int _a;int _b;
};
int main()
{A a1(10);// 用一个整形变量给A类对象赋值,实际编译器背后会用100构造一个无名对象,最后用无名对象给a2对象进行赋值A a2 = 100;a1.print();a2.print();return 0;
}

在这里插入图片描述
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。

2.explicit

当我们想要抑制构造函数的类型转换时,我们需要explicit关键字

class A
{
public:explicit A(int i = 0) :_a(i){cout << "A()" << endl;}void print(){cout << "A:" << _a << endl;}
private:int _a;int _b;
};
int main()
{A a1(10);A a2 = 100;a1.print();a2.print();return 0;
}

在这里插入图片描述
explicit关键字只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以多个实参的构造函数无需指定为explicit,且只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应该重复。
当我们使用explicit关键字声明构造函数时,它将只能以直接初始化的形式使用。而且,编译器将不会在自动转换过程中使用该构造函数。

3.委托构造

在C++11新标准扩展了构造函数初始值的功能,使得我们可以定义所谓的委托构造函数。一个委托构造也有一个成员初始值的列表和一个函数体。
在委托构造函数内,成员初始值列表只有唯一的入口,就是类名本身,和其他成员初始值一样,类名后面紧跟圆括号括起来的参数列表,参数列表必须与类中的另外一个构造函数匹配。

class A
{
public://非委托构造,使用对应的实参进行初始化成员A(int a,int b,int c) :_a(a),_b(b),_c(c){cout << "A(int a,int b,int c)" << endl;}//委托构造A() : A(0,0,0) {cout << "A()" << endl;}//委托构造A(int a) : A(a,0,0) {cout << "A(int a)" << endl;}void print(){cout << "_a:" << _a << "  _b" << _b << "  _c" << _c << endl;}
private:int _a;int _b;int _c;
};
int main()
{A a1;A a2(10);a1.print();a2.print();return 0;
}

在这里插入图片描述
这里默认构造把它委托给三个参数的构造函数,它无需执行任务,我们从结果可以看出,当三个参数的执行过后才执行默认构造,一个参数的构造函数也它委托给三个参数的构造函数。受委托的构造函数会执行,然后控制权才会还给委托者的函数体。

二、类的静态成员

有时间我们的类需要它的一些成员函数与类本身直接相关,而不是与类的各个对象保持关联,这时间我们就可以把这个类的成员声明为静态的。

1.静态成员声明

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。

class A
{
public:A();static void pid();//静态成员函数
private:int _a;int _b;static int id;//静态成员变量
};
int A::id = 0;//静态成员变量只能在类外进行初始化。

类的静态成员存在于任何对象之外,对象中不包含任何与静态成员有关数据,从上一篇的类的大小我们也可以看到。因此,只能有一个id对象,且它被所有的A对象共享。
类似的,静态成员函数也不可以与任何对象绑定在一起,他们不包含this指针,作为结果,静态成员函数不可以声明为const的。而且我们也不可以在static函数体内使用this指针。

2.静态成员定义

在类的外部定义静态成员时,不能重复static关键字,该关键字只能出现在类内部的声明语句中。
一般来说,我们不能在类的内部初始化静态成员。相反的,必须在类的外部定义和初始化每个静态成员。和其他对象一样,一个静态数据成员只能定义一次。想要确保对象只定义一次,最好的办法是把静态数据成员的定义与其他非内联函数的定义放在同一个文件中。

3.静态成员特性

静态成员特性如下:

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

三、重载运算符和类型转化

上一章我们已经浅浅的认识了一下重载,这次让我们更加深入的学习重载吧。

1.关系及算数运算符重载

class A
{
public:A(int a = 0,int b = 0):_a(a),_b(b){}A operator+(const A& a1)//重载加号{A tmp;tmp._a = _a + a1._a;tmp._b = _b + a1._b;return tmp;}A& operator+=(const A& a1)//重载加等号{_a += a1._a;_b += a1._b;return *this;}void print(){cout << "_a:" << _a << "  _b:" << _b << endl;}
private:int _a;int _b;
};
int main()
{A a1(10, 20);A a2(30, 40);cout << "a1:";a1.print();cout << "a2:";a2.print();A a3 = a1 + a2;a1 += a2;cout << "a1:";a1.print();cout << "a3:";a3.print();
}

在这里插入图片描述
问什么调用operator+=来替换operator+更加有效呢?
因为+需要创建一个临时对象,而+=只有一个对象。

class A
{
public:A(int a = 0, int b = 0) :_a(a), _b(b) {}bool operator<(const A& a1)//重载小于号{return (_a < a1._a) && (_b < a1._b);}
private:int _a;int _b;
};
int main()
{A a1(10, 20);A a2(30, 40);if (a1 < a2){cout << "TRUE" << endl;}return 0;
}

在这里插入图片描述
这里我们使用的是对象中每一个对象成员均小于另一个对象才返回真。

2.递增递减运算符重载及如何区分

class A
{
public:A(int a = 0, int b = 0) :_a(a), _b(b) {}A& operator++()//重载前置++{_a++;_b++;return *this;}A& operator++(int)//重载后置++{A tmp = *this;_a++;_b++;return tmp;}void print(){cout << "_a:" << _a << "  _b:" << _b << endl;}
private:int _a;int _b;
};
int main()
{A a1(10, 20);A a2(30, 40);++a1;//前置++a1.print();A a3 = a2++;a3.print();return 0;
}

在这里插入图片描述
我们发现后置++多了一个参数,但我们并没有进行传参,编译器是如何知道我们要调用后置的呢?
后置的版本接受一个额外的(不被使用)int型参数,当我们使用后置运算符时,编译器会提供一个值为0的实参。 尽管从语法上来说我们后置函数会使用这个额外的形参,但在实际过程中通常不会这样做。这个形参的唯一作用就是区分前置版本还是后置版本,而不是真的参加运算。
大家可以自己实现一下前置- -和后置- -;

3.赋值运算符重载

class A
{
public:A(int a = 0, int b = 0) :_a(a), _b(b) {}A& operator=(const A& a1){_a = a1._a;_b = a1._b;return *this;}void print(){cout << "_a:" << _a << "  _b:" << _b << endl;}
private:int _a;int _b;
};
int main()
{A a1(10, 20);A a2;a2 = a1;a2.print();return 0;
}

在这里插入图片描述
这个我们上一篇已经见到过了。需要注意的是:赋值运算符都必须定义为成员函数。

4.重载输入输出运算符

1.重载输入运算符

通常情况下,输出运算符的第一个形参是一个非常量ostream对象的引用。之所以ostream是非常量是因为向流写入内容会改变其状态:而该形参是引用是因为我们无法直接复制一个ostream对象.

class A
{
public:A(int a = 0, int b = 0) :_a(a), _b(b) {}ostream& operator<<(ostream &os){os << "_a:" << _a << "  _b:" << _b << endl;return os;}
private:int _a;int _b;
};
int main()
{A a1(10, 20);//cout << a1;a1 << cout;return 0;
}

在这里插入图片描述
在这里插入图片描述
我们发现打印的方式和我们正常使用的方式不一样,这又是为什么呢?
因为我们调用类的函数的一个参数都是隐含的this指针,所以我们的调用要我们的类对象在前。
为了与标准库兼容,我们的输入输出运算符必须是普通的非成员函数,而不是类的成员函数。

class A
{
public:friend ostream& operator<<(ostream& os, A& a1);A(int a = 0, int b = 0) :_a(a), _b(b) {}
private:int _a;int _b;
};
ostream& operator<<(ostream& os, A& a1)
{os << "_a:" << a1._a << "  _b:" << a1._b << endl;return os;
}
int main()
{A a1(10, 20);A a2(20, 40);cout << a1 << a2 << endl;return 0;
}

在这里插入图片描述
我们要保证返回的类型也为ostream的引用,这样可以做到连续输出。

2.重载输出运算符

class A
{
public:friend istream& operator>>(istream& is, A& a1);friend ostream& operator<<(ostream& os, A& a1);A(int a = 0, int b = 0) :_a(a), _b(b) {}
private:int _a;int _b;
};
istream& operator>>(istream& is, A& a1)
{int a = 0;int b = 0;is >> a >> b;if (is)//检查输入是否成功{a1._a = a;a1._b = b;}else{a1 = A();//输入失败,对象被赋予默认状态}return is;
}
ostream& operator<<(ostream& os, A& a1)
{os << "_a:" << a1._a << "  _b:" << a1._b << endl;return os;
}
int main()
{A a1;cin >> a1;cout << a1 << endl;return 0;
}

在这里插入图片描述
输入运算符必须处理输入可能失败的情况,而输出运算符不需要。当我们的读取操作发生错误时,输入运算符应该负责从错误中恢复。

5.函数匹配与重载运算符

重载运算符也是重载函数。因此,通用的函数匹配规则也同样适用于判断在给定的表达式中到底应该使用内置类型运算符还是重载的运算符。
当我们调用一个命名的函数时,具有该名字的成员函数和非成员函数彼此不会重载,这是因为我们用来调用命名函数的语法形式对于成员函数和非成员函数来说是不同的。当我们通过类类型的对象(或者该对象的指针及引用)进行函数调用时,只考虑该类的成员函数。而当我们在表达式中使用重载的运算符时,无法判断正在使用的是成员函数还是非成员函数,因此二者都应该在考虑的范围内。
表达式中的运算符的候选函数集既应该包括成员函数,也应该包括非成员函数。我们不可以对一个类既提供了转化目标是算数类型的转化,也提供重载的运算符,这样会产生二义性问题。

四、类的其他细节

1.const成员函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

class A
{
public:A(int a = 0, int b = 0) :_a(a), _b(b) {}void print() const{cout << "_a:" << _a << "  _b:" << _b << endl;}
private:int _a;int _b;
};
int main()
{A a1;return 0;
}

由于我们不想对该对象进行修改,但我们又不可以在形参上面加入const,所以我们把const加在形参列表后面
在这里插入图片描述
编译器会对const进行上述的处理。

2.内部类

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:
内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

内部类的特性

内部类的特性如下:

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。
class A
{
public:class B {public:void print(const A& a){cout << "A::_a:" << a._a << "   A::_b:" << a._b << "   B::id:" << id << endl;}private:int id = 0;};
private:int _a = 0;int _b = 0;
};
int main()
{A::B b;b.print(A());cout <<"A的大小:" << sizeof(A) << endl;return 0;
}

在这里插入图片描述

3.匿名对象

class A
{
public:A(int a = 0):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{A aa1;cout << "到匿名对象了" << endl;A();// 我们可以这么定义匿名对象,匿名对象的特点不用取名字// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数cout << "匿名对象结束了" << endl;A aa2(2);return 0;
}

在这里插入图片描述
注意:匿名对象的生命周期只有这一行

4.拷贝时编译器的优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,下面就让我们看看编译器的优化吧。

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void fun1(A aa){}
A fun2()
{A aa;return aa;
}
int main()
{//传值传参A aa1;fun1(aa1);cout << endl;//传值返回fun2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造fun1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造fun1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = fun2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = fun2();cout << endl;return 0;
}

在这里插入图片描述
我们要尽量构造函数和拷贝放进一个句子中,这样可以少量的提升我们的效率。

5.对封装的进一步认识

类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象。
在这里插入图片描述

总结

到这里我们的类已经入门了。想要对类进一步了解我们还要学习继承和多态,进一步的学习需要我们熟练的使用我们现在所学的类的内容,所以下一步我们会进入容器的学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/135322.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据Flink(七十七):SQL窗口的Over Windows

文章目录 SQL窗口的Over Windows 一、​​​​​​​时间区间聚合

选开源还是闭源?2区16天录用!国人友好,期刊指标优秀

在本期分享前&#xff0c;鉴于近期多位作者的困惑&#xff0c;小编想分享开源期刊和闭源期刊的含义、区别、以及如何选择&#xff1f; 概念区别 “开源期刊”即开放存取。简称OA&#xff08;Open access)&#xff0c;是指将学术信息放在互联网上供所有人共享&#xff0c;不需…

我学编程全靠B站了,真香(第一期)

你好&#xff0c;我是Martin。 我是就读于B站大学2020届的Martin同学&#xff0c;反正我学习计算机真的是全靠 B 站了。 我是个刷视频狂魔&#xff0c;B站收藏夹里也收藏了很多编程类视频&#xff0c; 比如C/C、Go语言、操作系统、数据结构和算法、计算机网络、数据库、Pyth…

一阶低通滤波器滞后补偿算法

一阶低通滤波器的推导过程和双线性变换算法请查看下面文章链接: PLC算法系列之数字低通滤波器(离散化方法:双线性变换)_双线性离散化_RXXW_Dor的博客-CSDN博客PLC信号处理系列之一阶低通(RC)滤波器算法_RXXW_Dor的博客-CSDN博客_rc滤波电路的优缺点1、先看看RC滤波的优缺点…

【码银送书第七期】七本考研书籍

八九月的朋友圈刮起了一股晒通知书潮&#xff0c;频频有大佬晒出“研究生入学通知书”&#xff0c;看着让人既羡慕又焦虑。果然应了那句老话——比你优秀的人&#xff0c;还比你努力。 心里痒痒&#xff0c;想考研的技术人儿~别再犹豫了。小编咨询了一大波上岸的大佬&#xff…

Go并发可视化解释 – select语句

上周&#xff0c;我发布了一篇关于如何直观解释Golang中通道&#xff08;Channel&#xff09;的文章。如果你对通道仍然感到困惑&#xff0c;请先查看那篇文章。 Go并发可视化解释 — Channel 作为一个快速复习&#xff1a;Partier、Candier和Stringer经营着一家咖啡店。Partie…

php高级 TP+Redis实现发布订阅和消息推送案例实战

Redis 的发布-订阅模型是一种消息通信模式&#xff0c;它允许客户端之间通过特定的频道进行通信。在这种模型中&#xff0c;有些客户端负责发布消息&#xff08;发布者&#xff09;&#xff0c;而其他客户端则订阅它们感兴趣的频道并接收这些消息&#xff08;订阅者&#xff09…

games101 作业2

题目 光栅化一个三角形 1. 创建三角形的 2 维 bounding box。 2. 遍历此 bounding box 内的所有像素&#xff08;使用其整数索引&#xff09;。然后&#xff0c;使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。 3. 如果在内部&#xff0c;则将其位置处的插值深度值 (…

股东分析报表程序思路

股东分析报表程序思路 (PS. 因为数据是要保密的&#xff0c;所以就不截图全部了) 目录内容 这是统计的所有维度(禁止外传的水印原本文件中是没有的&#xff0c;也是我自己加的) 公司投资原本是用两份需要对比的股东数据excel通过一个软件生成一份股东分析报表&#xff0c;不…

自然语言处理应用(二):自然语言推断

自然语言推断 自然语言推断&#xff08;Natural Language Inference&#xff09;是指通过对自然语言文本进行逻辑推理和推断&#xff0c;判断两个句子之间的关系&#xff0c;通常包括三种关系&#xff1a;蕴含&#xff08;entailment&#xff09;、矛盾&#xff08;contradict…

工商银行潍坊分行党建RPA机器人项目解析

01 案例背景&#xff1a;银行业掀起引入RPA加速实现数字化转型的浪潮 近年来&#xff0c;金融科技的蓬勃发展极大促进了银行的业务创新&#xff0c;新技术、新业态层出不穷。随着银行业务和科技的融合逐步落实&#xff0c;银行业务正朝着线上化、智能化转变。科技赋能的转型范…

如何在 Excel 中求平方根

需要在 Excel 中求一个数字的平方根吗&#xff1f;使用几个内置的 Excel 函数和公式可以轻松计算平方根。在本分步指南中&#xff0c;您将学习在 Excel 中计算平方根的 5 种不同方法&#xff0c;包括使用 SQRT 函数、POWER 函数、指数公式、VBA 代码和 Power Query。跟随教程&a…

华为云云耀云服务器L实例评测|基于L实例使用Docker部署MySQL服务并连接MySQL—phpMyAdmin管理工具

文章目录 一、云耀云服务器产品优势1、智能不卡顿2、价优随心用3、上手更简单4、管理更省心 二、远程连接云耀云服务器L实例三、安装Docker、docker-compse1、docker安装2、docker-compose安装 四、方法① 使用Docker安装部署MySQL服务五、方法② 使用docker-compse安装部署MyS…

Maven常见面试题总结

Maven简介 Maven 是一个项目管理和整合工具。Maven 包含了一个项目对象模型 (Project Object Model)&#xff0c;一组标准集合&#xff0c;一个项目生命周期管理系统(Project Lifecycle Management System)&#xff0c;一个依赖管理系统(Dependency Management System)&#x…

C语言天花板——指针(初阶)

&#x1f320;&#x1f320;&#x1f320; 大家在刚刚接触C语言的时候就肯定听说过&#xff0c;指针的重要性以及难度等级&#xff0c;以至于经常“谈虎色变”&#xff0c;但是今天我来带大家走进指针的奇妙世界。&#x1f387;&#x1f387;&#x1f387; 一、什么是指针&…

WebGL 正确处理对象前后的关系——隐藏面消除(深度测试)/ 深度冲突

目录 前言 验证WebGL处理对象前后关系的规则——后绘制的图形覆盖先绘制的图形 隐藏面消除&#xff08;深度测试&#xff09; 开启隐藏面消除功能&#xff0c;需要遵循以下两步&#xff1a; 1.开启隐藏面消除功能。 gl.enable&#xff08;&#xff09;函数规范 2.在绘制…

2023常用的原型设计软件推荐

美观易操作的产品原型可以帮助团队构建积极的用户体验&#xff0c;帮助团队理解产品交互逻辑。 因此&#xff0c;可互动、易修改的产品原型设计对产品的点击率和回访率具有重要意义。 选择专业的产品原型设计工具&#xff0c;可以为团队和企业带来高效的产品设计体验。本文选…

ddtrace 系列篇之 dd-trace-java 项目编译

dd-trace-java 是 Datadog 开源的 java APM 框架&#xff0c;本文主要讲解如何编译 dd-trace-java 项目。 环境准备 JDK 编译环境(三个都要&#xff1a;jdk8\jdk11\jdk17) Gradle 8 Maven 3.9 (需要 15G 以上的存储空间存放依赖) Git >2 (低于会出现一想不到的异常&#xf…

Linux JAVA环境的搭建tomcat的部署(含多实例)

tomcat tomcat是Apache软件基金会项目中的一个核心项目由 Apache、Sun 和其他一些公司及个人共同开发而成。tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器. tomcat的组成 tomcat用于支持Java Servlet和JSP。它是一个重要的We…

flutter开发实战-自定义长按TextField输入框剪切、复制、选择全部菜单AdaptiveTextSelectionToolba样式UI效果

flutter开发实战-自定义长按TextField输入框剪切、复制、选择全部菜单样式UI效果 在开发过程中&#xff0c;需要长按TextField输入框cut、copy设置为中文“复制、粘贴”&#xff0c;我首先查看了TextField中的源码&#xff0c;看到了ToolbarOptions、AdaptiveTextSelectionToo…