返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++字符串类的封装你真的了解吗
  • 777
分享到

C++字符串类的封装你真的了解吗

2024-04-02 19:04:59 777人浏览 安东尼
摘要

目录字符串类的封装常规代码头文件函数实现文件Test文件重载左移>>重载右移<<重载赋值=重载中括号[ ]重载加号+重载==总结字符串类的封装 常规代码 头文

字符串类的封装

常规代码

头文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iOStream>
using namespace std;
class MyString//字符串应该是维护着一个字符数组的,在堆区
{
public:
	MyString(const char* str);//有参构造函数
	MyString(const MyString& str);//拷贝构造函数
	~MyString();//析构函数(是需要的,因为这个类中还维护着一个指针,需要自己释放)
private:
	char* pString;//维护在堆区开辟的字符数组
	int m_Size;//字符串长度(不统计\0)
};

在头文件中定义了MyString类中应该有的属性和三个函数。(有参构造函数接收字符串并创建对象)(拷贝构造函数可以接收同类型的对象并且复制出一个新的对象)(析构函数负责在释放掉本对象的同时释放掉对象中维护的指针)

函数实现文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"myString.h";
//MyString str = "123";
MyString::MyString(const char* str)//有参构造函数,传进去的参数是一个字符串,返回的是MyString类型的对象。
{
	cout<<"MyString的有参构造函数调用"<<endl;
	this->pString = new char[strlen(str) + 1];//给本对象的pString准备空间。
	strcpy(this->pString, str);//将传进来带的字符串的地址也传给本对象的pString处,str是字符串,不能调用pString属性。
	this->m_Size = strlen(str);
}
MyString::MyString(const MyString& str)//拷贝构造函数
{
	//这个需要深拷贝,因为类中有个指针。注意:深拷贝是对指针进行的深拷贝,也就是会传进来个对象,然后给这个对象中的指针 分配传进来的对象中的指针的大小的空间,然后将这个值也赋值到本指针中。
	cout << "MyString的拷贝函数调用" << endl;
	this->pString = new char[strlen(str.pString)+1];
	strcpy(this->pString , str.pString);
	this->m_Size = str.m_Size;
}
MyString::~MyString()//析构(是需要的,因为这个类中还维护着一个指针,需要自己释放)
{
	cout << "MyString的析构函数调用" << endl;
	if (this->pString != NULL)
	{
		delete[] this->pString;
		this->pString = NULL;
	}
}

拷贝构造和有参构造的区别就是:

1,有参构造传进去的是一个字符串,返回成一个对象。所以要的是对象的各个属性与字符串的属性的匹配。

2,拷贝构造传进去的是一个对象,返回的也是一个对象,所以要的是传进来的对象和本对象(要创建的对象)之间属性的对应。

3,如果拷贝构造的时候发现需要拷贝个指针,那么就不能直接使用编译器的拷贝构造函数了,因为编译器的拷贝构造函数构造出来的对象的指针是和传进来的对象的指针是指向同一块地方的,那么等到需要释放指针的时候就会出现重释放的错误。**(这也是为什么需要自己重新创建拷贝构造的原因)**如果类中不需要维护指针,那么就不需要自己写拷贝构造(自己为指针再创建空间)。

Test文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include "myString.h";

int main()
{
	MyString str = "abc";//调用默认构造
	//和后面的一样MyString str("abc");
	MyString str2 = str;//调用拷贝构造
	return 0;
}

注意,这里的“abc”就是传进来的字符串str,然后这里的str就是创建出来的MyString类型的对象。

运行的结果:

MyString的有参构造函数调用
MyString的拷贝函数调用
MyString的析构函数调用
MyString的析构函数调用

重载左移>>

如果想进行

cout<<str<<endl;代码肯定会报错,因为str是MyString类型的对象,<<不认识这个。

所以,这个时候就需要重载一下<<左移运算符,可以在函数文件中实现。(需要用全局函数配合友元进行重载)

实现函数:

ostream& operator<<(ostream& cout, MyString& str) //重载左移运算符
{
	cout << str.pString;//这里的pString是类中的私有属性,所以需要在原类(在头文件中)中给整个重载函数设置友元
	return cout;
}

头文件

class MyString
{
	friend ostream& operator<<(ostream& cout, MyString& str);//设置的友元
public:
private:
};

这样cout << str << endl;这行代码就可以调用了。

重载右移<<

如果想进行

cin>>str;代码肯定也会报错,因为str是MyString类型的对象,>>不认识这个。

所以,这个时候就要重载一下>>右移运算符,可以在函数文件中实现。(需要用全局函数配合友元进行重载)

实现函数

