STL-string类的使用及其模拟实现

string类的使用

       string 类是 C++ 标准库提供的用于处理字符串的类,它相比 C 语言中的字符串处理函数更为高级、灵活和安全。

       string 类提供了丰富的成员函数和操作符,用于处理字符串的拼接、查找、替换、截取、插入等操作。string 类自动处理字符串的内存分配和释放,不会像 C 语言中的字符数组那样容易出现缓冲区溢出等安全问题。使用 string 类能够有效避免许多与字符数组相关的安全漏洞。string 类是 C++ 标准模板库(STL)中的一部分,学习 string 类也是为了更好地理解和使用 STL 的其他组件,如向量、链表、映射等。

string类对象的常见构造:

string类对象的容量接口:

string类对象的元素访问:

string类对象的Iterators(迭代器)接口:

string类对象的修改:

npos
     npos 是 std::string 类中的一个静态常量成员,用于表示无效或未找到的位置。它是一个特殊的 std::string::size_type 类型的常量,通常被定义为 std::string::npos,其值在不同的编译器和实现中可能不同,但通常被设为 -1 或一个非常大的值,用于表示在字符串中未找到指定的子串或字符。

    npos 主要用于字符串查找操作,比如在使用 find()、rfind()等成员函数时,当查找失败或没有找到指定的子串或字符时,这些函数通常会返回 std::string::npos 来表示无效的位置。

注意:npos 的值是一个非常大的无符号整数,因此在比较 std::string::size_type 类型的值时,应使用无符号类型的比较方式,避免可能出现的错误。比如使用 pos != std::string::npos 来判断是否找到了指定的子串或字符。

string类的模拟实现

      std::string 是一个类模板,模拟实现它需要深入理解类和对象的概念,包括构造函数、析构函数、成员函数、成员变量等。通过实现一个类似 std::string 的类,你可以更好地理解类的设计和使用。

class string
{
public:
    typedef char* iterator;
    typedef const char* const_iterator;
    iterator begin()
    {
        return _str;
    }
    iterator end() {
        return _str + _size;
    }
    iterator begin() const
    {
        return _str;
    }
    iterator end() const{
        return _str + _size;
    }
    /*string() 
        :_str(new char[1])
        ,_size(0)
        ,_capacity(0)
    {
        _str[0] = '\0';
    }*/
  
    string(const char* str = "")
        :_size(strlen(str))//不包括'\0'的大小
    {
        _capacity = _size==0 ? 3 : _size;
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }
    string(const string& s)
        :_size(s._size)
        ,_capacity(s._capacity)
    {
        _str = new char[s._capacity+1];
        strcpy(_str, s._str);
    }
    // 传统写法
    string& operator=(const string& s) {
        if (this != &s) {
            /*delete[] _str;
            _str = new char[s._capacity + 1];
            strcpy(_str, s._str);
            _size = s._size;
            _capacity = s._capacity;
            return *this;*/

            char* tmp = new char[s._capacity + 1];
            strcpy(tmp, s._str);
            delete[] _str;
            _str = tmp;

            _size = s._size;
            _capacity = s._capacity;
        }
    }
    // 现代写法 
    void swap(string& tmp)
    {
        ::swap(_str, tmp._str);
        ::swap(_size, tmp._size);
        ::swap(_capacity, tmp._capacity);
    }
    string(const string& s)
        :_str(nullptr)
        , _size(0)
        , _capacity(0)
    {
        string tmp(s._str);
        swap(tmp);
    }
    ~string() {
        delete[] _str;
        _str = nullptr;
        _size = _capacity = 0;
    }
    string to_string(int value)
    {
        bool flag = true;
        if (value < 0)
        {
            flag = false;
            value = 0 - value;
        }

        string str;
        while (value > 0)
        {
            int x = value % 10;
            value /= 10;

            str += ('0' + x);
        }

        if (flag == false)
        {
            str += '-';
        }

        std::reverse(str.begin(), str.end());
        return str;
    }
    const char* c_str() {
        return _str;
    }
    const char& operator[](size_t pos) const {
        assert(pos < _size);
        return _str[pos];
    }
    char& operator[](size_t pos) {
        assert(pos < _size);
        return _str[pos];
    }
    size_t size() const{
        return _size;
    }
    size_t capacity() const {
        return _capacity;
    }

