Encapsulation of string class -- c++

Encapsulation of string class


General code


Header file


#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

class MyString//The string should maintain a character array in the heap
{
public:
	MyString(const char* str);//Parameterized constructor
	MyString(const MyString& str);//copy constructor 
	~MyString();//Destructor (required, because this class also maintains a pointer, which needs to be released by itself)

private:
	char* pString;//Maintain the character array opened in the heap
	int m_Size;//String length (not counted \ 0)
};

The header file defines the attributes and three functions that should be in the MyString class. (the parameterized constructor receives a string and creates an object) (the copy constructor can receive an object of the same type and copy a new object) (the destructor is responsible for releasing the pointer maintained in the object while releasing the object)


Function implementation file


#define _CRT_SECURE_NO_WARNINGS 1
#include"myString.h";
//MyString str = "123";

MyString::MyString(const char* str)//The parameter constructor passes in a string and returns an object of type MyString.
{
	cout<<"MyString Parameterized constructor call"<<endl;
	this->pString = new char[strlen(str) + 1];//Prepare space for the pString of this object.
	strcpy(this->pString, str);//The address of the string passed in is also passed to the pString of this object. str is a string and the pString property cannot be called.
	this->m_Size = strlen(str);
}

MyString::MyString(const MyString& str)//copy constructor 
{
	//This requires a deep copy because there is a pointer in the class. Note: deep copy is a deep copy of the pointer, that is, an object will be passed in, and then the pointer in this object will be allocated space for the size of the pointer in the passed object, and then this value will also be assigned to this pointer.
	cout << "MyString Copy function call" << endl;
	this->pString = new char[strlen(str.pString)+1];
	strcpy(this->pString , str.pString);
	this->m_Size = str.m_Size;
}

MyString::~MyString()//Destruct (it is necessary, because this class also maintains a pointer, which needs to be released by itself)
{
	cout << "MyString Destructor call" << endl;
	if (this->pString != NULL)
	{
		delete[] this->pString;
		this->pString = NULL;
	}
}

The difference between copy structure and parameter structure is:

1. The parameter structure passes in a string and returns it as an object. Therefore, we need to match the attributes of the object with the attributes of the string.
2. The copy structure passes in an object and returns an object. Therefore, what we need is the attribute correspondence between the passed in object and the object to be created.
3. If it is found that a pointer needs to be copied during the copy construction, the copy constructor of the compiler cannot be used directly, because the pointer of the object constructed by the copy constructor of the compiler points to the same place as the pointer of the object passed in, so the error of re release will occur when the pointer needs to be released** (that's why you need to recreate the copy structure) * * if you don't need to maintain the pointer in the class, you don't need to write the copy structure yourself (create space for the pointer yourself).


Test file

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


int main()
{
	MyString str = "abc";//Call default construct
	//As in the following, MyString str("abc");
	MyString str2 = str;//Call copy construct

	return 0;
}

Note that "abc" here is the string str passed in, and then str here is the object of MyString type created.

Results of operation:

The parameterized constructor call of MyString
Copy function call of MyString
Destructor call of MyString
Destructor call of MyString




Reload shift left > >

If you want to

cout<<str<<endl; The code will definitely report an error, because STR is an object of MyString type, < < I don't know this.

Therefore, it is necessary to overload the shift left operator at this time, which can be implemented in the function file. (you need to use global functions to cooperate with friends for overloading)

Implementation function:

ostream& operator<<(ostream& cout, MyString& str) //Overload shift left operator
{
	cout << str.pString;//pString here is a private property in the class, so you need to set friends for the entire overloaded function in the original class (in the header file)
	return cout;
}

Header file:

class MyString
{
	friend ostream& operator<<(ostream& cout, MyString& str);//Set friends
public:
private:
};

So cout < < STR < < endl; This line of code can be called.




Heavy load shift right<<

If you want to

cin>>str; The code will also report an error, because STR is an object of MyString type, and > > doesn't know this.

Therefore, it is necessary to overload the > > shift right operator at this time, which can be implemented in the function file. (you need to use global functions to cooperate with friends for overloading)


Implementation function:

istream& operator>>(istream& cin, MyString& str)//Overload shift right operator
{
	//The original heap data should be cleared first
	if (str.pString)//pString here is a private property in the object, so you need to add the friend declaration of this overloaded function to the original class
	{
		delete[] str.pString;
		str.pString = NULL;
	}
	//Don't rush to directly pass the input content to pString. You can first open up a temporary array to record the input content.
	char buf[1024];
	cin >> buf;
	//Because it is a self overloaded function, the pString of str was deleted just now. Now you need to re apply for space.
	str.pString = new char[strlen(buf) + 1];
	strcpy(str.pString, buf);
	str.m_Size = strlen(buf);//Don't forget to include the size in str's size, because after passing in a string of characters, the length in the object remains the original length, so it needs to be modified.
	cout << str.m_Size << endl;

	return cin;
}


Header file:

class MyString//The string should maintain a character array in the heap
{
	friend istream& operator>>(istream& cin, MyString& str);
public:
private:
};

Then you can assign a value to pString of str.

When overloading the shift right operator, it seems that it is not important to empty the contents of the original string. It can work normally even if it is deleted. Create a temporary array record (the array size can be large enough), then assign a value, and finally don't forget to change the size in the object.





Overload assignment=

If you want to:

str2 = str1

The code will collapse when the two objects are run directly, because the full copy will cause the problem of shallow copy when deleting the object.

If you want to:

str2 = "abc"

It is certainly not possible to assign a string to a string directly.

Therefore, you need to overload the = operator with two different parameters. (one parameter is an object and the other parameter is a string)

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

The return value must be MyString &, because after using the = operator, you want to return itself (str2) Note that the scope MyString:: should be written in the implementation file after the header function is declared, and the scope of this class should be written after the return value type and in front of the function name.

Overloaded = operator, different from overloaded shift left and shift right operators, you don't need to use global function overloads like < < and > > and you need to use member functions

Header file:

#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);//Parameterized constructor
	MyString(const MyString& str);//copy constructor 
	~MyString();//Destruct (it is necessary, because this class also maintains a pointer, which needs to be released by itself)

	//Overload two = operators
	MyString& operator=(const MyString& str);
	MyString& operator=(const char* str);


private:
	char* pString;//Maintain the character array opened in the heap
	int m_Size;//String length (not counted \ 0)
};

