当前位置:   article > 正文

浅谈C++ 字符串类 —— string类_c++ string的转移

c++ string的转移

摘要: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中实现。


摘选了一些具有代表性的几个函数,使用C语言函数对其进行了实现。这几个函数,几乎都牵涉到内存的管理,如果不太清楚的,请移步至本文开头处的文章。另外,在函数实现过程中,引入了C++异常捕获技术,阅读《浅谈C++ 标准库中的异常 —— stdexcept类》一文,可以了解更多这方面的知识。

以下为String类构造情况,

  1. #pragma once
  2. #include <iostream>
  3. namespace mango
  4. {
  5. class String
  6. {
  7. public:
  8. String();
  9. String(const char* str_ptr);
  10. String(const String& str_ref);
  11. ~String();
  12. String& operator= (const String& str_ref);
  13. String operator+ (const String& str_ref) const;
  14. String& operator+= (const String& str_ref);
  15. char operator[] (const unsigned int index) const;
  16. bool operator== (const String& str_ref) const;
  17. unsigned int size() const;
  18. const char* c_str() const;
  19. protected:
  20. friend std::ostream& operator<< (std::ostream& output, const String& str_ref);
  21. friend std::istream& operator>> (std::istream& input, String& str_ref);
  22. private:
  23. char* m_str_ptr;
  24. unsigned int m_str_size;
  25. };
  26. }

String();

构造函数中,为了对内存进行统一释放,全部采用new char[]方式。这样,释放时就统一为delete[]。

  1. String::String()
  2. {
  3. m_str_size = 0;
  4. m_str_ptr = new char[1];
  5. if (m_str_ptr == NULL){
  6. throw std::length_error("Memory is not enough.");
  7. }
  8. *m_str_ptr = '\0';
  9. }

String(const char* str_ptr);

  1. String::String(const char* str_ptr)
  2. {
  3. m_str_size = strlen(str_ptr);
  4. m_str_ptr = new char[m_str_size + 1];
  5. if (m_str_ptr == NULL){
  6. throw std::length_error("Memory is not enough.");
  7. }
  8. strcpy(m_str_ptr, str_ptr);
  9. }

String(const String& str_ref);

这里注意,拷贝构造函数的参数必须为引用类型,否则将会陷入无休止的递归中。当前,如果不为&,编译器将会直接报错。

  1. String::String(const String& str_ref)
  2. {
  3. m_str_size = str_ref.m_str_size;
  4. m_str_ptr = new char[m_str_size + 1];
  5. if (m_str_ptr == NULL){
  6. throw std::length_error("Memory is not enough.");
  7. }
  8. strcpy(m_str_ptr, str_ref.m_str_ptr);
  9. }

~String();

由于构造函数、拷贝构造函数已经统一了内存开辟方式。这里释放内存时,也全部使用delete[]。

  1. String::~String()
  2. {
  3. if (m_str_ptr){
  4. delete[] m_str_ptr;
  5. m_str_ptr = NULL;
  6. }
  7. }

String& operator= (const String& str_ref);

为了进一步提高赋值效率,首先对自身赋值进行检查。另外一点需要特别注意的是,对this类中的m_str_ptr指针进行重定向时,一定要释放掉旧内存。否则必然内存泄漏。

  1. String& String::operator = (const String& str_ref)
  2. {
  3. if (this == &str_ref){
  4. return (*this);
  5. }
  6. if (m_str_ptr){
  7. delete[] m_str_ptr;
  8. m_str_ptr = NULL;
  9. }
  10. m_str_size = str_ref.m_str_size;
  11. m_str_ptr = new char[m_str_size + 1];
  12. if (m_str_ptr == NULL){
  13. throw std::length_error("Memory is not enough.");
  14. }
  15. strcpy(m_str_ptr, str_ref.m_str_ptr);
  16. return (*this);
  17. }

String operator+ (const String& str_ref) const;

