C++ string类(二)及深浅拷贝

news2024/11/25 21:18:00

一、string类方法使用举例

1.迭代器

迭代器本质:指针(理解)

迭代器:正向迭代器: begin() | end() 反向迭代器: rbegin() | rend()

2.find使用

//找到s中某个字符
void TestString3()
{
    string s("AAADEFNUIENFUIAAA");
    //找到AAA位置
    size_t pos = s.find("AAA");
    cout << pos << endl;
}

输出0,表示0位置处为AAA

3.获取文件后缀/substr

substr(pos = 0, n = npos);

从Pos位置开始,截取n个字符,如果n没有传递,表示从pos一直截取到末尾

如果都没有传递,截取整个字符

void TestString3()
{
    string s("124.text.cpp");
    //rfind从后面往前找 ’.’,+1找到后缀位置
    size_t pos =s.rfind('.') + 1;

    //从pos处截到末尾
    string postfix = s.substr(pos);
    cout << postfix << endl;
}

4.给多行单词,获取每行单词中最后一个单词的长度/getline

int main()
{
    string line;
    // 不要使用cin>>line,因为会它遇到空格就结束了
    // while(cin>>line)
    while (getline(cin, line))
    {
        size_t pos = line.rfind(' ');
        cout << line.size() - pos - 1 << endl;
    }
    return 0;
}

getline(cin,s): 获取一整行字符串,字符串中如果包含空格等空白字符也可以接收

oj练习

5.比大小

void Test()
{
    string s1("abc");
    string s2("afwef");
    if (s1 < s2)
    {
        cout << "s1<s2" << endl;
    }
    else if (s1>s2)
    {
        cout << "s1>s2" << endl;
    }
    else
    {
        cout << "s1=s2" << endl;
    }
}

6.其他一些练习OJ

反转字母OJ

找第一个出现的相同的字符

验证回文串

二. string类的模拟实现

1. 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规

上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝

2、解决浅拷贝的方法:深拷贝,写时拷贝(了解)

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供

3.String类的模拟实现

正确实现String

深拷贝,传统版本

