c++的string一键介绍

 

前言:

这篇文章旨在帮助读者回忆如何使用string,并提醒注意事项。它不是一篇详细的功能介绍,而是一篇润色文章。

先展示重载函数,如果该函数一笔不可带过,就先展示英文原档(附带翻译),最后展示代码实现与举例

这里先提供c++官网相关链接:cplusplus.com/reference/string/

可以直接去看英文文档,也可以看本篇文章,但是更建议去看英文原档。

那么废话少说直接开始进行挨个介绍

Member functions:

1:std::basic_string::basic_string构造函数

进行初始化,c++的官方给了很多的初始化重载函数,也完美应对了我们不同的要求,看来c++祖师爷还是很细心的。 


 

下面举一个例子,使用每一个初始化方式 

#include<string>
#include<iostream>
using namespace std;int main()
{string s0("Initial string");// constructors used in the same order as described above:string s1;string s2(s0);string s3(s0, 8, 3);string s4("A character sequence", 6);string s5("Another character sequence");string s6(10, 'x');string s7a(10, 42);string s7b(s0.begin(), s0.begin() + 7);cout << "s1: " << s1 << "\ns2: " << s2 << "\ns3: " << s3;cout << "\ns4: " << s4 << "\ns5: " << s5 << "\ns6: " << s6;cout << "\ns7a: " << s7a << "\ns7b: " << s7b << '\n';return 0;
}

运行结果展示:

 2:std::basic_string::~basic_string析构函数

 析构函数官方没有给详细解释,应该走的是默认析构

那么这里展示我写的析构函数:

class string
{
public:~string(){delete[]_str;_str = nullptr;_size = _capacity = 0;}
private:char* _str;size_t _size;size_t _capacity;const static size_t npos = -1;//可以先忽略
};

 3:std::basic_string::operator=运算符=

官方给出了三个重载

第一个就是将string类型赋值给string类型

第二个是将字符串赋值给string类型

第三个是单个字符赋值给string类型

需要注意的是自己实现的时候要进行深拷贝,而不是浅拷贝

