String C++完整实现。

String C++实现

改进:

/*
版权信息:狼
文件名称:String.h
文件标识:
摘    要:对于上版本简易的String进行优化跟进。
  改进
  1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需求大于定义大小时利用动态分配
  2.实现大块内存的写时拷贝功能,提高效率,优化空间利用
  3.类似new[]实现机制:将动态内存块大小信息保存为隐藏“头”
  4.对于大块内存采取动态增长型。不够时扩容为当前大小的2倍

当前版本:1.2
修 改 者:狼
完成日期:2015-12-12

取代版本:1.1
原 作 者:狼
完成日期:2015-12-11
*/
#ifndef _STRING_H
#define _STRING_H
#include<iostream>
using namespace ::std;

namespace COW
{
 class String
 {
 friend  void Check_Interface(String &S);
 friend ostream& operator << (ostream&os, String &Str);

 public:
  String(const char* str = "");
  String(String& Str);
  String& operator=(const String& Str);
  ~String();

 public:
  int Find(char ch)const;
  int Find(const char* str)const;
  void PushBack(char ch);
  void Insert(size_t pos, char ch);
  void Insert(size_t pos, const char* str);
  bool Erase(size_t pos);
  bool Erase(size_t pos, size_t n);
  size_t Strlen()const;

 private:
  void InitInfo(size_t inUse); //初始化capacity and inUse
  void CheckMem(size_t add = 1); //对于内存操作时的增否处理
  const char* GetAdd()const; //获取当前字符串所在位置
  size_t capacity()const;  //读取头,获得有效容量长度
  int inCount()const;  //获取引用计数
  void decrease();//减少引用计数。
  void increase();//增加引用计数。

 private:
  enum{BufSize = 16};  //利用枚举进行数组初始化
  char *_str;    //内容块,“头”代替capacity
  char _buff[BufSize]; // 小内存块处理。
 };
 ostream& operator << (ostream&os, String &Str);
}
#endif

#include"String.h"
#pragma warning (disable:4351 4996)

using namespace COW;

String::String(const char *Str)
 :_str(NULL)
 , _buff()
{
 //采用动态分配
 if (strlen(Str) >= BufSize)
 {
  //1.记录引用计数,2.记录当前有效大小   strlen()+1
  _str = new char[strlen(Str) + 5+4];

  InitInfo(strlen(Str)+1);

  char *cur = const_cast<char*>(GetAdd());
  strcpy(cur, Str); //有效位置
 }
 else//静态存储
 {
  strcpy(_buff, Str);
 }
}
String::String(String& Str)
 :_str(NULL)
 , _buff()
{
 // 1.如果是小块内存。浅拷贝
 if (Str._str == NULL)
 {
  strcpy(_buff,Str._buff);
 }
 else
 {
  _str = Str._str;
  this->increase();
 }
}
String& String::operator=(const String& Str)
{
 //都是动态 且指向一处
 if (_str == Str._str&&_str != NULL)
  return *this;

 //两个都是小内存时
 if (Str._str == NULL&&_str==NULL)
 {
  strcpy(_buff, Str._buff);
 }
 //如果*this 是小内存(不存在内存变更,不用处理buff),Str 动态。
 else if (_str==NULL&&Str._str!=NULL)
 {
  _str = Str._str;
  this->increase();
 }

 //*this 动态,Str小内存。减少*this._str计数。。.更改buff
 else if(_str!=NULL&&Str._str==NULL)
 {
  this->decrease();
  _str = NULL;
  strcpy(_buff,Str._buff);
 }

 //两个都是动态分配 但不同
 else
 {
  this->decrease();
  _str = Str._str;
  this->increase();
 }
 return *this;
}
///////问题1...命名空间冲突时
ostream& COW::operator << (ostream&os, String &Str)
{
 if (Str._str)
 {
  os << (Str._str + 8);
 }
 else
  os << Str._buff;
 return os;
}

////////////////////////core_func
inline void String::increase()
{
 *(int*)_str += 1;
}

inline void String::decrease()
{
 if (_str != NULL)
 {

  //////问题2,,每次decrease之后必须变更_str..为NULL或者指向新块
  if ((*(int*)_str) - 1 != 0)
  {
   (*(int*)_str) -= 1;
  }
  else
  {
   delete[]_str;
   _str = NULL;
  }
 }
}
String::~String()
{
 this->decrease();
 _str = NULL;
}

///获取字符串的核心地址
const char *String::GetAdd()const
{
 //忘记给对正确起始点
 return _str ? _str+8 : _buff;
}

size_t String::capacity()const
{
 return *((int*)_str+1);
}
int String::inCount()const
{
 return *((int*)_str);
}

//设置头 信息
void String::InitInfo(size_t inUse)
{
 *((int*)_str) = 1;
 *((int*)_str + 1) = inUse;
}

