String类的实现与深浅拷贝问题

C++实现string类是理解类和对象这个概念的基础,也能了解C++语法的特性--用户对内存的自主管理,通过类创建出一个对象的过程,首先要调用构造函数,经过一系列的操作,当退出对象所在的作用域时,便会调用析构函数,C++支持隐式的调用构造、析构等函数,但经常隐式调用并不能正确的管理内存,所以我们需要重写这两个函数,以及拷贝构造,和赋值运算符的重载。

string类的结构设计

string类型的设计源于我们队字符串操作及封装的需求,所以string类是对C语言中的字符串进行优化故设计代码如下:

class String
{
     private:
     char*_str;  
};

通过指针管理动态内存所开辟的空间,然后进行所需要的操作即可。

string隐式调用会存在什么问题

如果我用用以上结构实现string类的话,我们就会用到动态内存开辟来实现字符串的创建,在这里我们用new来实现string类的构造函数用以创建新的字符串,对其惊醒操作时会出现以下问题:

1.构造新的字符串时我们的构造函数并不能直接给字符串赋值。

2.调用析构函数时,因为在栈上开辟的仅仅是一个指针,如果不能对废弃空间进行合理的 释放,那么会出现内存泄漏问题。

3.如果重写析构函数,让其功能为对_str的释放,当我们进行拷贝构造及赋值运算符的重载时会导致一块空间被释放多次从而崩溃。

解决方案

1.构造函数开辟空间,使用strcpy进行拷贝,不直接赋值。析构函数对字符指针进行空间释放,拷贝构造以及赋值运算符的重载另外开辟一块空间,对指针所指向的内容进行拷贝。(深拷贝)

2.构造函数开辟空间,使用strcpy进行拷贝,不直接赋值。创建一个数据管理同一块空间被引用的次数(引用计数),若计数为1,析构函数对其空间进行释放,若不为1,计数自减一次。(浅拷贝)

代码实现

#define _CRT_SECURE_NO_WARNINGS 1//深拷贝
#include <iostream>
#include <cassert>
using namespace std;
class String
{
public:
	String(const char*s ="");
	~String();
	/*String(const String&s)
		:_str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
		size = s.size;;
		capacity = s.capacity;
	}*/
	String(const  String &s)
		:_str(NULL)
	{
		String tmp(s._str);
		std::swap(tmp._str, _str);
		std::swap(tmp.size, size);
		std::swap(tmp.capacity, capacity);

	}
	String PushBack(const String &s)
	{
		return *this = *this + s;
	}
	int PushFind(char goal)
	{
		assert(_str);
		int i = 0;
		while (char tmp=*(_str+i))
		{
			if (tmp == goal)
				return i;
			i++;
		}
		return -1;
	}
	char* C_str()
	{
		return _str;
	}
	/*String& operator=(const String &s)//传统写法
	{
		if (this != &s)
		{
			char* tmp = new char[strlen(s._str) + 1];
			strcpy(tmp, s._str);
			delete[]_str;
			_str = tmp;
			size = s.size;
			capacity = s.capacity;
		}
		return *this;
	}*/
	String& operator=(String s)//现代写法
	{
		std::swap(s._str, _str);
		std::swap(s.size, size);
		std::swap(s.capacity, capacity);
		return *this;
	}
	String  operator + (const String &s)
	{
		String tmp(this->_str);
		while(tmp.size + s.size-1 > tmp.capacity)
		{
			tmp.capacity *= 2;
			tmp._str = (char*)realloc(tmp._str, capacity);
		}
		strcpy(tmp._str + tmp.size - 1, s._str);
		tmp.size = tmp.size + s.size - 1;
		return tmp;
	}
	String  operator - (const String &s)
	{
		size_t i = 0;
		String tmp(*this);
		char*Cat_Dest = my_strstr(tmp._str, s._str);
		for (; i <= (strlen(Cat_Dest) - strlen(s._str)); ++i)
		{
 			*(Cat_Dest + i) = *(Cat_Dest + strlen(s._str) + i);
		}
		return tmp;
	}
	char & operator [] (const size_t i)const
	{
		assert(i <= strlen(_str));
		return _str[i];
	}
private:
	char* my_strstr(char*str, char*sub)
	{
		assert(str);
		assert(sub);
		char*Str_tail = str;
		char*Hstr = str;
		char*Hsub = sub;
		while (*Str_tail)
			Str_tail++;
		if (!(*sub))	 	 //处理减去空串,若为子串空串,则返回父串尾
			return Str_tail; 
		while ((*str)&&(*sub)//处理非空串
			&&(strlen(Hstr)>=strlen(Hsub)))
		{

			str = Hstr++;
			sub = Hsub;
			while (*sub == *str
				&&(*sub))
			{
				sub++;
				str++;
			}
		}
		if (*sub)
			return Str_tail;//若不为子串,则返回父串尾
		return Hstr-1;      //若为子串则返回子串在父串中所在位置
	}
	char *_str; 
	size_t size;
	size_t capacity;
};
String::String(const char*s)
:_str(new char[strlen(s)+1])
{
	strcpy(_str, s);
	size = strlen(s) + 1;
	capacity = size * 2;
}
String::~String()
{
	if (_str)
	{
 		delete[] _str;
		_str = NULL;
		capacity = 0;
		size = 0;
	}
}
#include<iostream>//浅拷贝
class String
{
public:
	String(char*str)
		:_str(new char[strlen(str)+1])//此外浅拷贝还可以将_RefCount与_str一并开辟
	{//通过强制类型转换操作同一块空间减少内存碎片的操作
		strcpy(_str,str);
		*_RefCount = 1;
	}
	~String()
	{
		if (--(*_RefCount) == 0)
			delete[]_str;
	}
	String(const String& s)
	{
		if (_str != s._str)
		{
			_RefCount = s._RefCount;
			_str = s._str;
			++(*_RefCount);
		}
	}
	String operator = (String &s)
	{
		if (_str != s._str)
		{
			if (--(*_RefCount) == 0)
				delete[]_str;
			_str = s._str;
			_RefCount = s._RefCount;
			++(*_RefCount);        
		}
	}
private:
	char* _str;
	int* _RefCount;
};

