复数类CComplex

编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法);如果没有成员方法,就砸全局作用域找合适的运算符重载函数

++--运算符是单目运算符,在参数列表里放上一个int表示其在数的前面还是后面:operator++()表示前置,operator++(int)表示后置,括号里的int没有任何作用。

复数类的具体实现:

  1. //
  2. // Created by 26685 on 2022-05-16 13:43.
  3. // Description:CComplex.h
  4. //
  5. #ifndef C___CCOMPLEX_H
  6. #define C___CCOMPLEX_H
  7. #include <iostream>
  8. using namespace std;
  9. class CComplex {
  10. friend CComplex operator+(const CComplex &l, const CComplex &r);
  11. friend iostream &operator<<(ostream &os, const CComplex &src);
  12. friend istream & operator>>(istream& is, CComplex &src);
  13. public:
  14. explicit CComplex(int r = 0, int i = 0) : _real(r), _image(i) {}
  15. // CComplex operator+(const CComplex& src) const{
  16. // return CComplex(this->_real+src._real,this->_image+src._image);
  17. // }
  18. void show() {
  19. cout << "real: " << _real << " image: " << _image << endl;
  20. }
  21. CComplex &operator++() {
  22. ++_real;
  23. ++_image;
  24. return *this;
  25. }
  26. CComplex operator++(int) {
  27. return CComplex(_real++, _image++);
  28. }
  29. private:
  30. int _real;
  31. int _image;
  32. };
  33. inline CComplex operator+(const CComplex &l, const CComplex &r) {
  34. return CComplex(l._real + r._real, l._image + r._image);
  35. }
  36. inline iostream &operator<<(ostream &os, const CComplex &src) {//重载输出操作
  37. os << "real: " << src._real << " image: " << src._image << endl;
  38. }
  39. inline istream & operator>>(istream& is,CComplex &src){//重载输入操作
  40. is>>src._real>>src._image;
  41. }
  42. #endif //C___CCOMPLEX_H

主函数:

  1. int main(){
  2. CComplex cp1(10,15);
  3. CComplex cp2(20,30);
  4. CComplex cp3=cp2+cp1;
  5. cp3.show();
  6. CComplex cp4=cp3++;
  7. cp4.show();
  8. cp3.show();
  9. CComplex cp5= ++cp3;
  10. cp5.show();
  11. cp3.show();
  12. cout<<cp4;
  13. CComplex cp6;
  14. cin>>cp6;
  15. cout<<cp6;
  16. return 0;
  17. }

模拟实现string类的代码

  1. //
  2. // Created by 26685 on 2022-05-16 14:30.
  3. // Description:String.h
  4. //
  5. #ifndef C___STRING_H
  6. #define C___STRING_H
  7. #include <iostream>
  8. #include <cstring>
  9. class String {
  10. friend std::ostream &operator<<(std::ostream &os, const String &src);
  11. public:
  12. String(const char *src = nullptr) {
  13. if (src == nullptr) {
  14. _pstr = new char[1];
  15. *_pstr = '\0';
  16. } else {
  17. _pstr = new char[strlen(src) + 1];
  18. strcpy(_pstr, src);
  19. }
  20. }
  21. ~String() {
  22. delete[] _pstr;
  23. _pstr = nullptr;
  24. }
  25. String(const String &src) {
  26. _pstr = new char[strlen(src._pstr) + 1];
  27. strcpy(_pstr, src._pstr);
  28. }
  29. bool operator>(const String &str) const {
  30. return strcmp(_pstr, str._pstr) > 0;
  31. }
  32. bool operator<(const String &str) const {
  33. return strcmp(_pstr, str._pstr) < 0;
  34. }
  35. bool operator==(const String &str) const {
  36. return strcmp(_pstr, str._pstr) == 0;
  37. }
  38. int length() const {
  39. return strlen(_pstr);
  40. }
  41. char &operator[](int index) {
  42. return _pstr[index];
  43. }
  44. char *c_str() const {
  45. return _pstr;
  46. }
  47. private:
  48. char *_pstr;
  49. };
  50. inline std::ostream &operator<<(std::ostream &os, const String &src) {
  51. os << src._pstr;
  52. return os;
  53. }
  54. inline String operator+(const String& l,const String& r){
  55. char* ptmp=new char[strlen(l.c_str())+ strlen(r.c_str())+1];
  56. strcpy(ptmp,l.c_str());
  57. strcat(ptmp,r.c_str());
  58. String temp(ptmp);
  59. delete[] ptmp;
  60. return temp;
  61. }
  62. #endif //C___STRING_H