void String::CheckMem(size_t add)
{
 if (_str == NULL)
 {
  //小内存,且变动后不越界。
  if (strlen(_buff) + add < 16)
   return;
  //小内存,变动后越界
  else
  {
   _str = new char[strlen(_buff) + add + 9];

   InitInfo(strlen(_buff) + add+1);
  }
 }
 else
 {
  //容量足够,且引用计数为1;
  if (strlen(_str + 8) + add < capacity()&& inCount()==1)
  {
   //,但是写时拷贝,需要更新空间,所以顺带增加宽度
   //if (inCount() != 1)
   //{
   // int new_mem = (strlen(_str + 8) + add) * 2 + 9;
   // this->decrease();
   //
   // _str = new char[new_mem];
   // //少了+1  会造成后续始终有一个字节浪费问题
   // InitInfo((strlen(_str + 8) + add) * 2 + 1);
   //}
   return;
  }
  else
  {
   //扩容后,容量为当前串长的2倍。  匹配使用,,用realloc 会出现问题
   //realloc(_str, (strlen(_str+8) + add) * 2 + 8);

   //不能delete,,写时拷贝忘记了。。引用计数问题忘记了?
   //delete[] _str;、
   //先计算所需,再decrease(),因为可能赋值为NULL,不得求Strlen
   int new_mem = (strlen(_str + 8) + add) * 2 + 9;
   this->decrease();
   _str = new char[new_mem];

   //少了+1  会造成后续始终有一个字节浪费问题
   InitInfo(new_mem-8);
  }
 }
}

//////////////////publicFunc
int String::Find(char ch)const
{
 char *cur = const_cast<char*>(GetAdd());
 int pos = -1;
 while (*cur)
 {
  pos++;
  if (*cur == ch)
   return pos;
  cur++;
 }
 return -1;
}

int String::Find(const char* str)const
{
 if (str == NULL)
  return -1;
 char *cur = const_cast<char*>(GetAdd());
 int pos = -1;
 int len = strlen(str);
 while (*(cur + len))
 {
  pos++;
  ///简直,,if 后边有个;bug
  if (strncmp(cur, str, len) == 0)
   return pos;
  cur++;
 }
 return -1;
}
void String::PushBack(char ch)
{
 //首先保存内容。然后判断空间,然后存储。
 char p[100];
 char *st = const_cast<char*>(GetAdd());

 strcpy(p, st);

 CheckMem(); //默认为1;

 st = const_cast<char*>(GetAdd());
 strcpy(st, p);

 st[strlen(st)+1] = ‘\0‘;
 st[strlen(st)] = ch;

}

size_t String::Strlen()const
{
 return strlen(GetAdd());
}

void String::Insert(size_t pos, char ch)
{
 //越界有效化。。。
 if (pos >= Strlen())
 {
  pos = Strlen()-1;
 }
 //首先保存内容。然后判断空间,然后存储。
 char p[100];
 char *st = const_cast<char*>(GetAdd());

 strcpy(p, st);

 CheckMem(); //默认为1;

 st = const_cast<char*>(GetAdd());
 strncpy(st, p, pos);

 st[pos] = ch;

 //不能用strcat,前边字符串不存在‘\0‘
 strcpy(st + pos + 1, p + pos);
}

void String::Insert(size_t pos, const char* str)
{
 //越界有效化。。。
 if (pos >= Strlen())
 {
  pos = Strlen();
 }
 //首先保存内容。然后判断空间,然后存储。
 char p[100];
 char *st = const_cast<char*>(GetAdd());

 strcpy(p, st);

 int len = strlen(str);
 CheckMem(len); //默认为1;

 st = const_cast<char*>(GetAdd());
 strncpy(st, p, pos);

 strcpy(st + pos, str);

 strcpy(st + pos + len, p + pos);
}

bool String::Erase(size_t pos)
{
 char *cur = const_cast<char*>(GetAdd());
 size_t len = Strlen();
 if (pos >= len)
  return false;
 memmove(cur+ pos, cur + pos + 1, len + 1);
 return true;
}

bool String::Erase(size_t pos, size_t n)
{
 char *cur = const_cast<char*>(GetAdd());
 size_t len = strlen(cur + pos + 1);
 if (pos >= len)
  return false;
 memmove(cur + pos, cur + pos + n, len + 1);
 return true;
}

//////friend 友员函数不能跨越命名空间访问私有成员????????????
void Check_Interface(COW::String &S)
{
 //char  COW::String::*ch = S._str;
 //const char *ch = S._str;
 cout << endl;
}

///////////////////////////Test
#include"String.h"
using namespace COW;

void Test_COPY_EPU()
{

 String S1("hellow     world!");
 String S2("hellow world");
 String S3;
 S3 = S2;
 String S4;
 S4 = S1;
 //少对多,少对少
 cout << "小内存赋值" << S3 << endl;
 cout << "小内存赋值" << S4 << endl;

 //多对多,多对少
 String S5("change             world");
 S5 = S1;
 cout << S5 << endl;
 S5 = S2;
 cout << S5 << endl;
 //多多且等
 String S6 = S1;
 S6 = S1;
 cout << S6 << endl;
}
void Test_Find_Erase()
{
 String S1("hellow     world!");
 String S2("hellow world");
 cout << S1.Find(‘l‘) << endl;
 cout << S2.Find(‘z‘) << endl;
 cout << S1.Find("low") << endl;
 char*p = NULL;
 cout << S2.Find(p) << endl;

 S2.Erase(4);
 S2.Erase(4, 3);
 cout << S2 << endl;
}

