当前位置:   article > 正文

C++知识点42——下标运算符[]的重载及string类的实现_c++ 42点

c++ 42点

一、下标运算符的重载

1.概念

如果一个类表示容器,那么要重载下标运算符[],下标运算符必须是成员函数。下表访问运算符通常要有一个const版本和一个非const版本。如果不定义const版本,那么const对象将无法调用下表运算符。非const版本返回对应位置的引用,const版本返回常量对象的引用

 

2.示例

通过实现一个string类来熟悉下标运算符的重载

string类的整体结构如下

  1. #include <utility>
  2. #include <iostream>
  3. #include <cstring>
  4. using namespace std;
  5. class mystring
  6. {
  7. friend istream &operator>>(istream &is, mystring &data);
  8. friend ostream &operator<<(ostream &os, const mystring &data);
  9. public:
  10. mystring();
  11. mystring(const char *str);
  12. mystring(const mystring &rval);
  13. mystring &operator=(const mystring &rval);
  14. char &operator[](size_t n);
  15. const char &operator[](size_t n) const;
  16. size_t size() const;
  17. const char *c_str() const;
  18. void swap(mystring &t);
  19. ~mystring();
  20. private:
  21. char *data_;
  22. };
  23. istream &operator>>(istream &is, mystring &data);
  24. ostream &operator<<(ostream &os, const mystring &data);

具体实现如下和测试

  1. #define _GLIBCXX_DEBUG
  2. #include "mystring.h"
  3. mystring::mystring():
  4. data_(new char[1])
  5. {
  6. cout<<"mystring()"<<endl;
  7. data_[0]='\0';
  8. }
  9. mystring::mystring(const char *str):
  10. data_(new char[strlen(str)+1])
  11. {
  12. cout<<"mystring(const char *str)"<<endl;
  13. strcpy(data_, str);
  14. }
  15. mystring::mystring(const mystring &rval):
  16. data_(new char[rval.size()+1])
  17. {
  18. cout<<"mystring(const mystring &t)"<<endl;
  19. strcpy(data_, rval.c_str());
  20. }
  21. mystring & mystring::operator=(const mystring &rval)
  22. {
  23. cout<<"operator=(const mystring &rval)"<<endl;
  24. mystring t(rval);
  25. this->swap(t);
  26. return *this;
  27. }
  28. char & mystring::operator[](size_t n)
  29. {
  30. cout<<"operator[](size_t n)"<<endl;
  31. return data_[n];
  32. }
  33. const char & mystring::operator[](size_t n) const
  34. {
  35. cout<<"operator[](size_t n) const"<<endl;
  36. return data_[n];
  37. }
  38. size_t mystring::size() const
  39. {
  40. cout<<__func__<<endl;
  41. return strlen(data_);
  42. }
  43. const char *mystring::c_str() const
  44. {
  45. cout<<__func__<<endl;
  46. return data_;
  47. }
  48. void mystring::swap(mystring &t)
  49. {
  50. cout<<__func__<<endl;
  51. std::swap(this->data_, t.data_);
  52. }
  53. mystring::~mystring()
  54. {
  55. cout<<"~mystring()"<<endl;
  56. delete data_[];
  57. }
  58. istream &operator>>(istream &is, mystring &data)
  59. {
  60. cout<<__func__<<endl;
  61. is>>data.data_;
  62. return is;
  63. }
  64. ostream &operator<<(ostream &os, const mystring &data)
  65. {
  66. cout<<__func__<<endl;
  67. os<<data.data_;
  68. return os;
  69. }
  70. int main(int argc, char const *argv[])
  71. {
  72. mystring t1("1234"), t2("5678");
  73. mystring t3=t1;
  74. cout<<t2<<t3<<endl;
  75. cout<<t2.size()<<endl;
  76. t1.swap(t2);
  77. cout<<t1<<t2<<endl;
  78. mystring t;
  79. t=t2;
  80. cout<<t<<endl;
  81. const mystring t4=t;
  82. cout<<t[2]<<t4[1]<<endl;
  83. return 0;
  84. }

上述代码中,mystring类实现了两个版本的operator[]

  1. char & mystring::operator[](size_t n)
  2. {
  3. cout<<"operator[](size_t n)"<<endl;
  4. return data_[n];
  5. }
  6. const char & mystring::operator[](size_t n) const
  7. {
  8. cout<<"operator[](size_t n) const"<<endl;
  9. return data_[n];
  10. }

都返回的是对应位置的元素的引用。表面看起来,这两个函数的函数名相同,形参列表也相同,好像是违背了函数重载的规则,但是实际上这两个函数的形参列表是不同的

因为operator[]是成员函数,,形参列表中都隐式的存在一个指向创建对象的this指针,非const版本的operator[]的this的类型是mystring *const this,所以,非常量的mystring对象可以调用非常量版本的operator[]

而const版本的operator[]的this的类型是const mystring *const this,所以不仅得永远指向创建对象,而且还要求指向的创建对象不能更改,所以只有const mystring的对象可以调用const版本的operator[]

因为二者的this形参类型不同,所以可以重载成功

关于const函数,见博客https://blog.csdn.net/Master_Cui/article/details/106885048

 

此外,mystring关于operator=的实现使用了swap,而不是将delete原来的对象,然后再重新创建对象

  1. void mystring::swap(mystring &t)
  2. {
  3. cout<<__func__<<endl;
  4. std::swap(this->data_, t.data_);
  5. }
  6. mystring & mystring::operator=(const mystring &rval)
  7. {
  8. cout<<"operator=(const mystring &rval)"<<endl;
  9. mystring t(rval);
  10. this->swap(t);
  11. return *this;
  12. }

类不用自定义swap成员,但是如果一个类含有动态分配内存的操作,那么swap可能就是一个优化的手段。上述代码通过自定义swap,只是改变了两个指针的指向对象,而指向的对象并不需要重新分配内存

减少了动态分配内存和销毁指向对象的次数

 

参考

https://github.com/chenshuo/recipes/blob/master/string/StringTrivial.h

《C++ Primer》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

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

闽ICP备14008679号