自实现 MyString 类

自实现 myString 类主要目的是剖析系统内部的 string 类的一些实现方法以及加强对类封装、运算符重载等特性的掌握。其中包含了几项非常重要的功能实现。

  1. 使用构造器创建对象。
  2. 拷贝构造器创建对象。
  3. 赋值运算符重载构造对象。
  4. []运算符重载构造对象数组。
  5. ==运算符重载判断对象是否相等。
  6. +运算符重载实现对象相加。
  7. >> << 流输入输出运算符实现打印和输入。

具体的实现代码分三个部分,一个 MyString.h 文件,包含类的声明和结构。一个 MyString.cpp 文件,包含类的成员及友元函数实现。最终是一个 main.cpp 来测试我们自己的 MyString 类是否可以正常使用。

【myString.h】

#pragma once
#include <iostream>

class CMyString
{
public:
	// 构造器
	CMyString(char* str = NULL);
	// 拷贝构造器原型,another为传递进来的同类对象
	CMyString(const CMyString & another);
	// 赋值运算符重载,系统会提供默认,但同样是等位拷贝
	CMyString& operator=(const CMyString & another);
	// []运算符重载
	char operator[](int idx);
	// ==运算符重载
	bool operator==(const CMyString& another);
	// + 运算符重载
	CMyString operator+(const CMyString& another);
	// 流输入输出
	friend std::ostream& operator<<(std::ostream& os, CMyString& another);
	friend std::istream& operator<<(std::istream& is, CMyString& another);

	~CMyString(void);
	char* c_str();
private:
	char* m_str;
};

【myString.cpp】

#include "MyString.h"
#include <cstring>


CMyString::CMyString(char* str)
{
	if (str)
	{
		int len = strlen(str);
		m_str = new char[len + 1];
		strcpy_s(m_str, len + 1, str);
	}
	else
	{
		m_str = new char[1];
		*m_str = '\0';
	}
}

CMyString::CMyString(const CMyString & another)
{
	// 浅拷贝,等位拷贝,会造成 double free
	// 这样操作会导致 m_str 和 another.m_str 指向同一块堆内存
	// 析构时会造成 delete 了同一块内存
	// m_str = another.m_str;

	// 深拷贝,根据another.m_str的长度给对象new一块内存使双方不冲突
	int len = strlen(another.m_str);
	m_str = new char[len + 1];
	strcpy_s(m_str, len + 1, another.m_str);
}

CMyString& CMyString::operator=(const CMyString & another)
{
	if (this == &another)
	{
		// 解决 s6 = s6 会导致崩溃的问题
		return *this;
	}
	delete [] m_str;
	int len = strlen(another.m_str);
	m_str = new char[len + 1];
	strcpy_s(m_str, len + 1, another.m_str);
	return *this;
}

char CMyString::operator[](int idx)
{
	return m_str[idx];
}

bool CMyString::operator==(const CMyString& another)
{
	if (strcmp(this->m_str, another.m_str) == 0)
		return true;
	else
		return false;
}

CMyString CMyString::operator+(const CMyString& another)
{
	int len = 0;
	len = strlen(this->m_str);
	len += strlen(another.m_str);

	CMyString tmp;
	delete [] tmp.m_str;

	tmp.m_str = new char[len + 1];
	memset(tmp.m_str, 0, len + 1);
	strcat_s(tmp.m_str, len + 1, this->m_str);
	strcat_s(tmp.m_str, len + 1, another.m_str);

	return tmp;
}

std::ostream& operator<<(std::ostream& os, CMyString& another)
{
	os << another.m_str;
	return os;
}

std::istream& operator<<(std::istream& is, CMyString& another)
{
	// 先删除了原来保存的数据
	delete [] another.m_str;
	char buffer[BUFSIZ];
	is.getline(buffer, BUFSIZ);
	int length = strlen(buffer);
	another.m_str = new char[length + 1];
	strcpy_s(another.m_str, length + 1, buffer);
	return is;
}

CMyString::~CMyString(void)
{
	std::cout << "delete [] m_str : " << m_str << std::endl;
	delete [] m_str;
}

char* CMyString::c_str()
{
	return m_str;
}

【main.cpp】

#include <iostream>
#include "MyString.h"

using namespace std;

int main(int argc, char* argv[])
{
	// 构造函数,无参数
	CMyString s;
	cout << s.c_str() << endl;

	// 构造函数,有参数
	CMyString s1("china");
	cout << s1.c_str() << endl;

	// 类型转化函数
	CMyString s2 = "china";
	cout << s2.c_str() << endl;

	/*
	 以上均是调用构造器来创建的
	 */

	// 拷贝构造器
	CMyString s3(s2);
	cout << s3.c_str() << endl;

	// 同上
	CMyString s4 = s3;
	cout << s4.c_str() << endl;

	// 赋值,=号运算符重载
	CMyString s5;
	// 等同于 s5.operator= (s4);
	s5 = s4;
	// 同上 相当于s5.operator=(s4.operator=(s3));
	s5 = s4 = s3;
	cout << s5.c_str() << endl;

	CMyString s6("china");
	// 这样操作会进入=号运算符重载的函数内
	// 但刚进入后 s6 就被 delete 了
	// 如果继续后面的操作将会导致访问违规内存而报错
	// 个别系统环境下可能不报错,带不代表就是正确的
	s6 = s6;
	cout << s6.c_str() << endl;
	
	// []运算符重载
	cout << s6[2] << endl;

	// ==运算符重载
	if (s5 == s6) cout << "s5 == s6" << endl;

	// + 运算符重载
	CMyString s7 = s6 + s5;
	cout << s7.c_str() << endl;

	cout << s7 << endl;

	getchar();
	return 0;
}

发表评论