void Test_Push_Insert()
{
 String S1("hellow     world!");
 String S2("hellow   world");
 //S1.PushBack(‘l‘);
 //S1.PushBack(‘v‘);
 //S2.PushBack(‘l‘);
 //S2.PushBack(‘v‘);
 int pos = S1.Find(‘l‘);
 S1.Insert(pos,‘v‘);
 S1.Insert(100, "lv");
 cout << S1 << endl;
 cout << S2 << endl;

}
void main()
{
 //Test_COPY_EPU();
 //Test_Find_Erase();
 Test_Push_Insert();
}
时间: 2024-08-09 02:06:28

String C++完整实现。的相关文章

String .split完整测试

数据库中有一个字段是用逗号拼接的,每个字段都是另一个表中的主键,需要查询子元素,或者给这个逗号拼接的字段删减元素时,就会有比较复杂的字符串解析. 为此,先测试了String提供的split方法. 可以看到,元素之前的逗号都被认为是分隔符,劈开后进了字符串数组:            而之后的逗号被抛弃.         所以在做字段解析时,应该把重点放在"去除前面的逗号",这样才能保证字符串数组的素值是那个表的主键,而不是空串. @Test public void testSplit(

H5-Web存储API使用

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>sessionStorage实现记事本功能</title> 5 <meta charset="utf-8" /> 6 </head> 7 8 <body> 9 <input type="text" id="note"><br> 10 <

classloader

4 Jboss 启动及加载过程 详细参考: http://tech.it168.com/j/2007-06-27/200706271521984.shtml 1) org.jboss.Main.main(String[]) 为入口 . 2) main 函数创建一个名叫” jboss ”的线程组 , 然后创建一个属于该组的线程 , 在线程中执行 boot 方法 . 3) boot 方法首先处理 main 函数中的参数 ( 及一些其它的系统环境设置 ), 接着就用系统的属性创建了org.jboss.

Java反射-再次认识

最近的学习发现在很多方面,基础知识掌握的还很不牢固,所以对于架构.知识点等属于那种问啥啥知道,做啥啥不出来的那种类型.前些日子,老师一直在抓基础,做什么都要从最简单的demo开始,只有懂了原理之后再去用一些高深的东西如框架等才会理解的更深刻.现在首先需要理解的就是基本上每个Java框架都在用的反射技术. 要想理解反射,首先得了解类的加载过程,看下图: 我们的源代码经过编译之后变成字节码,然后在JVM中运行时通过类加载器加载字节码在内存中生成Class类对象,这个Class类对象内包含有field

【转】 Android 开发 之 JNI入门 - NDK从入门到精通

原文网址:http://blog.csdn.net/shulianghan/article/details/18964835 NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub - https://github.com/han1202012/NDKHelloworld.git -- Java传递参数给C语言实例程序 : GitHub - https://github.com/han1202012/NDKParameterPassing.git --C语言回调Java方法示例

JSON 入门指南

JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与 JavaScript 的交互.本文将快速讲解 JSON 格式,并通过代码示例演示如何分别在客户端和服务器端进行 JSON 格式数据的处理. 尽管有许多宣传关于 XML 如何拥有跨平台,跨语言的优势,然而,除非应用于 Web Services,否则,在普通的 Web 应用中,开发者经常为 XML 的解析伤透了脑筋,无论是服务器端生成或处理 XML,还是客户端用 JavaScript

python之-- socket 基础篇

socket 网络模块 注意事项:在python3中,所有数据的传输必须用bytes类型(bytes只支持ascii码)所以在发送数据的时候要么在发送的字符串前面加 'b',要么使用encode('utf-8')进行转换成bytes类型发送,但是在接收端必须用decode()进行转码. 1.Socket 类型 套接字格式: socket(family,type[,protocal]) 使用给定的地址族(网络层).套接字类型(传输层).协议编号(默认为0)来创建套接字. socket类型 描述 s

19. yum 常用命令《Mr.Robot》

前言:其实yum就是能自动下载rpm包,并且自动安装依赖关系.就如同php里的composer一样!!!<Mr.Robot> ---------------------------------------------------- 1. 安装 #  yum install -y  vim 2. 卸载 #  yum remove -y  vim 3. 升级 #  yum update  (升级所有包,改变软件设置和系统设置,系统版本内核都升级) #  yum upgrade  (升级所有包,不改

NDK开发基本知识

(3) NDK开发中乱码问题 解决乱码思路 : C语言编译的时候用的是 ISO-8859-1 码表进行编码, 如果我们使用C语言jni开发, 需要进行转码操作; -- 将ISO-8859-1转为UTF-8字符: String string = new String(str.getBytes("iso8859-1"), "UTF-8"); 示例 : 添加中文jni调用 : 将jni中的hello.c 中返回的字符串修改为中文, 重新编译 .so 静态库文件; -- 修