    //string& operator=(const string& s)
    //{
    //    if (this != &s)
    //    {
    //        //string tmp(s._str);
    //        string tmp(s);
    //        swap(tmp); // this->swap(tmp);
    //    }

    //    return *this;
    //}
    string& operator=(const string& s)
    {
        if (this != &s) //防止自己给自己赋值
        {
            string tmp(s); //用s拷贝构造出对象tmp
            swap(tmp); //交换这两个对象
        }
        return *this; //返回左值(支持连续赋值)
    }
    //不修改成员变量数据的函数,最好都加上const
    bool operator>(const string& s) const {
        return strcmp(_str, s._str) > 0;
    }
    bool operator==(const string& s) const {
        return strcmp(_str, s._str) == 0;
    }
    bool operator>=(const string& s) const {
        return *this > s || *this == s;
    }
    bool operator<(const string& s) const {
        //return strcmp(_str, s._str) < 0;
        return !(*this >= s);
    }
    bool operator<=(const string& s) const {
        //return *this < s || *this == s;
        return !(*this > s);
    }
    //扩容
    void reserve(size_t n) {
        if (n > _capacity) {
            char* tmp = new char[n + 1];
            strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _capacity = n;
        }
    }
    void resize(size_t n, char ch = '\0') {
        if (n <= _size) {
            //删除数据--保留前n个
            _size = n;
            _str[_size] = '\0';
        }
        else {
            if (n > _capacity) {
                reserve(n);
            }
            size_t i = _size;
            while (i < n) {
                _str[i] = ch;
                ++i;
            }
            _size = n;
            _str[_size] = '\0';
        }
    }
    void push_back(char ch) {
        /*if (_size + 1 > _capacity) {
            reserve(_capacity * 2);
        }
        _str[_size] = ch;
        ++_size;
        _str[_size] = '\0';*/
        insert(_size, ch);
    }
    void append(const char* str) {
        //size_t len = strlen(str);
        //if (_size + len > _capacity) {
        //    reserve(_size + len);
        //}
        //strcpy(_str + _size, str);
        strcat(_str,str);
        //_size += len;
        insert(_size, str);
    }
    string& operator+=(char ch) {
        push_back(ch);
        return *this;
    }
    string& operator+=(const char* str) {
        append(str);
        return *this;
    }
    string& insert(size_t pos, char ch) {
        assert(pos <= _size);
        if (_size + 1 > _capacity) {
            reserve(2 * _capacity);
        }
        //int end = _size;
        //while (end >= (int)pos) {//有符号跟无符号类型比较会发生整型提升变成无符号类型
        //    _str[end + 1] = _str[end];
        //    --end;
        //}
        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 end = _size + len;
        while (end > pos+len-1) {
            _str[end] = _str[end - len];
            --end;
        }
        /*size_t end = _size;
        for (size_t i = 0; i < _size + 1; ++i) {
            _str[end] = _str[end - len];
            --end;
        }*/
        //拷贝插入
        strncpy(_str + pos, str, len);
        _size += len;
        return *this;
    }
    string& erase(size_t pos, size_t len = npos) {
        assert(pos < _size);
        if (len == npos || pos + len >= _size) {
            _str[pos] = '\0';
            _size = pos;
        }
        else {
            strcpy(_str + pos, _str + pos + len);
            _size -= len;
        }
        return *this;
    }
    //swap(s1,s2);
    //s1.swap(s2);比上面更高效
    void swap(string& s) {
        std::swap(_str, s._str);
        std::swap(_size, s._size);
        std::swap(_capacity, s._capacity);
    }
    size_t find(char ch,size_t pos=0) {
        assert(pos < _size);
        for (size_t i = pos; i < _size; ++i) {
            if (_str[i] == ch) {
                return i;
            }
        }
        return npos;
    }
    size_t find(const char* str, size_t pos = 0) {
        assert(pos < _size);
        char* p = strstr(_str + pos, str);
        if (p == nullptr) {
            return npos;
        }
        else {
            return p - str;
        }
    }
    void clear() {
        _str[0] = '\0';
        _size = 0;
    }
private:
    char* _str;
    size_t _size;
    size_t _capacity;