目前代码中的加法的重载运算效率不高,需要进一步改进。

上面代码的加法重载函数,会生成临时对象,影响性能。

暂时改进为:

  1. inline String operator+(const String &l, const String &r) {
  2. // char *ptmp = new char[strlen(l.c_str()) + strlen(r.c_str()) + 1];
  3. String temp;
  4. temp._pstr=new char[strlen(l.c_str()) + strlen(r.c_str()) + 1];//避免了开辟两次内存空间
  5. strcpy(temp._pstr, l.c_str());
  6. strcat(temp._pstr, r.c_str());
  7. // String temp(ptmp);
  8. // delete[] ptmp;
  9. return temp;
  10. }

String对象的迭代器的实现

迭代器可以透明的访问容器内部元素的值

foreach遍历容器,其底层是用迭代器实现的

迭代器的功能:提供一种统一的方式,透明的遍历容器

在对迭代器加加时,一般用前置的++,因为不会生成新的对象,效率会高一些

  1. /**
  2. * 迭代器的实现, 放在String类中
  3. */
  4. class Iterator{
  5. public:
  6. Iterator(char* p= nullptr):_p(p){}
  7. bool operator!=(const String::Iterator&it){//判断两个迭代器是否相等
  8. return _p!=it._p;
  9. }
  10. void operator++(){
  11. ++ _p;
  12. }
  13. char& operator*(){return *_p;}
  14. private:
  15. char* _p;
  16. };
  17. Iterator begin(){
  18. return {_pstr};
  19. }
  20. Iterator end(){
  21. return {_pstr+length()};
  22. }

实现vector容器中的迭代器

迭代器一般实现成容器的嵌套结构。

在VectorT类中添加以下代码:

  1. class iterator {
  2. public:
  3. iterator(const T *p = nullptr)
  4. : _ptr((int *) p) {}
  5. bool operator!=(const VectorT::iterator &it) {
  6. return _ptr != it._ptr;
  7. }
  8. void operator++() {
  9. ++_ptr;
  10. }
  11. T &operator*() { return *_ptr; }
  12. private:
  13. T *_ptr;
  14. };
  15. iterator begin(){
  16. return {_first};
  17. }
  18. iterator end(){
  19. return {_last};
  20. }

迭代器的失效问题

1、调用erase后,当前位置到末尾元素的迭代器就会失效。

2、调用insert后,当前位置到末尾元素的迭代器就会失效

3、容器扩容后迭代器也会失效

首元素到插入点/删除点的迭代器依然有效

迭代器失效该怎么解决?要对迭代器进行更新操作!

不同容器的迭代器是不能进行比较运算的