istream& operator>>(istream& cin, MyString& str)//重载右移运算符
{
	//应该先清空原来的堆区数据
	if (str.pString)//这里的pString是对象中的私有属性,所以需要在原类中加上这个重载函数的友元声明
	{
		delete[] str.pString;
		str.pString = NULL;
	}
	//不用急着直接将输入的内容传给pString,可以先开辟临时数组,记录着输入内容。
	char buf[1024];
	cin >> buf;
	//因为是自己重载的函数,刚才将str的pString删除了,现在需要重新申请空间。
	str.pString = new char[strlen(buf) + 1];
	strcpy(str.pString, buf);
	str.m_Size = strlen(buf);//别忘了还要把大小考进str的size中,因为传进来一串字符以后,对象中的长度还保持着原先的长度,所以需要进行修改。
	cout << str.m_Size << endl;
	return cin;
}

头文件

class MyString//字符串应该是维护着一个字符数组的,在堆区
{
	friend istream& operator>>(istream& cin, MyString& str);
public:
private:
};

然后就能给str的pString赋值了。

重载右移运算符的时候的清空原来字符串中的内容好像不太重要,删除了也能正常运行。创建临时数组记录(数组大小够大即可),然后将赋值,最后别忘了更改对象中的size。

重载赋值=

如果想进行:

str2 = str1

直接将两个对象进行=运行起来代码肯定会崩,因为:全拷贝了,删除对象的时候会出现浅拷贝的问题。

如果想进行:

str2 = “abc"

直接将字符串赋值给字符串肯定也是不行的。

所以需要重载两个不同参数的 = 运算符。(一种参数是对象,一种参数是字符串)

MyString& operator=(const MyString& str);
MyString& operator=(const char* str);

返回值必须要是MyString& ,因为使用完=运算符要返回的是自身(str2). 注意在头函数中声明完了以后到实现文件中去实现的时候要写范围MyString::,而且这个类的范围需要写在返回值类型的后面,函数名的前面。

重载=运算符,与重载左移和右移运算符不同,不用再像<<和>>一样使用全局函数重载了,需要使用成员函数

头文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class MyString
{
	friend ostream& operator<<(ostream& cout, MyString& str);
	friend istream& operator>>(istream& cin, MyString& str);
public:
	MyString(const char* str);//有参构造函数
	MyString(const MyString& str);//拷贝构造函数
	~MyString();//析构(是需要的,因为这个类中还维护着一个指针,需要自己释放)
	//重载两个=运算符
	MyString& operator=(const MyString& str);
	MyString& operator=(const char* str);

private:
	char* pString;//维护在堆区开辟的字符数组
	int m_Size;//字符串长度(不统计\0)
};

实现文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"myString.h";
MyString & MyString::operator=(const MyString & str)
{
	//先判断原堆区有没有内容,如果有先释放。
	if (this->pString)
	{
		delete[]this->pString;
		this->pString = NULL;
	}
	//进行深拷贝
	this->pString = new char[strlen(str.pString) + 1];
	strcpy(this->pString, str.pString);
	this->m_Size = strlen(str.pString);
	return *this;
}
MyString & MyString::operator=(const char* str)
{
	//先判断原堆区有没有内容,如果有先释放。
	if (this->pString)
	{
		delete[]this->pString;
		this->pString = NULL;
	}
	//进行深拷贝
	this->pString = new char[strlen(str) + 1];
	strcpy(this->pString, str);
	this->m_Size = strlen(str);
	return *this;
}

Test文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include "myString.h";

int main()
{
	MyString str = "abc";//调用默认构造
	MyString str2 = "bcd";
	str = str2;
	MyString str3 = "abc";
	cout << str << endl;//bcd
	cout << str3 << endl;//abc
	return 0;
}

重载中括号[ ]

如果项进行

str2[0] = 'a';

是不可以的,因为[ ]不认识str。

所以需要重载中括号[]。直接使用成员函数进行重载。

//在头文件中:
char operator[](int index);
//在实现函数中:
char MyString::operator[](int index)//重载中括号
{
	return this->pString[index];
}

正常来说就返回char类型的数值就行了,这样就可读了。

但是如果想将str2p[1]作为运算左值来修改,那么就需要返回本体char&

//在头文件中:
char& operator[](int index);
//在实现函数中:
char& MyString::operator[](int index)//重载中括号
{
	return this->pString[index];
}
//在Test文件中
int main()
{
	MyString str2 = "bcd";
	cout << str2[1] << endl;//c
	str2[1] = 'z';
	cout << str2[1] << endl;//z
	return 0;
}

重载加号+

如果想实现:

MyString str3 = "abc";
MyString str4 = "def";
MyString str5 = str3 + str4;
MyString str6 = str5+"abc";