string& operator= (const string& str)
{if (this != &str){char* tmp= new char[str._capacity + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_size = str._size;_capacity = str._capacity;}
}

也可以借助swap函数进行优化:

	string& operator=(string tmp){swap(tmp);return *this;}

Iterators:

1:begin

 

iterator begin();

const_iterator begin() const;

官方解释就是Returns an iterator pointing to the first character of the string.

也就是返回一个指向第一个字符的string迭代器

反之对应的就是end,最后一个

2:end

 

iterator end();

const_iterator end() const;

 需要注意的是返回的是最后一个元素的下一个,不是最后一个,

就比如说如果string里面存的是 abcdef  那么end返回的是指向f下一个位置的迭代器。

注意点:

If the object is an empty string, this function returns the same as basic_string::begin.

如果对象为空字符串,则此函数返回与 basic_string::begin 相同的值。

这一点也就解决了如果string为空的情况

3:rbegin与rend 

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

reverse_iterator rend();
const_reverse_iterator rend() const;

 两个是对应的begin与end其中r为reverse逆置的意思

返回的都是字符串反向端的反向迭代器。

举例:

#include<string>
#include<iostream>
using namespace std;
int main()
{string str("now step live...");string::reverse_iterator it = str.rbegin();while (it != str.rend()){cout << *it;++it;}cout << endl;return 0;
}

打印效果:

4: cbegin与cend

const_iterator cbegin() const noexcept;

const_iterator cend() const noexcept;

 分别是Return const_iterator to begin  回到const_iterator开始

Return const_iterator to end                返回const_iterator到结束

 5:同样有crbegin与crend

const_reverse_iterator crbegin() const noexcept;

const_reverse_iterator crend() const noexcept;

 Capacity

1:size

size_type size() const;

 

返回string的大小

这个还是很好理解的,直接展示代码的自己实现

        size_t size() const{return _size;} 

2:length

这个函数跟size()其实没什么区别,而且用的很少,他出现的原因就是因为string是早期的c++产物,当时c++还不成熟,就造出了他,到后面也不能删除导致的。

3:max_size

size_type max_size() const;

 

这个就是返回string中size最大是多大

4:resize 

void resize (size_type n);
void resize (size_type n, charT c);

这个函数就是修改size的值,意在修改string的大小

 在函数也对应了两种特殊情况;第1种便是n的值比原来的size大,还有1种是比原来的小;

那么我们看看原档对于此的解决

If n is smaller than the current string length, the current value is shortened to its first n character, removing the characters beyond the nth.
如果 n 小于当前字符串长度,则当前值将缩短为其第一个 n 字符,从而删除第 n 个字符之外的字符。

If n is greater than the current string length, the current content is extended by inserting at the end as many characters as needed to reach a size of n. If c is specified, the new elements are initialized as copies of c, otherwise, they are value-initialized characters (null characters).
如果 n 大于当前字符串长度,则通过在末尾插入所需数量的字符来扩展当前内容,以达到 n 的大小。如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符(空字符)。

那么展示代码的实现:

		void resize(size_t n, char ch = '\0') {if (n > _size){reserve(n);//修改capacity大小为n后面回介绍for (size_t i = _size; i < n; i++){_str[i] = ch;}_str[n] = '\0';_size = n;}else {_str[n] = '\0';_size = n;}}

5:capacity

size_type capacity() const;

 该函数与size差不多,但这个还是是返回capacity的大小

       size_type capacity() const;{return _capacity;} 

6:reserve

void reserve (size_type n = 0);

 该函数是在修改capacity的大小,但是它只能放大不可以缩小。主要是因为如果缩小的话,代价太过于大,而且会产生很多不必要的麻烦。所以该函数只支持放大容量不可以缩小。

代码实现也有很多小细节:主要是末尾的'\0';

		void reserve(size_t n = 0){if (n > _capacity){char* tmp = new char[n + 1];//第n+1个位置存的是'/0'strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}

7:clear

void clear();

该函数也是人如其名,进行了清理clear,所以函数的实现还是比较好理解的。这里不再进行多说那么这些进行代码展示

Erases the contents of the basic_string, which becomes an empty string (with a length of 0 characters).
擦除basic_string的内容,该内容将变为空字符串( 0 字符长度)。

		void clear(){_str[0] = '\0';_size = 0;}

 8:empty

bool empty() const;

Returns whether the basic_string is empty (i.e. whether its length is 0).

这句话的详细意思就是如果该对象为空就返回1(true)如果对象不为空就返回零(false);

		bool empty() const{return _size == 0;}

9:shrink_to_fit

void shrink_to_fit();

 这个函数官方的介绍还有比较少的,但是实际上实现起来还是比较麻烦的。

它的作用主要是修改修改capacity大小,使其正好达到满的效果,也就是第capacity的位置就为第size的值。

其实该函数的实现不单单是调用了reserve

因为如果原本的capacity很大很大,但是我们又删了很多,那么这时候在shrink_to_fit,就会很浪费空间,原码是这样实现的,会先拷贝,后修改,不是单单的reserve

 最后展示一下官方举例

Element access: 元素访问

1: operator[]

reference operator[] (size_type pos);

const_reference operator[] (size_type pos) const;

 实现起来还是比较简单的这里就不在说了。

		char& operator[] (size_t pos){assert(pos < _size);return _str[pos];}const char& operator[] (size_t pos) const{assert(pos < _size);return _str[pos];}

2:at

reference at (size_type pos);

const_reference at (size_type pos) const;

at与operator[]效果完全差不多,但是有一点不同的是operator在出错时会掏出他的七匹狼给你直接报告,但是at出错时只是会警告,所以at使用的很少 

3:back与front

charT& back();const charT& back() const;

charT& front();const charT& front() const;

 返回第一个与最后一个

Modifiers: 修饰 符

1:operator+=

 

string (1)

basic_string& operator+= (const basic_string& str);

c-string (2)

basic_string& operator+= (const charT* s);

character (3)

basic_string& operator+= (charT c);

这个重载运算符那就不需要多的介绍了吧,我们已经很详细了,就是+= 

string& operator+=(const char* str)
{append(str);return *this;
}
string& operator+=(char ch)
{push_back(ch);return *this;
}

2:append

string (1)

basic_string& append (const basic_string& str);

substring (2)

basic_string& append (const basic_string& str, size_type subpos, size_type sublen);

c-string (3)

basic_string& append (const charT* s);

buffer (4)

basic_string& append (const charT* s, size_type n);

fill (5)

basic_string& append (size_type n, charT c);

range (6)

template <class InputIterator>   basic_string& append (InputIterator first, InputIterator last);

initializer list(7)

basic_string& append (initializer_list<charT> il);

可以看到C++祖师爷写了很多它的重载,也完美了应对了我们的所有要求。而且这还是C++11如果C++14的话那么就会更多.那么这里就介绍几个我们经常用到的。

函数的作用

向后添加想要对象,俗话说时延长

代码实现 :

string& append(size_t n, char c)
{if (_size + n > _capacity){reserve(_size + n);}size_t begin = _size;_size += n;for (size_t i = begin; i < _size; i++){_str[i] = c;}return *this;
}
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;
}
string& append(const string& str)
{append(str._str);return *this;
}

3:push_back与pop_back

void push_back (charT c);

void pop_back();

向后添加,与后删,这个就没有好多说的。

4:assign

string (1)

basic_string& assign (const basic_string& str);

substring (2)

basic_string& assign (const basic_string& str, size_type subpos, size_type sublen);

c-string (3)

basic_string& assign (const charT* s);

buffer (4)

basic_string& assign (const charT* s, size_type n);

fill (5)

basic_string& assign (size_type n, charT c);

range (6)

template <class InputIterator>   basic_string& assign (InputIterator first, InputIterator last);

initializer list(7)

basic_string& assign (initializer_list<charT> il);

move (8)

basic_string& assign (basic_string&& str) noexcept;

 

Assign content to string
将内容分配给字符串

Assigns a new value to the string, replacing its current contents.
为字符串分配一个新值,替换其当前内容。

(1) string (1) 字符串

Copies str. 副本 str.

(2) substring (2) 子字符串

Copies the portion of str that begins at the character position subpos and spans sublen characters (or until the end of str, if either str is too short or if sublen is basic_string::npos).
复制从字符位置 subpos 开始并跨越 sublen 字符的 str 部分(如果任一 str 太短或 sublen 为 basic_string::npos,则复制到 str 的末尾)。

(3) c-string (3) C-串

Copies the null-terminated character sequence (C-string) pointed by s.

复制以 s 为结尾的以 null 结尾的字符序列(C 字符串)。


The length is determined by calling traits_type::length(s).
长度由调用 traits_type::length(s) 确定。

(4) buffer (4) 缓冲器

Copies the first n characters from the array of characters pointed by s.
从 s 指向的字符数组中复制前 n 个字符。

(5) fill (5)填写

Replaces the current value by n consecutive copies of character c.
将当前值替换为字符 c 的 n 个连续副本。

(6) range (6) 范围

Copies the sequence of characters in the range [first,last), in the same order.
以相同的顺序复制 范围 [first,last) 中的字符序列。

(7) initializer list (7) 初始值设定项列表

Copies each of the characters in il, in the same order.
以相同的顺序复制 il 中的每个字符。

(8) move (8) 移动

Acquires the contents of str.
获取 str 的内容。
str is left in an unspecified but valid state.
str 保持未指定但有效的状态。

简单举例演示

#include<string>
#include<iostream>
using namespace std;
int main()
{string str("now step live...");string s;s.assign(str);auto it = s.begin();while (it != s.end()){cout << *it;++it;}cout << endl;return 0;
}

 运行效果:

 5:insert

string (1)

basic_string& insert (size_type pos, const basic_string& str);

substring (2)

basic_string& insert (size_type pos, const basic_string& str,                      size_type subpos, size_type sublen);

c-string (3)

basic_string& insert (size_type pos, const charT* s);

buffer (4)

basic_string& insert (size_type pos, const charT* s, size_type n);

fill (5)

basic_string& insert (size_type pos,   size_type n, charT c);     iterator insert (const_iterator p, size_type n, charT c);

single character (6)

     iterator insert (const_iterator p, charT c);

range (7)

template <class InputIterator>     iterator insert (iterator p, InputIterator first, InputIterator last);

initializer list (8)

basic_string& insert (const_iterator p, initializer_list<charT> il);

同样c++给了很多重载,insert就是在pos的前面插入 

代码实现:

string& insert(size_t pos, char ch)
{assert(pos <  _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;_size++;return *this;
}
string& insert(size_t pos, const char* str)
{assert(pos < _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end1 = _size + 1;size_t end2 = _size + len + 1;while (end1 > pos){_str[end2 - 1] = _str[end1 - 1];end2--;end1--;}_str[_size + len] = '\0';strncpy(_str + pos, str, len);_size+=len;return *this;
}

6:erase

sequence (1)

basic_string& erase (size_type pos = 0, size_type len = npos);

character (2)

     iterator erase (const_iterator p);

range (3)

     iterator erase (const_iterator first, const_iterator last);

 

需要注意的是这里的len是删除的个数,而npos是-1; 

返回值就是删除后指向的位置(为了使其迭代器不失效)

 7:replace

就是代替,这个用的不是很多,就不想insert与erase那样全部展示了,就举个简单的例子吧

 

#include<string>
#include<iostream>
using namespace std;
int main()
{string str("now step live...");string s1("pqpq2");str.replace(2, 2, s1);//把str从第二个开始把两个字符,替换为s1,auto it = str.begin();while (it != str.end()){cout << *it;++it;}cout << endl;return 0;
}

效果打印演示:

通常可以与find一同使用,统一替换某个特殊字符转化为想要的结果。 

8:c_str

获取 C 字符串等效值

通常可以应用输出流

代码实现

        const char* c_str() const{return _str;}

9:copy

这个就是复制拷贝,很好理解 

同样第一个参数就是要拷贝的对象,第二个是要拷贝的个数,第三个参数就是被拷贝的对象第一个拷贝的位置。

举例: 

 10:find

string (1)

size_type find (const basic_string& str, size_type pos = 0) const noexcept;

c-string (2)

size_type find (const charT* s, size_type pos = 0) const;

buffer (3)

size_type find (const charT* s, size_type pos, size_type n) const;

character (4)

size_type find (charT c, size_type pos = 0) const noexcept;

 

代码实现:

		size_t find(char ch, size_t pos = 0){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}

简单的运用举例:

#include<string>
#include<iostream>
using namespace std;
void test()
{string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");// 协议// 域名// 资源名string sub1, sub2, sub3;size_t i1 = s3.find(':');if (i1 != -1)sub1 = s3.substr(0, i1);//可以理解为赋值从第0到第i1个elsecout << "没有找到i1" << endl;size_t i2 = s3.find('/', i1 + 3);if (i2 != -1)sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));elsecout << "没有找到i2" << endl;sub3 = s3.substr(i2 + 1);cout << sub1 << endl;cout << sub2 << endl;cout << sub3 << endl;
}
int main()
{test();return 0;
}

 运行展示:

 11:substr

asic_string substr (size_type pos = 0, size_type len = npos) const;

该函数就是生成其子字符串

代码实现 :

		string substr(size_t pos, size_t len = npos){string s;size_t end = pos + len;if (len == npos || pos + len >= _size) // 有多少取多少{len = _size - pos;end = _size;}s.reserve(len);for (size_t i = pos; i < end; i++){s += _str[i];}return s;}



 

那么到这里这篇文章就结束了,本篇文章不是详细介绍,而是帮助回忆!!!

 看到这了就给自己点个赞把

 

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

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

相关文章

教你搞一个比较简单的计时和进度条装饰器

教你搞一个比较简单的计时和进度条装饰器 什么是装饰器为啥要用装饰器呢&#xff1f;上代码&#xff01;如何使用装饰器效果 什么是装饰器 装饰器的英文是&#xff1a;Decorator。装修的英文是&#xff1a;Decoration。顾名思义就是我们要用装饰器在函数func()上搞点儿事儿&am…

原生APP开发和Flutter开发的比较

原生APP开发和Flutter开发各有优缺点&#xff0c;适用于不同的场景和需求。下面是两者的详细比较&#xff0c;从开发语言、性能、开发效率、维护和更新、社区和支持等多个方面进行分析。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。…

htb_BoardLight

信息收集 nmap -sSVC 10.10.11.11开放80端口&#xff0c;将boardlight.htb写入/etc/hosts 同步进行子域名和目录扫描 子域名扫不到 这个目录扫描很奇怪哈&#xff0c;明明访问80端口有页面&#xff0c;就是扫不出来 直接浏览器访问80端口&#xff0c;四处看看&#xff0c;发…

7、架构-架构的安全性

即使只限定在“软件架构设计”这个语境下&#xff0c;系统安全仍然是一 个很大的话题。我们谈论的计算机系统安全&#xff0c;不仅仅是指“防御系统 被黑客攻击”这样狭隘的安全&#xff0c;还至少应包括&#xff08;不限于&#xff09;以下这些问 题的具体解决方案。 认证&am…

自定义数据集训练 Yolo V10

上次介绍了Yolo 推理&#xff0c;本文我们将使用自己的数据集训练 Yolo V10&#xff0c;训练过程简单&#xff1a; 首先准备数据集&#xff0c;包括图片、标注训练推理 数据集准备 本次采用的数据集为内部数据&#xff0c;标注方法为 VOC 格式&#xff0c;首先我们需要建 VO…

智能辅导班技术架构:运用人工智能提升教育领域的学习效果

在教育领域&#xff0c;随着人工智能的发展&#xff0c;智能辅导班逐渐成为一种趋势。它利用先进的技术和创新的教学方法&#xff0c;为学生提供个性化的学习体验和全方位的辅导服务。本文将介绍智能辅导班的技术架构&#xff0c;探讨如何运用人工智能来提升学习效果。 ### 1. …

力扣之链表专题

1. (LeetCode-21)合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 …

微服务架构-微服务实施

目录 一、概述 二、微服务拆分 2.1 概述 2.2 拆分原则 2.3 拆分方法 2.3.1 以数据为维度进行拆分 2.3.2 按照使用场景拆分 2.3.3 重要和非重要的拆分 2.3.4 变和不变的拆分 三、微服务通信 3.1 概述 3.2 微服务通信方式选择 3.3 微服务编排 3.4 API接口设计 3.5 …

「计网」网络初识

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;计网 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 网络初识 &#x1f349;IP 地址 & 端口号&#x1f349;网络协议&#x1f34c;TCP/IP 网络协议 &#x1f349;封装和分用&#x1f349…

乡村振兴与乡村旅游创新:创新乡村旅游产品,提升旅游服务水平,打造特色乡村旅游品牌,助力美丽乡村建设

目录 一、引言 二、乡村旅游产品的创新 &#xff08;一&#xff09;挖掘乡村特色资源 &#xff08;二&#xff09;注重产品体验性 &#xff08;三&#xff09;创新旅游产品形态 三、旅游服务水平的提升 &#xff08;一&#xff09;加强基础设施建设 &#xff08;二&…

如何上传模型素材创建3D漫游作品?

一、进入3D空间漫游互动工具编辑器 进入720云官网-点击“开始创作”-选择3D空间漫游-进入到作品创建页面。 二、上传模型及素材&#xff0c;创建生成3D空间漫游模型 1.创建3D空间作品&#xff1a;您可以选择新建空白作品或使用720云提供的预设空间模板&#xff0c;本篇主要介绍…

[手游] Florence逝去的爱弗洛伦斯

图片处理工具箱Hummingbird : Hummingbird使用智能压缩技术来减少文件的大小&#xff0c;支持&#xff1a;jpg、png、webp、svg、gif、gif、css、js、html、mp4、mov&#xff0c;可以设置压缩的同时等比例缩放图片或视频的尺寸。可以拖放文件夹压缩&#xff0c;一次最多可处理1…

【vue3 + Echarts 】中国地图省市区下钻,并返回上级

实现效果如果&#xff1a; echarts版本&#xff1a; 地图数据来源&#xff1a;阿里云数据可视化平台 代码 <template><div class"mapWrapper"><a-button type"primary" click"goBack">返回上级</a-button><div…

一步步实现知乎热榜采集:Scala与Sttp库的应用

背景 在大数据时代&#xff0c;网络爬虫技术发挥着不可或缺的作用。它不仅能够帮助我们快速地获取互联网上的信息&#xff0c;还能处理和分析这些数据&#xff0c;为我们提供深刻的洞察。知乎&#xff0c;作为中国领先的问答社区&#xff0c;汇聚了各行各业的专家和广大用户的…

【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名

【LeetCode刷题】Day 14 题目1&#xff1a;153.寻找旋转排序数组中的最小值思路分析&#xff1a;思路1&#xff1a;二分查找&#xff1a;以A为参照思路2&#xff1a;二分查找&#xff0c;以D为参照 题目2&#xff1a;LCR 173.点名思路分析&#xff1a;思路1&#xff1a;遍历查找…

(2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X

Lumina-T2X: Transforming Text into Any Modality, Resolution, and Duration via Flow-based Large Diffusion Transformers 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 …

使用Streamlit和MistralAI创建AI聊天机器人应用

大家好&#xff0c;创建交互式和用户友好型的应用程序通常需要复杂的框架和耗时的开发过程。Streamlit是一个Python库&#xff0c;它简化了以数据为重点的网络应用程序的创建过程&#xff0c;使开发人员和数据科学家能够快速将他们的想法转化为交互式仪表盘和原型。本文将介绍使…

『 Linux 』文件系统

文章目录 磁盘构造磁盘抽象化 磁盘的寻址方式磁盘控制器磁盘数据传输文件系统Inode数据块(Data Blocks)超级块(SuperBlock)块组描述符(Group Descriptor) 磁盘构造 磁盘内部构造由磁头臂,磁头,主轴,盘片,盘面,磁道,柱面,扇区构成; 磁头臂&#xff1a;控制磁头的移动,可以精确地…

vs2019 QT UI 添加新成员或者控件代码不提示问题解决方法

右键点击头文件&#xff0c;添加ui的头文件 添加现有项 找到uic目录的头文件 打开ui,QtWidgetsApplication2.ui,进行测试 修改一个名字&#xff1a; 重点&#xff1a; 设置一个布局&#xff1a; 点击生成解决方案&#xff1a; 以后每次添加控件后&#xff0c;记得点击保存 这样…

flink 作业报日志类冲突的解决方案

文章目录 背景思考初步解决方案深入思考下终极解决方案总结 背景 实时作业在页面提交任务后&#xff0c;报NoSuchMethodException 方法&#xff0c;看了下是关于log4j的&#xff0c;首先是作业升级了很多依赖的版本&#xff0c;其次flink 也升级 到了1.19版本 思考 打的Jar有…