vector中迭代器的实现(包含迭代器失效的判断)

  1. //
  2. // Created by 26685 on 2022-05-15 20:33.
  3. // Description:
  4. //
  5. #ifndef C___VECTORT_H
  6. #define C___VECTORT_H
  7. #include "AllocatorT.h"
  8. using namespace std;
  9. /**
  10. * 容器底层内存开辟,内存释放,对象构造和析构都通过allocator实现
  11. * @tparam T
  12. * @tparam Alloc
  13. */
  14. template<typename T, typename Alloc=AllocatorT<T> >
  15. class VectorT {
  16. public:
  17. VectorT(int size = 10) {
  18. // _first=new T[size];
  19. _first = _alloctor.allocate(size);
  20. _last = _first;
  21. _end = _first + size;
  22. }
  23. ~VectorT() {
  24. // delete[] _first;
  25. //使用allocator对vector逐个删除
  26. for (T *p = _first; p != _last; ++p) {
  27. _alloctor.destory(p);
  28. }
  29. _alloctor.deallocate(_first);
  30. _first = _last = _end = nullptr;
  31. }
  32. VectorT(const VectorT<T> &src) {
  33. int size = src._end - src._first;
  34. // _first = new T[size];
  35. _first = _alloctor.allocate(size);
  36. int len = src._last - src._first;
  37. for (int i = 0; i < len; i++) {
  38. // _first[i] = src._first[i];
  39. _alloctor.contruct(_first + 1, src._first[i]);
  40. }
  41. _last = _first + len;
  42. _end = _first + size;
  43. }
  44. VectorT<T> &operator=(const VectorT<T> &src) {
  45. if (src == *this) {
  46. return *this;
  47. }
  48. //delete[] _first;
  49. for (T *p = _first; p != _last; p++) {
  50. _alloctor.destory(p);
  51. }
  52. _alloctor.deallocate(_first);
  53. int size = src._end - src._first;
  54. _first = new T[size];
  55. int len = src._last - src._first;
  56. for (int i = 0; i < len; i++) {
  57. // _first[i] = src._first[i];
  58. _alloctor.contruct(_first + 1, src._first[i]);
  59. }
  60. _last = _first + len;
  61. _end = _first + size;
  62. return *this;
  63. }
  64. T &operator[](int index) {
  65. if (index < 0 || index >= size()) {
  66. throw "OutOfRangeException";
  67. }
  68. return _first[index];
  69. }
  70. void push_back(T val) {
  71. if (full()) {
  72. expend();
  73. }
  74. //*_last++ = val;
  75. _alloctor.construct(_last, val);
  76. _last++;
  77. }
  78. void pop_back() {
  79. if (empty()) { return; }
  80. verify(_last - 1, _last);
  81. --_last;
  82. _alloctor.destory(_last);
  83. }
  84. T back() const {
  85. return *(_last - 1);
  86. }
  87. bool full() const {
  88. return _last == _end;
  89. }
  90. bool empty() const {
  91. return _first == _last;
  92. }
  93. int size() const {
  94. return _last - _first;
  95. }
  96. /**
  97. * 实现迭代器
  98. */
  99. class iterator {
  100. friend void VectorT<T, Alloc>::verify(T *first, T *last);
  101. friend iterator VectorT<T, Alloc>::insert(iterator it,const T& val);
  102. friend iterator VectorT<T, Alloc>::erase(iterator it);
  103. public:
  104. /*iterator(const T *p = nullptr)
  105. : _ptr((int *) p) {}*/
  106. /**
  107. * 根据新的成员变量实现新的构造函数,使其能实现迭代器失效
  108. * @param pvec 容器指针
  109. * @param ptr 位置指针
  110. */
  111. iterator(VectorT<T, Alloc> *pvec = nullptr, T *ptr = nullptr) : _ptr(ptr), _pVec(pvec) {
  112. Iterator_Base *itb = new Iterator_Base(this, _pVec->_head._next);//构造新节点
  113. _pVec->_head._next = itb;//将头结点连接新节点
  114. }//接下来就是在改变数组的过程中使迭代器失效
  115. bool operator!=(const VectorT<T, Alloc>::iterator &it) {
  116. /**
  117. * 判断迭代器是否失效
  118. */
  119. if (_pVec == nullptr || _pVec != it._pVec) {
  120. throw "iterator incompatable!";
  121. }
  122. return _ptr != it._ptr;
  123. }
  124. void operator++() {
  125. if (_pVec == nullptr) {
  126. throw "iterator invalid!";
  127. }
  128. ++_ptr;
  129. }
  130. T &operator*() {
  131. if (_pVec == nullptr) {
  132. throw "iterator invalid!";
  133. }
  134. return *_ptr;
  135. }
  136. private:
  137. T *_ptr;
  138. /**
  139. * 实现迭代器失效,首先要添加一个指向容器的指针
  140. */
  141. VectorT<T, Alloc> *_pVec;
  142. };
  143. /**
  144. * 根据新的成员方法生成相应的begin和end方法
  145. * @return
  146. */
  147. iterator begin() {
  148. return {this, _first};
  149. }
  150. iterator end() {
  151. return {this, _last};
  152. }
  153. /**
  154. * 最后一步:判断迭代器是否失效
  155. * @param first
  156. * @param last
  157. */
  158. void verify(T *first, T *last) {
  159. Iterator_Base *pre = &this->_head;
  160. Iterator_Base *it = this->_head._next;
  161. while (it != nullptr) {
  162. if (it->_cur->_ptr > first && it->_cur->_ptr <= last) {
  163. //迭代器失效,把iterator持有的容器指针置null
  164. it->_cur->_pVec = nullptr;
  165. //删除当前迭代器节点,继续判断后面的迭代器节点是否失效
  166. pre->_next = it->_next;
  167. delete it;
  168. it = pre->_next;
  169. }else{
  170. pre=it;
  171. it=it->_next;
  172. }
  173. }
  174. }
  175. /**
  176. * 插入操作
  177. * @param it 迭代器位置
  178. * @param val 插入的值
  179. * @return 迭代器
  180. */
  181. iterator insert(iterator it,const T& val){
  182. /*
  183. * 不考虑扩容,
  184. * 不考虑指针的合法性
  185. */
  186. verify(it._ptr-1,_last);
  187. T* p=_last;
  188. while(p>it._ptr){
  189. _alloctor.construct(p,*(p-1));
  190. _alloctor.destory(p-1);
  191. p--;
  192. }
  193. _alloctor.construct(p,val);
  194. _last++;
  195. return {this,p};
  196. }
  197. iterator erase(iterator it){
  198. verify(it._ptr-1,_last);
  199. T* p=it._ptr;
  200. while(p<_last-1){//元素向前移
  201. _alloctor.destory(p);
  202. _alloctor.construct(p,*(p+1));
  203. p++;
  204. }
  205. _alloctor.destory(p);
  206. _last--;
  207. return {this,it._ptr-1};
  208. }
  209. private:
  210. T *_first;//表示vector起始位置
  211. T *_last;//表示vector定义元素的末尾
  212. T *_end;//表示vector的末尾
  213. Alloc _alloctor;//负责内存管理
  214. /**
  215. * 在链表的结构中保存每个迭代器
  216. */
  217. struct Iterator_Base {
  218. Iterator_Base(iterator *c = nullptr, VectorT<T, Alloc>::Iterator_Base *n = nullptr) : _cur(c), _next(n) {}
  219. iterator *_cur;
  220. Iterator_Base *_next;
  221. };
  222. /**
  223. * 头结点
  224. */
  225. Iterator_Base _head;
  226. void expend() {//size扩大两倍
  227. int size = _end - _first;
  228. // T *ptmp = new T[size * 2];
  229. T *ptmp = _alloctor.allocate(2 * size);
  230. for (int i = 0; i < size; i++) {
  231. //ptmp[i] = _first[i];
  232. _alloctor.construct(ptmp + i, _first[i]);
  233. }
  234. //delete[] _first;
  235. for (T *p = _first; p != _last; p++) {
  236. _alloctor.destory(p);
  237. }
  238. _alloctor.deallocate(_first);
  239. _first = ptmp;
  240. _last = _first + size;
  241. _end = _first + (2 * size);
  242. }
  243. };
  244. #endif //C___VECTORT_H

