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

目录
  • 字符串类的封装
    • 常规代码
      • 头文件
      • 函数实现文件
      • 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;
}

总结

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

(0)

相关推荐

  • winfrom 打印表格 字符串的封装实现代码 附源码下载

    所以对于应用层用着还不是很方便.最近做一个项目顺便就封装了一个调用默认打印机的类.虽说有几个小bug,但对于目前来说,已经满足需求了.以后不够了在来升级吧. 1,关于打印上下左右边距和纸张的高宽.以往都把这些写死到代码里面.既然是调用默认打印机,打印机的型号自然有差异.所以我就把这些配置放到app.config里面.但又怕每次打印都加载config影响效率.故此设计了个PrintPaper类.里面所有属性都是静态的.还有一个静态的构造方法.这样只有在程序开始运行加载一次config.之后就直接从

  • Objective-C封装字符串存储操作示例

    Objective-C简单封装 字符串的存储操作,省去中间沙盒处理方式 复制代码 代码如下: /存储publickey和sessionID -- writeContent: nil - 仅取出数据, 其他 - 修改原内容并提取+(NSString *)storeFile:(NSString *)fileName content:(NSString *)writeContent{    NSString *pathDocuments=[NSSearchPathForDirectoriesInDom

  • PHP封装函数实现生成随机的字符串验证码

    前言 一般情况下我们在做程序的时候肯定会有很多地方使用到随机字符串.比如做验证码用到的.然后就把这个函数封装起来.使用时候要设置2个参数.原理是随机抓取字符串.对字符串进行拼接 $str设置里要被采集的字符串.比如 $str=´jfowef34098094j3204efa234sfg2z23srhftj345xjxjhsrth´; 则在函数里面生成的字符串就回从 $str 里面随机抓取 $codeLen设置要生成的随机字符串.设置5.则生成5个随机字符串.比如 $codeLen=´5´;//设置

  • PHP封装返回Ajax字符串和JSON数组的方法

    实例如下: <?php class DBDA { public $host="localhost"; public $uid = "root"; public $pwd = "123"; public $dbname = "mydb"; //成员方法 public function Query($sql,$type=1) { $db = new MySQLi($this->host,$this->uid,$t

  • C#常用字符串加密解密方法封装代码

    复制代码 代码如下: //方法一//须添加对System.Web的引用//using System.Web.Security;/// <summary>/// SHA1加密字符串/// </summary>/// <param name="source">源字符串</param>/// <returns>加密后的字符串</returns>public string SHA1(string source){    r

  • PHP封装的字符串加密解密函数

    程序中经常使用的PHP加密解密字符串函数 代码如下: /********************************************************************* 函数名称:encrypt 函数作用:加密解密字符串 使用方法: 加密 :encrypt('str','E','nowamagic'); 解密 :encrypt('被加密过的字符串','D','nowamagic'); 参数说明: $string :需要加密解密的字符串 $operation:判断是加密还

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

    目录 字符串类的封装 常规代码 头文件 函数实现文件 Test文件 重载左移>> 重载右移<< 重载赋值= 重载中括号[ ] 重载加号+ 重载== 总结 字符串类的封装 常规代码 头文件 #pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; class MyString//字符串应该是维护着一个字符数组的,在堆区 { public: MyString(

  • C++动态数组类的封装实例

    C++中的动态数组(Dynamic Array)是指动态分配的.可以根据需求动态增长占用内存的数组.为了实现一个动态数组类的封装,我们需要考虑几个问题:new/delete的使用.内存分配策略.类的四大函数(构造函数.拷贝构造函数.拷贝赋值运算符.析构函数).运算符的重载.涉及到的知识点很多,对此本文只做简单的介绍. 一.内存分配策略 当用new为一个动态数组申请一块内存时,数组中的元素是连续存储的,例如 vector和string.当向一个动态数组添加元素时,如果没有空间容纳新元素,不可能简单

  • Java实现Http工具类的封装操作示例

    本文实例讲述了Java实现Http工具类的封装操作.分享给大家供大家参考,具体如下: http工具类的实现:(通过apache包)第一个类 import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolE

  • Python面向对象程序设计类的封装与继承用法示例

    本文实例讲述了Python面向对象程序设计类的封装与继承用法.分享给大家供大家参考,具体如下: 访问限制(封装) 1.概念 面向对象语言的三大特征:封装, 继承, 多态. 广义的封装: 类和函数的定义本身就是封装的体现. 狭义的封装:一个类的某些属性,不希望外界直接访问,而是把这个属性私有化[只有当前类持有],然后暴露给外界一个访问的方法. 封装的本质:就是属性私有化的过程. 封装的好处:提供了数据的复用性,保证了数据的安全性. 举例:插排 2.使用 class Person(object):

  • PHP验证类的封装与使用方法详解

    本文实例讲述了PHP验证类的封装与使用方法.分享给大家供大家参考,具体如下: <?php /** * Created by PhpStorm. * User: jiqing * Date: 18-7-24 * Time: 下午4:36 * 常用验证 */ class Valid { static protected $error; static protected $error_tips = [ 'tel' => '手机号格式有误', 'email' => '邮箱格式有误', 'max_

  • C#串口通信工具类的封装

    本文实例为大家分享了C#串口通信工具类的封装代码,供大家参考,具体内容如下  1.SerialPortHelper串口工具类封装 using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers;   namespace public.Util {

  • 实例解析iOS app开发中音频文件播放工具类的封装

    一.简单说明 1.关于音乐播放的简单说明 (1)音乐播放用到一个叫做AVAudioPlayer的类 (2)AVAudioPlayer常用方法 加载音乐文件 复制代码 代码如下: - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError; - (id)initWithData:(NSData *)data error:(NSError **)outError; 准备播放(缓冲,提高播放的流畅性) - (BOOL)prep

  • php实现的替换敏感字符串类实例

    本文实例讲述了php实现的替换敏感字符串类及其用法,在php程序开发中有着非常广泛的应用价值.分享给大家供大家参考.具体方法如下: StrFilter.class.php类文件如下: <?php /** string filter class * Date: 2013-01-09 * Author: fdipzone * Ver: v1.0 * * Func: * public replace 替换非法字符 * public check 检查是否含有非法字符 * private protect_

  • PHP类的封装与继承详解

    封装 把成员方法和成员属性封装到类中,隐藏属性和方法实现的细节,通过public.protected.private等限定类成员的访问权限,数据被保护在内部,只通过被授权的成员方法才可以操作,尽可能的对成员进行封装. public:方法或者属性在任何作用域下都可以访问到,而且是默认的,如果没有为一个属性或方法指定访问修饰符,它将是public. protected:本类和子类可以访问,外部对象不可以调用. private:只能在本类访问,子类与外部对象都不可能调用.经过private标记的方法或

  • C#生成不重复随机字符串类

    本文实例讲述了C#生成不重复随机字符串类.分享给大家供大家参考.具体如下: 这个C#类用于随机产生不重复的字符串,可以指定字符串范围,可以指定要产生字符串的长度 using System; namespace DotNet.Utilities { public class RandomOperate { // 一:随机生成不重复数字字符串 private int rep = 0; public string GenerateCheckCodeNum(int codeCount) { string

随机推荐