    //static const size_t npos;
    static const size_t npos = -1;

    //static const double npos = -1.0;//错误,只针对整型
};

//const size_t string::npos = -1;

ostream& operator<<(ostream& out, const string& s) {
    for (auto ch : s) {
        out << ch;
    }
    return out;
}
istream& operator>>(istream& in, string& s) {
    s.clear();
    char ch=in.get();
    char buff[128];
    size_t i = 0;
    while (ch != ' ' && ch != '\n') {
        buff[i++] = ch;
        if (i == 127) {
            buff[127] = '\0';
            s += buff;
            i = 0;
        }
        ch= in.get();
    }
    if (i !=0) {
        buff[i] = '\0';
        s += buff;
    }
    return in;
}

构造函数

//构造函数
    /*string() 
        :_str(new char[1])
        ,_size(0)
        ,_capacity(0)
    {
        _str[0] = '\0';
    }*/
    //string(const char* str=nulltr)不可以,strlen检测不到'\0'会崩
    //string(const char* str='\0')不可以,类型不匹配
    //string(const char* str="\0")可以,但是C语言规定常量字符串后面会有默认'\0'会造成歧义
    string(const char* str = "")
        :_size(strlen(str))//不包括'\0'的大小
    {
        _capacity = _size==0 ? 3 : _size;
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }

构造函数我们可以用无参和带参的构造,但是最好的方式是全缺省,const char* str = “” 是构造函数的默认参数。

拷贝构造

浅拷贝:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间。其中一个对象的改动会对另一个对象造成影响。
深拷贝:深拷贝是指源对象与拷贝对象互相独立。其中任何一个对象的改动不会对另外一个对象造成影响。

因为我们并不希望源对象与拷贝对象存在关系,所以我们需要实例化出新的对象再进行拷贝。

传统写法:

 string(const string& s)
        :_size(s._size)
        ,_capacity(s._capacity)
    {
        _str = new char[s._capacity+1];
        strcpy(_str, s._str);
    }

现代写法:

void swap(string& s) {
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capacity, s._capacity);
}

string(const string& s)

:_str(nullptr)

, _size(0)

, _capacity(0)

{

string tmp(s._str);

swap(tmp);

}

swap 函数的实现会交换当前对象和临时对象的内存空间资源,而临时对象会在析构时释放资源。

这种实现方式通过避免了不必要的内存拷贝,从而提高了拷贝构造函数的性能。

赋值运算符重载

传统写法:

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

            char* tmp = new char[s._capacity + 1];
            strcpy(tmp, s._str);
            delete[] _str;
            _str = tmp;

            _size = s._size;
            _capacity = s._capacity;
        }
    }

现代写法:

//现代写法2
string& operator=(const string& s)
{
    if (this != &s) //防止自己给自己赋值
    {
        string tmp(s); //用s拷贝构造出对象tmp
        swap(tmp); //交换这两个对象
    }
    return *this; //返回左值(支持连续赋值)
}

析构函数

~string() {
    delete[] _str;
    _str = nullptr;
    _size = _capacity = 0;
}

扩容