深入理解new和delete的原理

1、malloc和new的区别:

  • malloc按字节开辟内存,new开辟内存时需要指定类型,如 new int[10],所以malloc开辟内存返回的都是void*
  • malloc只负责开辟空间,new不仅有malloc的功能,还可以进行数据的初始化
  • malloc开辟内存失败返回nullptr指针,new抛出的是bad_alloc类型的异常

2、free和delete的区别:

  • delete调用析构函数,free是内存释放

检查内存泄漏要重写new和delete

new和delete能混用吗?C++为什么要区分单个元素和数组的内存分配和释放呢?

对于内置类型int等,可以混用。但是对于自定义的类,就不能混用,因为自定义的类类型有析构函数,为了正确的析构函数,在开辟对象数组的时候会在数组前多开辟4个字节,记录对象的个数。

  1. //两个操作符的重载
  2. void* operator new(size_t size){
  3. void* p=malloc(size);
  4. if(p== nullptr){
  5. throw bad_alloc();
  6. }
  7. cout<<"opeartor new addr:"<<p<<endl;
  8. return p;
  9. }
  10. void operator delete (void *ptr) noexcept{
  11. cout<<"opeartor delete addr:"<<ptr<<endl;
  12. free(ptr);
  13. }

new和delete重载实现对象池应用

