Bootstrap

(C++)模拟实现string(1)

本章目的主要是实现四个默认成员函数(加深理解深浅拷贝)

我将代码分成了两个文件 头文件和一个test.c文件

头文件:

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;//写了这个 下文就不需要像我一样给每一个cout和cin标明命名空间了
namespace greenbyte//因为标准库里面也有string,所以我们需要自己弄一个命名空间,可以随便取名
{
	class string
	{
	public:
		string(const char* str="\0")//这里缺省值不是nullptr,因为strlen函数会直接对字符串解引用,我们需要防止对空指针解引用问题
			:_str(new char[strlen(str)+1])//这里把空间开在堆上是为了方便扩容; 因为strlen函数遇到\0停止,所以需要+1
		{
			strcpy(_str, str);//开辟空间之后把数据拷贝过来 strcpy会拷贝反斜杠0
		}
		~string()
		{
			delete[]_str;
			_str = nullptr;
		}
		string(const string& s)
			:_str ( new char[strlen(s._str) + 1])//拷贝构造函数也是构造函数,同样建议使用初始化列表//这里是深拷贝,如有疑问请看最后一行
		
		{
			strcpy(_str, s._str);
		}
		string& operator=(const string& s)
		{
			if (this != &s)//这里需要讨论是不是s1=s1的调用情况,且比较‘=’左右是否是同一个对象必须比较地址,比较值是错误的
			{
				delete[]_str;//这里先释放_str原本指向的空间,否则这一块空间我们再也找不到了,会导致内存泄漏
				_str = new char[strlen(s._str) + 1];
				strcpy(_str, s._str);
				return *this;//这里返回对象,是为了实现s2=s1=s3的连等于情况,想象一下,如果该函数没有返回值,那么s1=s3实现之后,s2=? 问号处什么也没有
			}
		}
		const char* c_str()//该函数在string类里也有,可以返回指向_str的指针,此处可供我们打印
		{
			return _str;
		}
		int size()
		{
			return strlen(_str) ;
		}
		char& operator[](size_t pos)
		{
			return _str[pos];//这里return字符的引用,是为了达到可以通过'[]'修改字符的目的
		}
	private:
		char* _str;
};
	void test1()
	{
		string s1;//无参
		std::cout << s1.c_str()<<std::endl;
		string s2("hello");//传参
		std::cout << s2.c_str()<<std::endl;
		string s3;//拷贝构造
		s3 = s2;
		std::cout << s3.c_str()<<std::endl;
		for (size_t i = 0; i < s3.size(); i++)//遍历-只读取数据
		{
			std::cout << s3[i] << " ";
		}
		cout << endl;
		for (size_t i = 0; i < s3.size(); i++)//遍历-修改数据
		{
			s3[i] += 1;
			std::cout << s3[i] << " ";
		}

	}
}
//默认拷贝函数和赋值运算符重载函数都是浅拷贝,也叫值拷贝,即一个字节一个字节地拷贝,会导致只把对象的_str这一指针拷贝了,如全是0x22ff4433,却没有重新开辟空间,导致两个对象的_str指向同一块空间,一动全动,这不是我们想要的
// 且最后两个对象同时调用析构函数,对同一块地址空间释放两次也会导致程序崩溃。解决方法即重新为左侧对象开辟空间

关于最后一行注释中的“同时”,这里修改一下,并不是同时,对象建立在栈上,符合先创建的后析构,我在这里想说的意思是,两个对象都会调用析构函数~

test.c文件:

#include"string.h"
int main()
{
	greenbyte::test1();
	
	return 0;
}

我将知识点写成了注释,欢迎初学者复制代码在本地ide里面查看(注释部分会更清晰)

如果有疑问或者想要与我讨论,欢迎评论~

下一章实现增删查改

 

;