void reserve(size_t n) {
    if (n > _capacity) {
        char* tmp = new char[n + 1];
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

获取对应位置字符

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

改变字符串大小


void resize(size_t n, char ch = '\0') {
    if (n <= _size) {
        //删除数据--保留前n个
        _size = n;
        _str[_size] = '\0';
    }
    else {
        if (n > _capacity) {
            reserve(n);
        }
        size_t i = _size;
        while (i < n) {
            _str[i] = ch;
            ++i;
        }
        _size = n;
        _str[_size] = '\0';
    }
}

迭代器

iterator begin()
{
    return _str;
}
iterator end() {
    return _str + _size;
}
iterator begin() const
{
    return _str;
}
iterator end() const{
    return _str + _size;
}

数字转字符串


string to_string(int value)
    {
        bool flag = true;
        if (value < 0)
        {
            flag = false;
            value = 0 - value;
        }

        string str;
        while (value > 0)
        {
            int x = value % 10;
            value /= 10;

            str += ('0' + x);
        }

        if (flag == false)
        {
            str += '-';
        }

        std::reverse(str.begin(), str.end());
        return str;
    }

字符/字符串任意位置插入

string& insert(size_t pos, char ch) {
    assert(pos <= _size);
    if (_size + 1 > _capacity) {
        reserve(2 * _capacity);
    }
    //int end = _size;
    //while (end >= (int)pos) {//有符号跟无符号类型比较会发生整型提升变成无符号类型
    //    _str[end + 1] = _str[end];
    //    --end;
    //}
    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 end = _size + len;
    while (end > pos+len-1) {
        _str[end] = _str[end - len];
        --end;
    }
    /*size_t end = _size;
    for (size_t i = 0; i < _size + 1; ++i) {
        _str[end] = _str[end - len];
        --end;
    }*/
    //拷贝插入
    strncpy(_str + pos, str, len);
    _size += len;
    return *this;

字符/字符串的追加

void push_back(char ch) {
    /*if (_size + 1 > _capacity) {
        reserve(_capacity * 2);
    }
    _str[_size] = ch;
    ++_size;
    _str[_size] = '\0';*/
    insert(_size, ch);
}
void append(const char* str) {
    //size_t len = strlen(str);
    //if (_size + len > _capacity) {
    //    reserve(_size + len);
    //}
    //strcpy(_str + _size, str);
    strcat(_str,str);
    //_size += len;
    insert(_size, str);
}
string& operator+=(char ch) {
    push_back(ch);
    return *this;
}
string& operator+=(const char* str) {
    append(str);
    return *this;
}
}

从任意位置删除n个字符


string& erase(size_t pos, size_t len = npos) {
    assert(pos < _size);
    if (len == npos || pos + len >= _size) {
        _str[pos] = '\0';
        _size = pos;
    }
    else {
        strcpy(_str + pos, _str + pos + len);
        _size -= len;
    }
    return *this;
}

查找字符/字符串

size_t find(char ch,size_t pos=0) {
    assert(pos < _size);
    for (size_t i = pos; i < _size; ++i) {
        if (_str[i] == ch) {
            return i;
        }
    }
    return npos;
}
size_t find(const char* str, size_t pos = 0) {
    assert(pos < _size);
    char* p = strstr(_str + pos, str);
    if (p == nullptr) {
        return npos;
    }
    else {
        return p - str;
    }
}

将字符串置空

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

获取字符串

const char* c_str() {
    return _str;
}

比较运算符重载

bool operator>(const string& s) const {
    return strcmp(_str, s._str) > 0;
}
bool operator==(const string& s) const {
    return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const {
    return *this > s || *this == s;
}
bool operator<(const string& s) const {
    //return strcmp(_str, s._str) < 0;
    return !(*this >= s);
}
bool operator<=(const string& s) const {
    //return *this < s || *this == s;
    return !(*this > s);
}

流提取和流插入运算符重载

ostream& operator<<(ostream& out, const string& s) {
    for (auto ch : s) {
        out << ch;
    }
    return out;
}
istream& operator>>(istream& in, string& s) {
    s.clear();
    char ch=in.get();
    char buff[128];
    size_t i = 0;
    while (ch != ' ' && ch != '\n') {
        buff[i++] = ch;
        if (i == 127) {
            buff[127] = '\0';
            s += buff;
            i = 0;
        }
        ch= in.get();
    }
    if (i !=0) {
        buff[i] = '\0';
        s += buff;
    }
    return in;
}

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

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

相关文章

MySQL双层游标嵌套循环方法

文章目录 1、需求描述2、思路3、创建存储过程 1、需求描述 1、在项目中&#xff0c;需要将A表中主键id&#xff0c;逐个取出&#xff0c;作为条件&#xff0c;在B表中去逐一查询&#xff0c;将B表查询到的结果集&#xff08;A表B表关系&#xff1a;一对多&#xff09;&#xf…

在CentOS7中wget命令显示not found怎么解决

实验需要&#xff0c;在本机上安装了一个CentOS7虚拟机&#xff0c;运行wget命令时显示not found。按照网上的教程&#xff0c;使用命令sudo yum install wget&#xff0c;依然失败。 怎么回事呢&#xff1f;会不会是网络的原因呢&#xff1f;想到这&#xff0c;ping了一下www…

达梦(DM) SQL日期操作及分析函数

达梦DM SQL日期操作及分析函数 日期操作SYSDATEEXTRACT判断一年是否为闰年周的计算确定某月内第一个和最后一个周末某天的日期确定指定年份季度的开始日期和结束日期补充范围内丢失的值按照给定的时间单位查找使用日期的特殊部分比较记录 范围处理分析函数定位连续值的范围查找…

C++系列-输入输出

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” C输入和输出 我们都知道C语言的输出是用printf函数来实现的&#xff0c;那么C呢&#xff0c;它的实现逻辑是什么呢&#xff0c;让我们一起来看一下&#xff0c; #include<i…

docker入门级命令

基本概念 docker的连个基本概念&#xff1a;镜像、容器。 docker镜像可以理解为是存储docker安装包的地方&#xff0c;比如&#xff1a;mcr.microsoft.com/mssql/server:2017-latest是sqlserver的docker镜像。 可以通过docker pull命令拉取远程镜像到本地。比如&#xff1a;…

Linux系统安全及应用(1)

目录 一.账号安全控制 系统账号清理 二.密码安全控制 密码安全控制 三.命令历史限制 命令历史限制 四.限制su切换用户 1&#xff09;将信任的用户加入到wheel组中 2&#xff09;修改su的PAM认证配置文件 ​编辑五.PAM认证的构成 六.使用sudo机制提升权限…

IDEA离线安装插件

1、下载地址 https://plugins.jetbrains.com/idea 如果去其他编辑器&#xff0c;点击下拉&#xff0c;选择即可。 2.搜索 在输入框输入关键词&#xff0c;按照提示选择即可&#xff0c;点击搜索按钮&#xff0c;查看结果。 3、选择版本 按照自己的版本选择合适的版本 4、安…

linux的SSH(远程登录)服务

目录 1.SSH远程管理 1.1 SSH的概述 1.2 OpenSSH服务器 1.3 SSH端口、配置文件 2.服务端重要配置 3.登录验证方式 3.1 密码验证 3.2 密钥对验证 4.使用 SSH 客户端程序 4.1 ssh 远程登录 4.2 scp 远程复制 4.3 sftp文件传输 5.创建使用密钥对 6.TCP Wrappers访问控…

CLIP论文笔记:Learning Transferable Visual Models From Natural Language Supervision

导语 会议&#xff1a;ICML 2021链接&#xff1a;https://proceedings.mlr.press/v139/radford21a/radford21a.pdf 当前的计算机视觉系统通常只能识别预先设定的对象类别&#xff0c;这限制了它们的广泛应用。为了突破这一局限&#xff0c;本文探索了一种新的学习方法&#x…

sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

Linux线程(pthread)概念以及编程

pthread 线程的意思 1.简的来说&#xff0c;线程的概念就是在一个程序中&#xff0c;同时进行多个函数的运行比如以下代码 2.要引入头文件#include <pthread.h> ; 线程被调函数的定义是空指针类型的如&#xff1a; void *func1() 在main函数中&#xff0c;pthread_cre…

【软件】ERETCAD-Env:在轨空间环境3D动态仿真软件

文章介绍了Extreme-environment Radiation Effect Technology Computer-Aided Design – Environment (ERETCAD-Env)软件&#xff0c;文章的介绍和展示了ERETCAD-Env软件的功能和特点&#xff0c;这是一款用于动态模拟在轨卫星所处空间环境的计算机辅助设计软件。强调了该软件在…

Rust异步并发编程tokio异步运行时讲解和使用,新手必学

Rust 在v1.39版本以后就引入了async关键字&#xff0c;用于支持异步编程。 async fn foo() {}Rust中&#xff0c;async函数或块会被视作一个 Future 对象&#xff0c;async 关键字只是用来定义这个 Future 对象&#xff0c;定义好的这片异步代码并不会自动执行&#xff0c;而是…

c语言指针的应用场景

​ 1.什么是指针&#xff1f; 当我们提起指针的时候&#xff0c;可能第一反应会露出惊喜的表情 &#xff08;但是我们其实没必要那么慌&#xff0c;因为当我们随着我们学习的越来越深入就会发现&#xff0c;指针虽然看起来难&#xff0c;实际上也不怎么简单。哈哈哈开玩笑的&a…

TCN-LSTM时间卷积网络长短期记忆网络多输入多输出回归预测

文章目录 效果一览文章概述 订阅专栏只能获取一份代码部分源码参考资料 效果一览 文章概述 TCN-LSTM时间卷积网络长短期记忆网络多输入多输出回归预测 matlab2021 订阅专栏只能获取一份代码 部分源码 %------------------------------------------------------------------…

c# winform打包

本次采用vs2022打包winform窗体应用&#xff0c;有时应用不需要打包为安装应用&#xff0c;执行一个EXE就运行了 测试了几次&#xff0c;winform 非.net Framework 和控制台程序也是非.net Framework项目能打包这种&#xff0c;后续在研究

边缘计算在视频监控领域的应用

一、边缘计算在视频监控领域的应用 运用边缘计算解决视频监控问题&#xff0c;可以带来许多优势。以下是一些具体的应用示例&#xff1a; 实时分析与处理&#xff1a;在视频监控系统中&#xff0c;边缘计算盒子可以实时处理和分析视频流&#xff0c;实现对监控画面的智能识别…

STM32H7 HSE时钟的使用方法介绍

目录 概述 1 STM32H750 HSE时钟介绍 2 使用STM32Cube创建Project 3 认识HSE时钟 3.1 HSE时钟的特性 3.2 HSE的典型应用电路 4 STM32Cube中配置时钟 4.1 时钟需求 4.2 配置参数 4.2.1 使能外围资源 4.2.2 使用STM32Cube注意项 4.2.3 配置参数 5 总结 概述 本文主要…

IBM SPSS Statistics for Mac v27.0.1中文激活版:强大的数据分析工具

IBM SPSS Statistics for Mac是一款功能强大的数据分析工具&#xff0c;为Mac用户提供了高效、精准的数据分析体验。 IBM SPSS Statistics for Mac v27.0.1中文激活版下载 该软件拥有丰富的统计分析功能&#xff0c;无论是描述性统计、推论性统计&#xff0c;还是高级的多元统计…

论文解读:(CAVPT)Dual Modality Prompt Tuning for Vision-Language Pre-Trained Model

v1文章名字&#xff1a;Dual Modality Prompt Tuning for Vision-Language Pre-Trained Model v2文章名字&#xff1a;Class-Aware Visual Prompt Tuning for Vision-Language Pre-Trained Model 文章汇总 对该文的改进&#xff1a;论文解读&#xff1a;(VPT)Visual Prompt …