对象池是在堆上开辟的静态链表

  1. //
  2. // Created by 26685 on 2022-05-17 9:40.
  3. // Description:
  4. //
  5. #ifndef C___QUEUEWITHITEMPOOL_H
  6. #define C___QUEUEWITHITEMPOOL_H
  7. #include <iostream>
  8. using namespace std;
  9. template<typename T>
  10. class Queue {
  11. public:
  12. Queue(){//默认构造
  13. _front=_rear=new QueueItem();
  14. }
  15. ~Queue(){
  16. QueueItem* cur=_front;
  17. while(cur!= nullptr){//遍历链表,依次删除元素
  18. _front=_front->_next;
  19. delete cur;
  20. cur=_front;
  21. }
  22. }
  23. void push(const T& val){
  24. QueueItem* item=new QueueItem(val);
  25. _rear->_next=item;
  26. _rear=item;//尾部元素置为新值,与front区分开
  27. }
  28. void pop(){
  29. if(empty()){
  30. return;
  31. }
  32. QueueItem* first=_front->_next;
  33. _front->_next=first->_next;
  34. if(_front->_next== nullptr){//如果队列只有一个有效节点
  35. _rear=_front;
  36. }
  37. delete first;
  38. }
  39. bool empty() const{
  40. return _front==_rear;
  41. }
  42. T front()const {
  43. return _front->_next->_data;
  44. }
  45. private:
  46. /**
  47. * 实现一个链式的队列,带有头结点
  48. */
  49. struct QueueItem {
  50. QueueItem(T data=T()):_data(data),_next(nullptr){}
  51. //重载new实现对象池
  52. void* operator new (size_t size){
  53. if(_itemPool== nullptr){//如果未开辟空间;如果当前内存池使用完,最后一个元素指向的也是nullptr,会分配新的内存池
  54. _itemPool=(QueueItem*)new char[POOL_ITEM_SIZE*sizeof(QueueItem)];//开辟对象池
  55. //我们用char,按字节开辟,因为如果用new QueueItem,
  56. //就又会调用到当前这个方法了,
  57. //我们现在就是在给QueueItem自定义new运算符重载
  58. QueueItem* p=_itemPool;
  59. for(;p<_itemPool+POOL_ITEM_SIZE-1;++p){
  60. p->_next=p+1;//初始化连续链表
  61. }
  62. p->_next= nullptr;
  63. }
  64. //新建queueItem的时候会使用对象池中未使用的节点,然后指向下一个未使用的节点
  65. QueueItem* p=_itemPool;
  66. _itemPool=_itemPool->_next;
  67. return p;
  68. }
  69. void operator delete (void* ptr){
  70. QueueItem* p=(QueueItem*)ptr;
  71. p->_next=_itemPool;
  72. _itemPool=p;
  73. }
  74. T _data;
  75. QueueItem *_next;
  76. static const int POOL_ITEM_SIZE=100000;
  77. static QueueItem *_itemPool;
  78. };
  79. QueueItem* _front;//指向头结点
  80. QueueItem* _rear;//指向队尾,
  81. };
  82. template<typename T>
  83. typename Queue<T>::QueueItem* Queue<T>::QueueItem::_itemPool= nullptr;
  84. #endif //C___QUEUEWITHITEMPOOL_H

