C++初阶 String类的模拟实现(上)

简介: C++初阶 String类的模拟实现(上)

本章目标


1.学会剩下的String类使用方法

2.模拟String类的实现


让我们进入愉快的学习吧~


String类的使用(补)

大部分String类的使用放在前面一篇博客中咯


由于老师讲课篇幅的限制所以说只能在这篇博客中补上剩下的一部分


String类的使用


1. String类中字符串比较


比较方式一


函数使用是compare


compare的使用方式如下


a10722bc2a034ce78daf047604e0726f.png


使用规则如下

比较字符实际上是比较字符的ASCII码大小


前面字符大于后面的字符返回大于0的值 (一般情况下是1)


前面字符小于后面的字符返回小于0的值 (一般情况下是-1)


如果相同就返回0


那么比较字符串呢?


实际上就是按照字符的顺序一个一个进行比较


那么如果其中一个字符串结束了 还有一个没有结束怎么办呢?


我们都知道字符串的结束标志是 ’/0‘ 事实上它的ASCII码是0


那么还有没有结束的那个字符串肯定是大于结束的这个字符串的


代码实例如下


string s1("hello world");
  string s2 = "hello Ntu";
  string s3(s1);
  // 这里写三种初始化方法
  // 1. 通过一个对象来调用 看看另外一个对象和自己相不相等
  cout << s1.compare(s2) << endl;
  cout << s1.compare(s3) << endl;

9688711dd0d543a9b99e7a38d156520d.png

我们可以发现完全正确


比较方式二


比较语法方式二 我们可以在前面指定开始比较的位置还有需要比较的个数(被比较字符串的)


代码表示如下


int main()
{
  string s1("hello world");
  string s2 = "hello Ntu";
  string s3(s1);
  // 这里写三种初始化方法
  // 2. 通过一个对象来调用   指定位置还有比较个数
  cout << s1.compare(1,3,s2) << endl;
  // s1的“hel”和整个s2比较
  cout << s1.compare(1,3,s3) << endl;
  // s1的“hel”和整个s3比较
  return 0;
}


运行结果如下


5fd6d4b6059340bd9a74d9f21203e619.png


我们发现可以完美运行


那么上面的代码是什么意思呢?


截取s1字符串从1位置开始 截取三个字符 然后和s2比较


比较方式三


比较语法三 我们指定两个字符串中需要比较的内容来比较


代码表示如下


int main()
{
  string s1("hello world");
  string s2 = "hello Ntu";
  string s3(s1);
  // 这里写三种初始化方法
  // 3. 通过一个对象来调用   分别指定两个字符串比较的位置和个数
  cout << s1.compare(1, 3, s2 ,1,3) << endl;
  // s1的“hel”和s2的“hel”比较
  cout << s1.compare(1, 3, s3,1 , 1) << endl;
  // s1的“hel”和 “h”比较
  return 0;
}


运行结果如下

71a93d9e0e2f4a68b214be032793a8d0.png


也完美符合我们的预期


这个是什么意思呢?


我们在前面被比较字符串需要比较的位置以及要比较的字符个数


在指定比较字符串后继续指定比较字符串的位置以及比较字符个数


2. String对象类型转换


String对象转化成其他类型(以Int为例)

函数名称 stoi

411321973a9d4ef6aeb4915ccfc855c6.png


这里的三个参数分别是什么意思呢?


第一个参数我们应该传进去一个字符串 这里应该没什么说法


第二个参数我们应该传进去一个pos指针 指向我们开始要转化成int类型的位置(默认是0 也就是全部转换)


第三个参数是进制 转化后是一个什么进制的数字 默认是10


下面我们来看看代码表示


int main()
{
  string s1 = "7"; // 十进制 7  
  int a = stoi(s1, 0, 10); // 0就是空指针 最后的10可以不写
  // int a = stoi(s1) 两段代码效果一样
  cout << a << endl;
  return 0;
}


表示结果如下


67c5ed5e3ef44477965f3698b397024c.png


我们可以发现 这里的a确实被赋值成数字7了


后面又诸多类似的函数 比如说 stos stold stolld 我们这里只要记住这一个的用法


后面需要用到其他的时候改变 stoi(int)最后的i就可以


其他类型转化成String类型


6576745a1af34fd0bef023519653dd61.png

这个函数的用法就很简单了 不用过多的讲解 我们直接看代码和效果


代码表示如下


int main()
{
  string s1 = to_string(123123123);
  string s2 = to_string(123.123+1.11);
  cout << s1 << endl;
  cout << s2 << endl;
  return 0;
}


实现效果如下

90ae4a6ad4ea4dbf814bceee4c2426d7.png