如有错误,希望批评指正。

时间: 2024-10-10 12:42:14

String类的实现与深浅拷贝问题的相关文章

Day2.字典,字符串,元组,字典,集合set,类的初步认识,深浅拷贝

Python之路[第二篇]:Python基础(一) 入门知识拾遗 一.作用域 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. if 1==1: name = 'JasonWang' print name 下面的结论对吗? 外层变量,可以被内层变量使用 内层变量,无法被外层变量使用 二.三元运算 result = 值1 if 条件 else 值2 #三目运算符 name = 'aa' if 1 == 2 else 'SB'print(name)SB 如果条件为真:res

string类的写时才拷贝(Copy-On-Write)

写时才拷贝(Copy-On-Write)的原理是什么? Copy-On-Write,其实是使用了一个计数器.当第一个类构造时,计数器初始值为1,string类的构造函数会根据传入的参数从堆上分配内存,当其他类需要这块内存时,这个计数器自动累加.当有析构函数时,这个计数器会减1,直到当计数器为1时,即最后一个类析构.此时,程序才会真正的delete这块从堆上分配的内存. 2.  string在什么情况下会发生写时才拷贝(Copy-On-Write)? 当string类中发生内存共享且内容发生改变时

简单的String类实现及写时拷贝

#include<iostream> using namespace std; class String { public: /*String(const char* str=" ") :_str(new char[strlen(str)+1]) { strcpy(_str, str); } */ String(const char* str = " ") { if (str == NULL) { _str = new char; _str[0] = '

C++ String类写时拷贝

    维基百科:     写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略.其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变.这过程对其他的调用者都是透明的(transparently).此作法主要的优点是如果调用者没有修

1——自我实现一个简洁版的String类

在C++中有C没有的string字符串类型,string类型的数据其实是一个指向字符串首地址的指针变量,因此在string类的默认成员函数拷贝构造和赋值运算符的重载就会涉及到深浅拷贝的问题,一不小心要么就是内存泄露要么就是多次释放同一块空间导致程序崩溃,下面就来模拟实现一个简洁版的String类: 既然是指向一个字符串的指针,因此类的成员变量就需要有一个char*类型的指针: #include <iostream> #include <string.h> using namespa

C++实现String类

这是一道十分经典的面试题,可以短时间内考查学生对C++的掌握是否全面,答案要包括C++类的多数知识,保证编写的String类可以完成赋值.拷贝.定义变量等功能. #include<iostream> using namespace std; class String { public: String(const char *str=NULL); String(const String &other); ~String(void); String &operator =(cons

[经典面试题][百度]c++实现STL中的string类

题目 请用c++ 实现stl中的string类,实现构造,拷贝构造,析构,赋值,比较,字符串相加,获取长度及子串等功能. 代码 /*------------------------------------- * 日期:2015-03-31 * 作者:SJF0115 * 题目: 实现string类 * 来源:百度 * 博客: ------------------------------------*/ #include <iostream> #include <cstring> us

string类的深浅拷贝问题

字符串是我们在编写程序的时候经常用的到的.C++库已经帮我们实现了一个功能更加强大的字符串类string,我们要去了解它是怎么实现的. 只要是涉及到 string类的地方多少都会涉及到深浅拷贝的问题.在C++中,在用一个对象初始化另一个对象时,只复制了成员,并没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制.       深拷贝是将指向内容复制到给当前对象新分配的缓冲区中的一种复制方式.     下面就是自己实现的深拷贝string类: #include<iostream> #i

【C/C++学院】0819-/类的成员函数与const-mutable /构造与析构/拷贝构造deletedefault以及深浅拷贝/静态成员函数成员变量类在内存的存储默认参数/友元类以及友元函数

类的成员函数与const-mutable 成员函数 Fushu.h #pragma once #include <iostream> class fushu { public: int x; int y; public: fushu(); ~fushu(); void show(); inline void showall(int x, int y);//显式内联 void setxy(int x, int y);//编译器优化,默认隐式内联 void show(int x, int y);