五、C++运算符重载,使面向对象编程更方便的更多相关文章

  1. 四、C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 .NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就 ...

  2. 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...

  3. 转:[你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单

    本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单  async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...

  4. [你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单

    本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单  async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...

  5. C++(三十五) — 运算符重载

    运算符重载的实质:函数重载.除了增加一个关键字 operator 外,与函数重载没有区别,都是通过该类的某个对象来访问重载运算符. (1)重载运算符时,运算符运算顺序和优先级不变,操作数个数不变: ( ...

  6. Python之路【第五篇续】:面向对象编程二

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABgQAAALaCAIAAABxja8cAAAgAElEQVR4nOzd6X9Tdd74/+uv+f5uzF

  7. C# 5.0 新特性——Async和Await使异步编程更简单

    http://www.cnblogs.com/zhili/archive/2013/05/15/csharp5asyncandawait.html http://blog.zhaojie.me/201 ...

  8. C++ 关于运算符重载

    转载来源:http://c.biancheng.net/cpp/biancheng/view/216.html 重载运算符的函数一般格式如下:    函数类型 operator 运算符名称 (形参表列 ...

  9. C++ 运算符重载三(链式编程)

    //运算符重载之链式编程 #include<iostream> using namespace std; //对于友元函数重载运算符只适用于左操作数是系统变量的场景 //因为成员无法在系统 ...

随机推荐

  1. 路径规划—BUG算法

  2. Spring Security OAuth 笔记

    1  单点登录 关于单点登录的原理,我觉得下面这位老哥讲的比较清楚,有兴趣可以看一下,下面我把其中的重点在此做个笔记总结 https://juejin.cn/post/6844904079274197 ...

  3. 【转载】10个Web3D可视化精彩案例

    1.化学元素周期表 六种排列方式,炫酷动画效果,TWaver 3D轻松实现. 演示地址:http://demo.servasoft.com/che... 2.DNA螺旋图 DNA3D模型,包含几千个球 ...

  4. javaweb图书管理系统之账号密码验证登录

    验证账号与密码是否正确功能 一.注册功能 首先,在验证账号与密码是否正确的前提下的,需要先注册一个账号,如果没有账号,就会进不去,也无法验证. 其实,注册功能就是一个添加的功能,仿照我的第一篇文章,往 ...

  5. mysql在cmd中查询到的汉字乱码问题解决 方法一

    只要执行如上两个 set character_set_connection = gbk; set character_set_results= gbk; 将编码格式转换成gbk即可

  6. jdbc连接MySQL数据库+简单实例(普通JDBC方法实现和连接池方式实现)

    jdbc连接数据库 总结内容 1. 基本概念 jdbc的概念 2. 数据库连接 数据库的连接 DAO层思想 重构设计 3. 事务 概念 事务的ACID属性 事务的操作 4. 连接池 为什么要使用连接池 ...

  7. C++---变量、数据类型和运算符

    内存 计算机使用内存来记忆或存储计算时所使用的的数据. 计算机执行程序时, 组成程序的指令和程序所操作的数据都必须存放在某个地方, 而这个地方就是计算机的内存, 也称为主存, 或随机访问存储器(RAM ...

  8. Go语言 映射(map)

    Go语言  映射(map) 1. 什么是 map2. 创建 map3. 访问 map4. nil map和空map5. map中元素的返回值6. len()和delete()7. 测试map中元素是否 ...

  9. linux中查看端口号使用情况

    百度一圈,以下是整理来的操作命令. 1.netstat -anp |grep (端口号) 这个方法可以直观看到对应端口号是否被使用. 2.netstat -nultp 这个方法可以看到该机上所有以用的 ...

  10. SSM实现个人博客-day04

    项目源码免费下载:SSM实现个人博客 有问题询问vx:kht808 3.项目搭建(SSM整合) (1)创建maven工程,导入相应的依赖 <properties> <project. ...