浅谈C++ 字符串类 —— string类
来源:互联网 发布:java集合转换为字符串 编辑:程序博客网 时间:2024/06/04 08:53
摘要:C++ string类的编写,牵涉最多的就是内存管理了。对此不太了解的,推荐阅读《C++ 构造函数执行原理》、《 C++ 拷贝构造函数》两篇文章,在文中有详细介绍了类的创建及销毁过程。同时,阐述了拷贝构造函数的调用时机。在此基础上,需要学习如何进行运算符重载以及学习const的相关用法,该文《C++ 修饰符const、static、extern、ref、volatile、explicit总结》中的const部分,阐述十分详尽并附带案例。学习起来很方便。在这些知识的基础上,那么就开始动手构建String类吧。
目录:
--------------------------------------------
- String();
- String(const char* str_ptr);
- String(const String& str_ref);
- ~String();
- String& operator= (const String& str_ref);
- String operator+ (const String& str_ref) const;
- String& operator+= (const String& str_ref);
- char operator[] (const unsigned int index) const;
- bool operator== (const String& str_ref) const;
- unsigned int size() const;
- const char* c_str() const;
- friend std::ostream& operator<< (std::ostream& output, const String& str_ref);
- friend std::istream& operator>> (std::istream& input, String& str_ref);
C++中,字符串类的名称为string,位于std命名空间之下。本文模仿构造类String,同时将其位于mango命名空间之下。这里需要强调的是,标准库string类是用模板实现的,用以兼容大多数类型的情况,所以如果需要写底层库dll的话(库中.h文件不允许使用模板),string类是不可以的使用的,这时如果非要使用string的话,可以考虑两种方式:①不用模板,自己实现String类;②使用桥接技术,将.h内容转移到.cpp中实现。
以下为String类构造情况,
#pragma once#include <iostream>namespace mango{ class String { public: String(); String(const char* str_ptr); String(const String& str_ref); ~String(); String& operator= (const String& str_ref); String operator+ (const String& str_ref) const; String& operator+= (const String& str_ref); char operator[] (const unsigned int index) const; bool operator== (const String& str_ref) const; unsigned int size() const; const char* c_str() const; protected: friend std::ostream& operator<< (std::ostream& output, const String& str_ref); friend std::istream& operator>> (std::istream& input, String& str_ref); private: char* m_str_ptr; unsigned int m_str_size; };}
String();
构造函数中,为了对内存进行统一释放,全部采用new char[]方式。这样,释放时就统一为delete[]。
String::String(){ m_str_size = 0; m_str_ptr = new char[1]; if (m_str_ptr == NULL){ throw std::length_error("Memory is not enough."); } *m_str_ptr = '\0';}
String(const char* str_ptr);
String::String(const char* str_ptr){ m_str_size = strlen(str_ptr); m_str_ptr = new char[m_str_size + 1]; if (m_str_ptr == NULL){ throw std::length_error("Memory is not enough."); } strcpy(m_str_ptr, str_ptr);}
String(const String& str_ref);
这里注意,拷贝构造函数的参数必须为引用类型,否则将会陷入无休止的递归中。当前,如果不为&,编译器将会直接报错。
String::String(const String& str_ref){ m_str_size = str_ref.m_str_size; m_str_ptr = new char[m_str_size + 1]; if (m_str_ptr == NULL){ throw std::length_error("Memory is not enough."); } strcpy(m_str_ptr, str_ref.m_str_ptr);}
~String();
由于构造函数、拷贝构造函数已经统一了内存开辟方式。这里释放内存时,也全部使用delete[]。
String::~String(){ if (m_str_ptr){ delete[] m_str_ptr; m_str_ptr = NULL; }}
String& operator= (const String& str_ref);
为了进一步提高赋值效率,首先对自身赋值进行检查。另外一点需要特别注意的是,对this类中的m_str_ptr指针进行重定向时,一定要释放掉旧内存。否则必然内存泄漏。
String& String::operator = (const String& str_ref){ if (this == &str_ref){ return (*this); } if (m_str_ptr){ delete[] m_str_ptr; m_str_ptr = NULL; } m_str_size = str_ref.m_str_size; m_str_ptr = new char[m_str_size + 1]; if (m_str_ptr == NULL){ throw std::length_error("Memory is not enough."); } strcpy(m_str_ptr, str_ref.m_str_ptr); return (*this);}
String operator+ (const String& str_ref) const;
运算符+重载过程中,返回值为对象(非引用),这时候将调用拷贝构造函数。由于在拷贝构造函数中已经实现了深拷贝(对成员函数所指向的内存一并拷贝)技术,这种返回不会造成内存泄漏亦或是内存丢失的现象。
String String::operator + (const String& str_ref) const{ if (str_ref.m_str_ptr == '\0'){ return (*this); } else if (m_str_ptr == '\0'){ return str_ref; } String str_merge; if (str_merge.m_str_ptr){ delete[] str_merge.m_str_ptr; str_merge.m_str_ptr = NULL; } str_merge.m_str_size = m_str_size + str_ref.m_str_size; str_merge.m_str_ptr = new char[str_merge.m_str_size + 1]; if (str_merge.m_str_ptr == NULL){ throw std::length_error("Memory is not enough."); } strcpy(str_merge.m_str_ptr, m_str_ptr); strcat(str_merge.m_str_ptr, str_ref.m_str_ptr); return str_merge;}
String& operator+= (const String& str_ref);
该过程中,需要注意的是“+=操作”:①将覆盖原来的this类空间;或者是②this类指针将指向一块新空间。不管如何,这时候一定要释放掉原来的旧空间。
String& String::operator += (const String& str_ref){ if (this == &str_ref){ return (*this); } m_str_size = m_str_size + str_ref.m_str_size; char* str_merge_ptr = new char[m_str_size + 1]; if (str_merge_ptr == NULL){ throw std::length_error("Memory is not enough."); } strcpy(str_merge_ptr, m_str_ptr); strcat(str_merge_ptr, str_ref.m_str_ptr); if (m_str_ptr){ delete[] m_str_ptr; m_str_ptr = NULL; } m_str_ptr = str_merge_ptr; return (*this);}
char operator[] (const unsigned int index) const;
char String::operator[](const unsigned int index) const{ if (index < 0 || index >= m_str_size){ throw std::out_of_range("Array index out of bounds."); } return m_str_ptr[index];}
bool operator== (const String& str_ref) const;
bool String::operator == (const String& str_ref) const{ if (this == &str_ref){ return true; } if (!strcmp(m_str_ptr, str_ref.m_str_ptr)){ return true; } return false;}
unsigned int size() const;
unsigned int String::size() const{ return m_str_size;}
const char* c_str() const;
const char* String::c_str() const{ return m_str_ptr;}
std::ostream& mango::operator<< (std::ostream& output, const String& str_ref)
由于使用了命名空间,友元函数并不属于类成员函数。
虽然在.cpp顶部使用了“using namespace mango;”,但是友元函数就相当于C语言中的全局函数一样,需要直接加上域限制的。
std::ostream& mango::operator<< (std::ostream& output, const String& str_ref){ output << str_ref.m_str_ptr; return output;}
std::istream& mango::operator>> (std::istream& input, String& str_ref)
重载输入流中,为了模拟cin>>(①不限字符串长度输入;以及②空格或者回车结束输入。),在实现过程中,采用了内存缓冲以加快输入过程。
std::istream& mango::operator>> (std::istream& input, String& str_ref){ const int max_buffer_size = 5; char str_buffer[max_buffer_size] = { '\0' }; int index = 0; do{ str_buffer[index++] = input.get(); if (str_buffer[index - 1] == ' ' || str_buffer[index - 1] == '\n'){ str_buffer[index - 1] = '\0'; str_ref += str_buffer; break; } if (index >= max_buffer_size - 1){ str_buffer[index] = '\0'; str_ref += str_buffer; index = 0; } } while (true); return input;}
@qingdujun
2017-7-20 于西安
- 浅谈C++string类
- 浅谈C++ 字符串类 —— string类
- string类和c字符串
- C++—String类
- swift String 字符串浅谈
- 浅谈C++ string类
- 字符串处理总结之一(C#String类)
- 字符串处理总结之一(C#String类)
- 【c++】用string类定义字符串数组
- 字符串处理总结(C#String类)
- 字符串处理总结之一(C#String类)
- 字符串处理总结之一(C#String类)
- 【C++】 用string类定义字符串数组
- C++ C 风格字符串与string类
- 字符串处理总结之一(C#String类)
- 【学习C++】学习C++ -> string类字符串
- 黑马程序员——浅谈java中的String类
- 【c#】—解密类String
- select-option
- Mysql索引介绍及常见索引的区别
- 灵光一闪
- MySql中主键约束和主表是什么?外键约束是什么?主表和从表又是什么?怎么创建?
- MYSQL学习笔记(三)过滤数据
- 浅谈C++ 字符串类 —— string类
- laravel5.4 迁移数据库 出错(一)
- /*Oracle 增加、删除、修改*/
- PCA主成分分析推导
- jQuery瀑布流技术封装并制作插件
- Varnish的基础配置
- MYSQL学习笔记(四)高级数据过滤
- shell 中的for循环while循环和case语句
- ZOJ 3469 Food Delivery [区间DP]