这样肯定会报错,因为+不认识对象,也不认识这样的字符串

所以,需要对+进行重载,使用的还是成员函数,只有一个参数。

从题意得,传进去一个对象,然后返回出一个对象,或者是传进去一个字符串,返回出一个对象。(前提是将第一个传进去的看作是调用对象)

头文件

MyString operator+(const MyString& str);
MyString operator+(const char* str);

实现文件

//重载+运算符
MyString MyString:: operator+(const MyString& str)
{
	//本身abc,传入的是def,刚开始应该先计算一下需要开辟的内存空间。
	int newSize = this->m_Size + strlen(str.pString) + 1;
	char* temp = new char[newSize];//然后将这块空间开辟出来,temp指针指向它。
	memset(temp, 0, newSize);//将空间里面的内容全部清空。
	strcat(temp, this->pString);//将this->pString扔进了temp中。
	strcat(temp, str.pString);//然后再将str字符串扔进去,这样它们就自己结合了。
	//但是创建好的新的字符串不能直接返回,因为需要返回一个对象
	//所以就创建一个新的对象,然后通过构造函数将字符串赋给新对象,最后再返回新对象。
	MyString newString = temp;
	//还有一点,创建的类是空间temp用完了需要释放
	delete[]temp;
	return newString;	
}
MyString MyString:: operator+(const char* str)
{
	//本身abc,传入的是def,刚开始应该先计算一下需要开辟的内存空间。
	int newSize = this->m_Size + strlen(str) + 1;
	char* temp = new char[newSize];//然后将这块空间开辟出来,temp指针指向它。
	memset(temp, 0, newSize);//将空间里面的内容全部清空。
	strcat(temp, this->pString);//将this->pString扔进了temp中。
	strcat(temp, str);//然后再将str字符串扔进去,这样它们就自己结合了。
	//但是创建好的新的字符串不能直接返回,因为需要返回一个对象
	//所以就创建一个新的对象,然后通过构造函数将字符串赋给新对象,最后再返回新对象。
	MyString newString = temp;
	//还有一点,创建的类是空间temp用完了需要释放
	delete[]temp;
	return newString;
}

两种+重载函数几乎一样

TEST文件

int main()
{
	MyString str3 = "abc";
	MyString str4 = "def";
	MyString str5 = str3 + str4;
	MyString str6 = str5 + "abc";
	cout << str5 << endl;//abcdef
	cout << str6 << endl;//abcdefabc
	return 0;
}

重载==

ps补充:strcmp函数中,如果两个字符串相等,那么就返回0,如果不相等,那么就返回1。

也是提供两种重载函数:

//头文件:
//重载==运算符
bool operator==(const MyString& str);
bool operator==(const char* str);
//实现文件:
//重载==运算符
bool MyString::operator==(const MyString & str)
{
	if (strcmp(this->pString, str.pString) == 0)
		return true;
	else
		return false;
}
bool MyString::operator==(const char* str)
{
	if (strcmp(this->pString, str) == 0)
		return true;
	else
		return false;
}
//Test文件
int main()
{
	MyString str3 = "abc";
	MyString str4 = "def";
	MyString str5 = str3 + str4;
	cout << str5 << endl;//abcdef
	if (str5 == str5)
	{
		cout << "是相等的" << endl;
	}
	else
	{
		cout << "是不相等的" << endl;
	}
	//结果是相等的。
	return 0;
}

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容! 

--结束END--

本文标题: C++字符串类的封装你真的了解吗