运算符+重载过程中,返回值为对象(非引用),这时候将调用拷贝构造函数。由于在拷贝构造函数中已经实现了深拷贝(对成员函数所指向的内存一并拷贝)技术,这种返回不会造成内存泄漏亦或是内存丢失的现象。

  1. String String::operator + (const String& str_ref) const
  2. {
  3. if (str_ref.m_str_ptr == '\0'){
  4. return (*this);
  5. }
  6. else if (m_str_ptr == '\0'){
  7. return str_ref;
  8. }
  9. String str_merge;
  10. if (str_merge.m_str_ptr){
  11. delete[] str_merge.m_str_ptr;
  12. str_merge.m_str_ptr = NULL;
  13. }
  14. str_merge.m_str_size = m_str_size + str_ref.m_str_size;
  15. str_merge.m_str_ptr = new char[str_merge.m_str_size + 1];
  16. if (str_merge.m_str_ptr == NULL){
  17. throw std::length_error("Memory is not enough.");
  18. }
  19. strcpy(str_merge.m_str_ptr, m_str_ptr);
  20. strcat(str_merge.m_str_ptr, str_ref.m_str_ptr);
  21. return str_merge;
  22. }

String& operator+= (const String& str_ref);

该过程中,需要注意的是“+=操作”:①将覆盖原来的this类空间;或者是②this类指针将指向一块新空间。不管如何,这时候一定要释放掉原来的旧空间。

  1. String& String::operator += (const String& str_ref)
  2. {
  3. if (this == &str_ref){
  4. return (*this);
  5. }
  6. m_str_size = m_str_size + str_ref.m_str_size;
  7. char* str_merge_ptr = new char[m_str_size + 1];
  8. if (str_merge_ptr == NULL){
  9. throw std::length_error("Memory is not enough.");
  10. }
  11. strcpy(str_merge_ptr, m_str_ptr);
  12. strcat(str_merge_ptr, str_ref.m_str_ptr);
  13. if (m_str_ptr){
  14. delete[] m_str_ptr;
  15. m_str_ptr = NULL;
  16. }
  17. m_str_ptr = str_merge_ptr;
  18. return (*this);
  19. }

char operator[] (const unsigned int index) const;

  1. char String::operator[](const unsigned int index) const
  2. {
  3. if (index < 0 || index >= m_str_size){
  4. throw std::out_of_range("Array index out of bounds.");
  5. }
  6. return m_str_ptr[index];
  7. }

bool operator== (const String& str_ref) const;

  1. bool String::operator == (const String& str_ref) const
  2. {
  3. if (this == &str_ref){
  4. return true;
  5. }
  6. if (!strcmp(m_str_ptr, str_ref.m_str_ptr)){
  7. return true;
  8. }
  9. return false;
  10. }

unsigned int size() const;

  1. unsigned int String::size() const
  2. {
  3. return m_str_size;
  4. }

const char* c_str() const;

  1. const char* String::c_str() const
  2. {
  3. return m_str_ptr;
  4. }

std::ostream& mango::operator<< (std::ostream& output, const String& str_ref)

由于使用了命名空间,友元函数并不属于类成员函数。

虽然在.cpp顶部使用了“using namespace mango;”,但是友元函数就相当于C语言中的全局函数一样,需要直接加上域限制的。

  1. std::ostream& mango::operator<< (std::ostream& output, const String& str_ref)
  2. {
  3. output << str_ref.m_str_ptr;
  4. return output;
  5. }

std::istream& mango::operator>> (std::istream& input, String& str_ref)

重载输入流中,为了模拟cin>>(①不限字符串长度输入;以及②空格或者回车结束输入。),在实现过程中,采用了内存缓冲以加快输入过程。

  1. std::istream& mango::operator>> (std::istream& input, String& str_ref)
  2. {
  3. const int max_buffer_size = 5;
  4. char str_buffer[max_buffer_size] = { '\0' };
  5. int index = 0;
  6. do{
  7. str_buffer[index++] = input.get();
  8. if (str_buffer[index - 1] == ' ' || str_buffer[index - 1] == '\n'){
  9. str_buffer[index - 1] = '\0';
  10. str_ref += str_buffer;
  11. break;
  12. }
  13. if (index >= max_buffer_size - 1){
  14. str_buffer[index] = '\0';
  15. str_ref += str_buffer;
  16. index = 0;
  17. }
  18. } while (true);
  19. return input;
  20. }



@qingdujun  

2017-7-20 于西安

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/501757
推荐阅读
相关标签
  

闽ICP备14008679号