class String
{
public:
    String(const char* str = "")
    {
        if (nullptr == str)
            str == "";
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    String(const String& s)
        :_str(new char[strlen(s._str) + 1])
    {
        strcpy(_str, s._str);
    }
    String& operator=(const String& s)
     {
         if (this != &s)
         {
         char* pStr = new char[strlen(s._str) + 1];
         strcpy(pStr, s._str);
         delete[] _str;
         _str = pStr;
     }
 return *this;
 }
    ~String()
    {
        if (_str)
        {
            delete[]_str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};

void TestString()
{
    String s1("hello");
    String s2(s1);
    String s3(s2);
}
int main()
{
    TestString();
    return 0;
}

如下,每个对象都有自己独立的空间,释放时也是,只释放自己的互不影响

深拷贝(现代版写法)

减少传统代码重复,相似性代码进行优化

class String
{
public:
     String(const char* str = "")
     {
        if (nullptr == str)
         {
             assert(false);
             return;
         }
         _str = new char[strlen(str) + 1];
         strcpy(_str, str);
     }
     String(const String& s)
         : _str(nullptr)//_Str可能为随机指针,要先置为空
     {
         String strTmp(s._str);
         swap(_str, strTmp._str);//交换地址
     }
    String& operator=(String s)//赋值运算符重载,先传入参数
     {
         swap(_str, s._str);//
         return *this;
     }

4.写时拷贝

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源

三、string模拟实现代码(全部)

#include <assert.h>
#include<iostream>
#include<string>
using namespace std;
namespace zx
{
    class string
    {
    public:
        typedef char* iterator;
        ///
        // 构造
        string(const char* str = "")
        {
            if (nullptr == str)
                str = "";

            _size = strlen(str);
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        string(const string& s)
        {
            string temp(s._str);
            this->swap(temp);
        }

        string(size_t n, char val)
        {
            _str = new char[n + 1];
            memset(_str, val, n);
            _str[n] = '\0';

            _size = n;
            _capacity = n;
        }

        string& operator=(string s)
        {
            this->swap(s);
            return *this;
        }

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

        /
        // 迭代器
        iterator begin()
        {
            return _str;
        }

        iterator end()
        {
            return _str + _size;
        }

        /
        // 容量
        size_t size()const
        {
            return _size;
        }

        size_t length()const
        {
            return _size;
        }

        size_t capacity()const
        {
            return _capacity;
        }

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

        void clear()
        {
            _size = 0;
        }

        void resize(size_t newsize, char val)
        {
            size_t oldsize = size();
            if (newsize > oldsize)
            {
                // 有效元素个数增多
                if (newsize > capacity())
                    reserve(newsize);

                // 多出的元素使用val填充
                memset(_str + _size, val, newsize - _size);
            }

            _size = newsize;
            _str[_size] = '\0';
        }

        void resize(size_t newsize)
        {
            resize(newsize, '\0');
        }

        void reserve(size_t newcapacity)
        {
            size_t oldcapacity = capacity();
            if (newcapacity > oldcapacity)
            {
                // 1. 申请新空间
                char* temp = new char[newcapacity + 1];

                // 2. 拷贝元素
                strncpy(temp, _str, _size);

                // 3. 释放旧空间
                delete[] _str;

                // 4. 使用新空间
                _str = temp;

                _capacity = newcapacity;
                _str[_size] = '\0';
            }
        }

        
        // 元素访问
        char& operator[](size_t index)
        {
            assert(index < _size);
            return _str[index];
        }

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

        char& at(size_t index)
        {
            // 如果越界,抛出out_of_range的异常
            return _str[index];
        }

        const char& at(size_t index)const
        {
            // 如果越界,抛出out_of_range的异常
            return _str[index];
        }

        //
        // modify
        void push_back(char ch)
        {
            *this += ch;
        }

        string& operator+=(char ch)
        {
            if (_size == _capacity)
                reserve((size_t)_capacity*1.5 + 3);

            _str[_size] = ch;
            ++_size;
            _str[_size] = '\0';
            return *this;
        }

        string& operator+=(const char* str)
        {
            size_t needSpace = strlen(str);
            size_t leftSpace = capacity() - size();
            if (needSpace > leftSpace)
            {
                reserve(capacity()+ needSpace);
            }

            strcat(_str, str);
            _size += needSpace;
            _str[_size] = '\0';

            return *this;
        }

        string& operator+=(const string& s)
        {
            *this += s.c_str();
            return *this;
        }

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

        string& appent(const string& s)
        {
            *this += s;
            return *this;
        }

        string& insert(size_t pos, const string& s);
        string& erase(size_t pos = 0, size_t n = npos);

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

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

        size_t find(char ch, size_t pos = 0)const
        {
            if (pos >= _size)
            {
                return npos;
            }

            for (size_t i = pos; i < _size; ++i)
            {
                if (_str[i] == ch)
                    return i;
            }

            return npos;
        }

        size_t rfind(char c, size_t pos = npos)
        {
            if (pos >= _size)
            {
                pos = _size-1;
            }

            for (int i = pos; i >= 0; --i)
            {
                if (_str[i] == c)
                    return i;
            }

            return npos;
        }

        string substr(size_t pos = 0, size_t n = npos) const
        {
            // 从pos位置开始,往后借助n个字符长度
            if (pos >= _size)
            {
                return string();
            }

            // 从pos到字符串的末尾剩余字符个数
            n = min(n, _size - pos);

            string temp(n, '\0');
            size_t index = 0;
            for (size_t i = pos; i < pos+n; ++i)
            {
                temp[index++] = _str[i];
            }

            return temp;
        }

    private:
        char* _str;
        size_t _size;
        size_t _capacity;

        const static size_t npos = -1;

        friend ostream& operator<<(ostream& out, const string& s);
    };

    ostream& operator<<(ostream& out, const string& s)
    {
        for (size_t i = 0; i < s.size(); ++i)
        {
            cout << s[i];
        }

        return out;
    }
}

int main()
{
    // TestMyString01();
    // TestMyString02();

    // TestMyString03();
    TestMyString04();
    _CrtDumpMemoryLeaks();
    return 0;
}

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

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

相关文章

分层测试(1)分层测试是什么?【必备】

1. 什么是分层测试&#xff1f; 分层测试是通过对质量问题分类、分层来保证整体系统质量的测试体系。 模块内通过接口测试保证模块质量&#xff0c;多模块之间通过集成测试保证通信路径和模块间交互质量&#xff0c;整体系统通过端到端用例对核心业务场景进行验证&#xff0c…

Spring Boot 3.0系列【2】部署篇之使用GraalVM构建原生镜像

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot版本2.7.0 文章目录概述JIT & AOTJIT &#xff08;动态编译&#xff09;AOT&#xff08;静态编译&#xff09;GraalVM简介运行模式Native Image&#xff08;原生镜像&#xff09;…

无线蓝牙耳机哪个牌子性价比高?国内性价比高的蓝牙耳机推荐

近年来&#xff0c;蓝牙耳机越来越成为人们日常生活中常见的数码产品之一&#xff0c;其便捷性以及愈发先进的功能得到了不少消费者的认可。无线蓝牙耳机哪个牌子性价比高&#xff1f;针对这个问题&#xff0c;我来给大家推荐几款国内性价比高的蓝牙耳机&#xff0c;一起来看看…

AI画图_stable-diffusion-webui安装使用指南(1)

本文章适用于: 有一定学习能力和钻研能力&#xff0c;遇到问题能合理使用搜索引擎尝试解决问题的人想在windows系统中尝试使用AI作画工具stable-diffusion-webui进行绘画的人有一定的计算机基础&#xff08;会魔法上网、知道 python和Git&#xff09;和英文阅读能力的人显卡为…

js侧滑显示删除按钮

效果图&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno"><title>js侧滑显示删…

堡垒机采购注意事项说明

随着各大企业对数据安全的高度重视&#xff0c;堡垒机作为基础数据安全产品&#xff0c;被采购的也越来越多。但面对是棉厂众多的堡垒机品牌&#xff0c;采购者往往很蒙圈。这里我们行云管家小编就给大家汇总说明了几点堡垒机采购注意事项&#xff0c;希望对大家有用。 堡垒机…

在中外合作办学硕士领域似乎自己一直在纠结,也许是为了能遇见人大女王金融硕士

2023考研成绩如期而至&#xff0c;还记得考试时的一幕幕吗&#xff1f;在身体被高热侵蚀的情况下&#xff0c;我们似乎很难忘记这次考试所带给我们的经历。如今成绩下来了&#xff0c;可能与我们预期的几乎相同&#xff0c;但是在不断地寻找新的学习途径的过程中我们发现&#…

字节跳动青训营--Webpack

文章目录前言一、为什么要学习Webpack&#xff1f;二、什么是Webpack&#xff1f;1. 产生背景2. 基础概念三、使用Webpack1. 安装2. 编辑配置文件3. 执行编译命令核心流程四、如何使用Webpack流程类配置配置总览五、理解Loader六、理解插件插件钩子课外关注资料前言 此文章仅用…

华为OD机试题,用 Java 解【特异性双端队列】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…

I.MX6ULL内核开发12:使用设备树插件实现RGB灯驱动

目录 一、引言 二、设备树插件格式 三、实验说明 四、实验准备 4.1 通过内核工具编译设备树插件 五、实验效果 5.1 uboot加载 5.2 加载RGB驱动 一、引言 Linux4.4以后引入了动态设备树&#xff08;Dynamic DevicesTree&#xff09;&#xff0c;这里翻译位“设备树插件…

【C++ | bug | 运算符重载】定义矩阵(模板)类时,使用 “友元函数” 进行 * 运算符重载时编译报错

作者&#xff1a;非妃是公主 专栏&#xff1a;《C》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐一、类的声明及函数定义二、错误信息三、问题…

MySQL数据库操作

查看数据库语法show databases——列出所有的数据库 show databases [ like wild ];——列出和字符串wild名字相同的数据库 这里可以配合SQl的 "%" 和 "_" 通配符使用来查找多个数据库在SQL语句中"%"代表任意字符出现任意次数,"_"代表…

无代码资讯 | 《低代码开发平台能力要求》发布;CADP列入Gartner《2022-2024 中型企业技术采用路线图》

栏目导读&#xff1a;无代码资讯栏目从全球视角出发&#xff0c;带您了解无代码相关最新资讯‍。TOP3 大事件1、《低代码开发平台能力要求》团体标准正式发布近日&#xff0c;中国电子工业标准化协会发布公告&#xff08;中电标【2022】037 号&#xff09;&#xff0c;由中国电…

Mysql数据查询

文章目录1 group by子句2 回溯统计3 having子句1 group by子句 group by子句**&#xff1a;分组统计&#xff0c;根据某个字段将所有的结果分类&#xff0c;并进行数据统计分析 分组的目的不是为了显示数据&#xff0c;一定是为了统计数据group by子句一定是出现在where子句之…

代码随想录算法训练营day45 |动态规划之背包问题 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数

day4570. 爬楼梯 &#xff08;进阶&#xff09;1. 确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例来推导dp数组322. 零钱兑换1. 确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组279.完全平方数1. 确…

讯鸿喜讯丨民建广州市委科技与信息化委员会莅临讯鸿

2023年2月17日&#xff0c;民建广州市委科技与信息化委员会成员莅临广州讯鸿网络技术有限公司&#xff08;以下简称讯鸿网络&#xff09;&#xff0c;举办首期“星五分享汇”活动&#xff0c;并召开2023年第一次全体委员会议。此次活动主要围绕科技与信息化等领域的前沿、热点问…

防范网络钓鱼仍然很重要!

在众多网络攻击中&#xff0c;网络钓鱼可以说是攻击者最喜欢使用的攻击手段之一。据《2022年数据泄露成本报告》显示&#xff0c;网络钓鱼已成为数据泄露的第二大方式&#xff0c;占比达16%&#xff0c;给受访组织造成高达491万美元的泄露成本。钓鱼者可以攻击任何在线服务中的…

ProtoEditor - 如何在Unity中实现一个Protobuf通信协议类编辑器

文章目录简介Protobuf 语法规则Proto Editor实现创建窗口定义类、字段增删类编辑字段导入、导出Json文件生成.proto文件生成.bat文件简介 在Socket网络编程中&#xff0c;假如使用Protobuf作为网络通信协议&#xff0c;需要了解Protobuf语法规则、编写.proto文件并通过编译指令…

【编程思想】计算机领域的所有问题都可以通过增加一个间接的中间层来解决

文章目录计算机领域的所有问题都可以通过增加一个间接的中间层来解决一、间接中间层可以解决计算机领域的问题二、操作系统如何通过间接中间层解决计算机问题三 结论七层网络协议中体现的分层思想概述物理层数据链路层网络层传输层会话层表示层应用层代码实例计算机存储体系设计…

回归预测 | MATLAB实现GRU(门控循环单元)多输入单输出(多指标评价)

回归预测 | MATLAB实现GRU(门控循环单元)多输入单输出(多指标评价) 文章目录 回归预测 | MATLAB实现GRU(门控循环单元)多输入单输出(多指标评价)预测效果基本介绍程序设计参考资料预测效果 基本介绍 GRU神经网络是LST