Implementation file:

#define _CRT_SECURE_NO_WARNINGS 1
#include"myString.h";

MyString & MyString::operator=(const MyString & str)
{
	//First judge whether there is content in the original heap area. If so, release it first.
	if (this->pString)
	{
		delete[]this->pString;
		this->pString = NULL;
	}
	//Make a deep copy
	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)
{
	//First judge whether there is content in the original heap area. If so, release it first.
	if (this->pString)
	{
		delete[]this->pString;
		this->pString = NULL;
	}
	//Make a deep copy
	this->pString = new char[strlen(str) + 1];
	strcpy(this->pString, str);
	this->m_Size = strlen(str);
	return *this;
}

Test file:

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


int main()
{
	MyString str = "abc";//Call default construct
	MyString str2 = "bcd";
	str = str2;
	MyString str3 = "abc";
	cout << str << endl;//bcd
	cout << str3 << endl;//abc
	return 0;
}



Overloaded brackets []

If item

str2[0] = 'a';

No, because [] doesn't know str.

So you need to overload brackets []. Overload directly using member functions.

//In the header file:
char operator[](int index);
//In the implementation function:
char MyString::operator[](int index)//Overloaded brackets
{
	return this->pString[index];
}

Normally, it's OK to return the value of char type, so it's readable.

However, if you want to modify str2p[1] as an lvalue, you need to return the ontology char&

//In the header file:
char& operator[](int index);
//In the implementation function:
char& MyString::operator[](int index)//Overloaded brackets
{
	return this->pString[index];
}
//In the Test file

int main()
{
	MyString str2 = "bcd";
	cout << str2[1] << endl;//c
	str2[1] = 'z';
	cout << str2[1] << endl;//z
	return 0;
}



Overload plus sign+

If you want to achieve:

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

This will definitely report an error, because + doesn't know the object or such a string
Therefore, you need to overload + and use member functions with only one parameter.

From the meaning of the question, pass in an object and then return an object, or pass in a string and return an object. (the premise is that the first one passed in is regarded as the calling object)

Header file:

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

Implementation file:

//Overload + operator
MyString MyString:: operator+(const MyString& str)
{
	//abc itself passes in def. At the beginning, you should calculate the memory space to be opened up.
	int newSize = this->m_Size + strlen(str.pString) + 1;
	char* temp = new char[newSize];//Then open up this space and point the temp pointer to it.
	memset(temp, 0, newSize);//Empty all the contents of the space.
	strcat(temp, this->pString);//Throw this - > pstring into temp.
	strcat(temp, str.pString);//Then throw in the str string so that they combine themselves.

	//However, the created new string cannot be returned directly because an object needs to be returned
	//So create a new object, then assign the string to the new object through the constructor, and finally return the new object.
	MyString newString = temp;

	//Another point is that the created class needs to be released when the space temp is used up
	delete[]temp;

	return newString;	
}

MyString MyString:: operator+(const char* str)
{
	//abc itself passes in def. At the beginning, you should calculate the memory space to be opened up.
	int newSize = this->m_Size + strlen(str) + 1;
	char* temp = new char[newSize];//Then open up this space and point the temp pointer to it.
	memset(temp, 0, newSize);//Empty all the contents of the space.
	strcat(temp, this->pString);//Throw this - > pstring into temp.
	strcat(temp, str);//Then throw in the str string so that they combine themselves.

	//However, the created new string cannot be returned directly because an object needs to be returned
	//So create a new object, then assign the string to the new object through the constructor, and finally return the new object.
	MyString newString = temp;

	//Another point is that the created class needs to be released when the space temp is used up
	delete[]temp;

	return newString;
}

The two + overloaded functions are almost the same

TEST file

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;
}



Heavy load==

ps supplement: in the strcmp function, if two strings are equal, it returns 0. If they are not equal, it returns 1.

It also provides two overloaded functions:

//Header file:
//Overload = = operator
bool operator==(const MyString& str);
bool operator==(const char* str);

//Implementation file:
//Overload = = operator
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 file
int main()
{
	MyString str3 = "abc";
	MyString str4 = "def";
	MyString str5 = str3 + str4;
	cout << str5 << endl;//abcdef
	if (str5 == str5)
	{
		cout << "Are equal" << endl;
	}
	else
	{
		cout << "Are not equal" << endl;
	}

	//The results are equal.
	return 0;
}
	

Keywords: C++ MFC

Added by rekha on Sun, 13 Feb 2022 17:12:37 +0200