模拟String类的实现


默认成员函数


构造函数


还记得构造函数的格式是什么嘛?


函数名和类名相同 无返回值


我们现在模拟这个类的成员有三个 Size(字符个数) Capacity (能储存的字符个数)_str (指向字符串的指针)


我们来看看效果


75b22911b48b491eb22b373b0201ed56.png

可以完美运行


析构函数


String类中的析构函数需要我们自己来写 因为我们使用了动态内存开辟 所以肯定需要我们手动来释放资


当然啦 析构函数也很简单


代码表示如下


~String()
  {
  delete[] _str;   // 释放内存 避免内存泄漏
  _str = nullptr;
  _size = 0;
  _capacity = 0;
  }


拷贝构造函数


我们先来试试 如果我们不写拷贝构造函数 使用系统默认的会怎么样


aec31d62437f4d0ca5d4135cf278eff9.png


这个错误其实我们讲类和对象中的时候是不是已经遇到过了 这就是对同一空间的连续释放


这个其实就是由于浅拷贝 两个指针指向同一块空间的后果


浅拷贝: 浅拷贝只复制某个对象的引用 而不复制对象本身 新旧对象还是共享同一块内存

深拷贝: 深拷贝会创造一个一摸一样的对象 新对象和原对象不共享内存 修改新对象不会改变原来的对象


所以说 我们这里要写一个深拷贝函数



String(const String& s)
  :_str(new char[strlen(s._str)+1])
  ,_capacity(s._capacity)
  ,_size(s._size)
  {
  strcpy(_str, s._str); // 复制里面的内容 
  }


73fad3726e3947f1875cec4d71df8b31.png


我们发现 使用深拷贝之后就不会报错了 (因为这个时候都有自己的空间了)


运算符重载函数


这里我们同样也要使用深拷贝来避免内存错误


类似这样

d10d534e93e04ad6a867bee285bbbd9c.png

这里我们首先要明确一点


开辟内存是有可能失败的 而释放内存是一定成功的


所以说我们这里可以先开辟一个tmp内存 开辟成功之后再释放原来的内存(防止释放之后开辟不成功的问题)


写法如下


因为tmp是一个临时变量 所以说出了作用域会自动调用析构函数 所以说我们不用管原来_str指向的区域


String& operator=(const String& s)
  {
  String tmp(s);  // 拷贝构造一个tmp
  swap(_str,tmp._str); // 交换指针指向
  _size = s._size;
  _capacity = s._capacity;
  return *this;
  }


随着时间的发展这里诞生出一种现代写法更加简洁


String& operator=(String s)
  {
  // s是一个传值拷贝的参数 
  swap(_str, s._str);
  swap(_size, s._size);
  swap(_capacity, s._capacity);
  return *this;
  }


这里直接使用传值传递 之后我们交换下他们的指针 大小 容量


s会自动销毁 我们返回*this就可以


迭代器相关函数


还记不记得我们上篇博客说的话


我们将这个迭代器默认为一个指针就好了


实际上Stirng类中的迭代器就是一个指针


但是我们要注意的是 并不是所有类中的迭代器都是指针


那么知道它是指针之后就很好办了


注意!!! 这里的格式不能错


typedef char* iterator;
typedef const char* const_iterator;


begin


代码表示如下


iterator begin()
  {
  return _str; // 返回字符串中第一个字符地址
  }
  iterator begin() const
  {
  return _str; // 返回字符串中第一个字符地址
  }


end


代码表示如下


iterator end()
  {
  return _str + _size; // 返回 /0
  }
  iterator end() const
  {
  return _str + _size; // 返回 /0
  }



接下来我们来试验下 使用迭代器遍历数组


代码表示如下

String::iterator it = s1.begin();
  while (it!=s1.end()) // 实际上就是不等于/0的时候
  {
  // 这里实际上就是字符串遍历数组的方式
  cout << *it << " ";
  it++;
  }


运行结果如下


4bf802af811e4dc0a7dd8564ee298c04.png


所以我们发现这里是不是没有什么新东西啊


只不过是把我们以前学的东西换了个名字而已


范围for


在我们前面的String类中介绍过了范围for


实际上它的底层原理是什么呢?


实际上就是使用了我们的一个迭代器


for (auto x : s1)
  {
  cout << x << " ";
  }


运行结果如下

2ea19af2218542b1bae6ac4c85a7ff88.png


假设我们把把迭代器屏蔽掉 这里就运行不了了

相关文章
|
2月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
81 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
63 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
113 5
|
2月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
112 4
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
152 4
|
3月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
64 2
|
3月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
35 4
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
33 1
|
3月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
33 4
|
3月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)