本文链接: https://lsjlt.com/news/139115.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++字符串类的封装你真的了解吗
    目录字符串类的封装常规代码头文件函数实现文件Test文件重载左移>>重载右移<<重载赋值=重载中括号[ ]重载加号+重载==总结字符串类的封装 常规代码 头文...
    99+
    2024-04-02
  • C++的运算符你真的了解吗
    目录前言1 算术运算符2 赋值运算符3 比较运算符4 逻辑运算符总结前言 运算符的作用:用于执行代码的运算 主要有: 1 算术运算符 用于处理四则运算 对于前置递增:将递增运算前...
    99+
    2024-04-02
  • C++的数据类型你真的了解吗
    目录前言1 整型2 sizeof关键字3 实型(浮点型)4 字符型5 转义字符6 字符串型7 布尔类型 bool8 数据的输入总结前言 C++不像python,创建变量的时候必须指定...
    99+
    2024-04-02
  • C++重载运算符你真的了解吗
    目录1.重载运算符的必要性2.重载运算符的形式与规则3.重载运算符的运算4.转义运算符总结运算符实际上是一个函数,所以运算符的重载实际上是函数的重载,。编译程序对运算符的重载的选择,...
    99+
    2024-04-02
  • C语言的常量,字符串,转义字符,注释你都了解吗
    目录四种常量:字符串:转义字符注释总结四种常量: 1.字面常量 如数字100,‘a’ 2.const修饰的常变量 const int n = 10 //常变量...
    99+
    2024-04-02
  • 说说字符串转 OffSetDateTime 你真的会用吗
    字符串转 OffSetDateTime 你真的会用 要创建OffsetDateTime ,需要日期 (日,月和年), 时间 (小时,分钟,秒和纳秒)和偏移量 (与UTC的差异)。 如...
    99+
    2024-04-02
  • JavaVolatile关键字你真的了解吗
    目录正文并发编程的三要素1.原子性2.可见性3.有序性VolatileVolatile 的内存模型Volatile 的实现原理(1) lock(2) unclock(3) read(...
    99+
    2024-04-02
  • python3中的类继承你真的了解吗
    目录(1)首先使用直接继承的方式(2)在子类中定义属性(3)如果想要继承父类中的属性,一般使用super方法:(4)如果需要在子类的__init__中传入参数则可以这样使用:&nbs...
    99+
    2024-04-02
  • JavaSE的类和对象你真的了解吗
    目录1.基本概念1.1面向对象1.2类和对象2.类的定义及使用2.1定义2.2类的实例化3.this引用3.1访问成员变量3.2访问成员方法3.3this引用的特性4.构造方法4.1...
    99+
    2024-04-02
  • C++中的函数你真的理解了吗
    目录1 概述2 函数的定义及调用3 值传递4 函数的常见形式5 函数的声明6 函数的分文件编写作用:让代码结构更加清晰1.2.3.4.总结1 概述 作用:将一段经常使用的代码进行封装...
    99+
    2024-04-02
  • C++中的数组你真的理解了吗
    目录1 概述2 一维数组2.1 一维数组定义方式2.2 一维数组组名2.3 冒泡排序3 二维数组3.1 二维数组定义方式3.2 二维数组数组名3.3二维数组应用举例总结1 概述 所谓...
    99+
    2024-04-02
  • C++的智能指针你真的了解吗
    目录什么是RAIIRAII的原理裸指针存在的问题auto_ptrunique_ptr总结什么是RAII RAII(Resource Acquisition Is Initializa...
    99+
    2024-04-02
  • 你真的了解PHP中的引用符号(&)吗
    引言 最近接手了五六年前的老项目,用的是CI2.0框架,在看框架源码的时候有个地方让我楞了一下,于是有了这篇文章。 字符&的最早历史可以追溯到公元1世纪,最早是拉丁语et ...
    99+
    2024-04-02
  • Java中的封装、继承和多态,你真的都懂了吗
    目录封装继承多态接口一些建议和小结写在最后的话封装 所谓的封装就是把类的属性和方法使用private修饰,不允许类的调用者直接访问,我们定义如下一个类,可以看到所有的成员变量和成员方...
    99+
    2024-04-02
  • python数据类型中的字符串你了解多少
    目录一、字符串创建二、字符串的下标str[beg:end]三、字符串的切片:slice(start, stop[, step])四、字符串格式化:# %s——...
    99+
    2024-04-02
  • C语言的字符空间与非字符空间你了解吗
    目录前言一、字符空间与非字符空间概念二、字符空间三、非字符空间四、void * 非字符空间形参化总结前言 自学笔记,没有历史知识铺垫(省略百度部分)C语言字符空间与非字符空间 一、字...
    99+
    2024-04-02
  • Java面向对象的封装你了解吗
    目录面向对象思想之封装什么是封装呢?封装的好处意义getter方法和setter方法toString方法总结:面向对象思想之封装 或许大家都听说过java是纯面向对象语言,面向对象思...
    99+
    2024-04-02
  • 你真的了解 PHP 关键字 API 容器吗?
    PHP 是一种广泛使用的服务器端脚本语言,由于其易学易用的特性,它已经成为了众多 Web 开发者的首选语言。而在 PHP 中,有一些关键字,比如 API 和容器,它们在开发中也扮演着非常重要的角色。本文将详细介绍这两个关键字,帮助读者更好地...
    99+
    2023-10-25
    关键字 api 容器
  • C++的多态和虚函数你真的了解吗
    目录一、C++的面试常考点二、阿里真题2.1 真题一(1)虚函数表vtbl(2)构造一个派生类对象的过程(3)析构一个派生类对象的过程2.2 真题二2.3 真题三2.4 真题四2.5...
    99+
    2024-04-02
  • C++的动态内存管理你真的了解吗
    目录前言用法上对内置类型对自定义类型new/delete底层原理重载类的专属operator new和 operator delete定位newnew/delete与malloc/f...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作