复数类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. SQL数据库之“TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2)”

    一.介绍 样本:TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2) 解析:TIMESTAMPDIFF(格式,开始时间,结束时间) 二.参数解析 格式: ...

  2. InfoQ Trends Report

    InfoQ Trends Report InfoQ Trends Report Culture & Methods Trends Report - March 2021 DevOps and ...

  3. 设计模式之:抽象工厂模式AbstractFactoryPattern的实现

    相比于工厂模式,抽象工厂模式的每个工厂可以创建产品系列,而不是一个产品: 抽象工厂用到的技术:接口.多态.配置文件.反射: 抽象工厂模式的设计原则: 实现客户端创建产品和使用产品的分离,客户端无须了解 ...

  4. spark-shell报错java.lang.IllegalArgumentException: java.net.UnknownHostException: namenode

    在使用spark on yarn启动spark-shell时,发现报错: 是说找不到主机名为namenode的主机,那么应该是配置文件出错了. 经过检查,发现是spark-defaults.conf文 ...

  5. java中hashCode和equals什么关系,hashCode到底怎么用的

    Object类的hashCode的用法:(新手一定要忽略本节,否则会很惨) 马 克-to-win:hashCode方法主要是Sun编写的一些数据结构比如Hashtable的hash算法中用到.因为ha ...

  6. Blazor组件自做三 : 使用JS隔离封装ZXing扫码

    Blazor组件自做三 : 使用JS隔离封装ZXing扫码 本文基础步骤参考前两篇文章 Blazor组件自做一 : 使用JS隔离封装viewerjs库 Blazor组件自做二 : 使用JS隔离制作手写 ...

  7. QT 如何让release生成的EXE可在他人电脑上运行(尝试了全网的方法后,这应该能帮你解决)

    这两天,迅速上手了QT并用其基于C++编写了一个含UI的小程序 跳过中间的开发阶段 当我兴致满满的要将程序打包时,却是费尽周折,搜尽全网的关于QT的打包教程,最后都不顶用. 后面自己和队友的共同发现, ...

  8. Water 2.5.8 发布,一站式服务治理平台

    Water(水孕育万物...) Water 为项目开发.服务治理,提供一站式解决方案(可以理解为微服务架构支持套件).基于 Solon 框架开发,并支持完整的 Solon Cloud 规范:已在生产环 ...

  9. c++对于c的拓展_引用的本质是指针常量

    本质:c++底层实现的指针常量(Type & ref =val; // Type *const ref =&val)

  10. Mybatis-typeAliases的作用

    其他具体代码请访问->mybatis的快速入门 1.在mybatis核心配置文件SqlMapperConfig.xml中配置别名 <!-- 定义别名--> <typeAlias ...