C++对象运算符重载,容器迭代器
2022/2/2 20:13:05
本文主要是介绍C++对象运算符重载,容器迭代器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
+,++, += 、其他的运算符重载函数差不多,以及输出out,输入 in
#include <iostream> using namespace std; /* C++ 运算符重载:使对象的运算表现得和编译器内置类型一样 template<typename T> T sum(T a, T b) { return a+ b; //a.+(b) } 1.编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法);如果没有成员方法 就在全局作用域找合适的运算符重载函数,如果没有就会报全局没有合适的方法 */ class CComplex { public: CComplex(int r = 0, int i = 0) :mreal(r),mimage(i) {} CComplex operator+(const CComplex &src) { // CComplex temp; // temp.mimage = this->mimage + src.mimage; // temp.mreal = this->mreal + src.mreal; // return temp; return CComplex(this->mreal + src.mreal,this->mimage + src.mimage); } void show() const { cout << "real:" << mreal<<", image:" << mimage << endl; } CComplex operator++(int) //后置对象是先赋值然后再给自身的对象加 { // CComplex comp = *this; // mreal += 1; // mimage += 1; // return comp; return CComplex(mreal ++, mimage ++); } CComplex& operator++() //前置++ { mreal += 1; mimage += 1; return *this; } void operator+=(const CComplex &src) { mreal += src.mreal; mimage += src.mimage; } private: int mreal; int mimage; friend CComplex operator+(const CComplex &lhs, const CComplex &rhs); friend ostream& operator<<(ostream &cout , const CComplex & src); friend istream& operator>>(istream& in, CComplex &src); }; CComplex operator+(const CComplex &lhs, const CComplex &rhs) { //不能直接访问私有变量,但是可以把这个重载函数设置为友元函数 return CComplex(lhs.mreal+rhs.mreal, lhs.mimage + rhs.mimage); } // ostream& operator<<(ostream &out , const CComplex & src) { out << "real:" << src.mreal<<", image:" << src.mimage << endl; return out; } istream& operator>>(istream& in, CComplex &src) { in >> src.mreal >> src.mimage ; return in; } int main() { CComplex comp1(10,10); CComplex comp2(20,20); //comp1.operator+(comp2) 加法运算符重载函数 CComplex comp3 = comp1 + comp2; comp3.show(); //首先知道的是comp1 是一个对象,然后20强转为complex接受的一个参数,CComplex(20) //调用的加法运算重载函数,是可以的 CComplex comp4 = comp1 + 20; //首先是知道的30是个一个整数,并不会转成complex对象,然后调用comp的重载函数,所以是错误的 // CComplex comp5 = 30 + comp2; //定义一个全局作用域的重载函数就可以 CComplex comp6 = 30 + comp2; // ++ -- 是做单目运算 operator++() operator++(int) 无参数是前++ //CComplex operator++(int) comp6 = comp1 ++; comp1.show(); // real:11, image:11 comp6.show(); //real:10, image:10 //以上后置++ ,也就是先赋值之后再给自己加1 //CComplex operator++() comp6 = ++comp1; comp1.show();//real:12, image:12 comp6.show();//real:12, image:12 //前置++ ,自身先++,然后再赋值 // void comp1.operator+=(comp2) comp1 += comp2; comp1.show(); //把打印对象的输出写成和内置的打印一样 //cout ::operator<<(cout, comp1); //ostream& operator<<(ostream &out, comp2) cout << comp1 ; cin >> comp3; cout << comp3; return 0; }
模拟实现C++ string类代码,加法运算符重载的效率不好。
#include <iostream> #include <cstring> using namespace std; class String { public: String(const char*p = nullptr) { if(p != nullptr) { _pstr = new char[strlen(p) + 1]; strcpy(_pstr,p); }else { _pstr = new char[1]; *_pstr = '\0'; } } ~String() { delete []_pstr; _pstr = nullptr; } String(const String &str) { _pstr = new char[strlen(str._pstr) + 1]; int len = strlen(str._pstr) + 1; strcpy(_pstr,str._pstr); } String& operator= (const String &str) { if(this == &str) return *this; delete []_pstr; _pstr = new char[strlen(str._pstr) + 1]; strcpy(_pstr, str._pstr); return *this; } bool operator>(const String &str) const { return strcmp(_pstr,str._pstr) > 0; } bool operator<(const String &str) const { return strcmp(_pstr,str._pstr) < 0; } bool operator==(const String &str) const { return strcmp(_pstr,str._pstr) == 0; } int length()const { return strlen(_pstr); } char& operator[](int index) { return _pstr[index]; } const char& operator[](int index) const { return _pstr[index]; } const char* c_str() const { return _pstr; } private: char *_pstr; friend String operator+(const String &lhs, const String &rhs); friend ostream& operator<<(ostream &out, const String &str); friend istream& operator>>(istream &in , String &str); }; String operator+(const String &lhs, const String &rhs){ char *ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1]; strcpy(ptmp, lhs._pstr); strcat(ptmp,rhs._pstr); String tmp(ptmp); delete []ptmp; return tmp; } ostream& operator<<(ostream &out, const String &str) { out << str._pstr; return out; } istream& operator>>(istream &in , String &str) { in >> str._pstr; return in; } int main() { String str1; String str2 = "aaa"; String str3 = "bbb"; String str4 = str2 + str3; String str5 = str2 + "ccc"; String str6 = "ddd" + str2; cout << "str6:" << str6 << endl; if (str5 > str6) cout << str5 << ">" << str6 << endl; else cout << str5 << "<" << str6 << endl; int len = str6.length(); for(int i = 0; i < len; i++) { cout << str6[i] << " "; } cout << endl; return 0; }
vector容器迭代器iterator的实现
迭代器的方法有:构造函数,!=, * , 以及前置加加
#include <iostream> #include <cstring> using namespace std; /**/ //空间适配器 template<typename T> class Allocator { public: T* allocate(size_t size) // 只负责开辟内存 { return (T*)malloc(sizeof(T) * size); } void deallocate(void *p) // 只负责内存释放 { free(p); } //在指定开辟好的内存上进行构造,用定位new void construct(T *p, const T &val) { new (p) T(val); } void destroy(T *p) //负责对象析构 { p->~T(); //调用对象的析构函数 ~T()代表了T类型的析构函数 } }; template<typename T, typename Alloc = Allocator<T> > // Alloc 默认使用Allocator class vector { public: //构造 //空间配置器可以让用户传进来,模板上边就不用 Alloc 那个参数名了 // vector(int size = 10,const &alloc = Allocator<T>()):_allocator(alloc) vector(int size = 10) { //需要把内存开辟和对象构造分开处理 // _first = new T[size]; _first = _allocator.allocate(size); _last = _first; _end = _first + size; } //析构 ~vector() { //析构容器有效地元素,然后再释放_first指针指向的堆内存 // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); _first = _last = _end = nullptr; } //拷贝构造 vector(const vector<T> &rhs) { int size = rhs._end - rhs._first; // _first = new T[size]; _first = _allocator.allocate(size); int len = rhs._last - rhs._first; for(int i = 0; i < len; ++i) { // _first[i] = rhs._first[i]; _allocator.construct(_first+i,rhs._first[i]); } _last = _first + len; _end = _first + size; } //赋值重载 vector<T>& operator=(const vector<T> &rhs) { if(this == &rhs) return *this; // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); int size = rhs._end - rhs._first; int len = rhs._last - rhs._first; // _first = new T[size]; _first = _allocator.allocate(size); for(int i = 0; i < len; ++i) { // _first[i] = rhs._first[i]; _allocator.construct(_first+i,rhs._first[i]); } _last = _first + len; _end = _first + size; return *this; } //向容器末尾添加元素 void push_back(const T &val) { if(full()) expand(); // *_last++ = val; //_last 指针指向的内存构造一个值为val的对象 _allocator.construct(_last,val); _last ++; } //从容器末尾删除元素 void pop_back() { if(empty()) return; --_last; //不仅仅要把_last指针--,还需要析构删除的元素 _allocator.destroy(_last); } //返回容器末尾的值 T back() const { return *(_last - 1); } //检查容器是否满了 bool full() const {return _last == _end;} //检查容器是否为空 bool empty() const { return _last == _first;} //返回容器的个数 int size() const {return _last - _first;} //中括号运算符重载 T& operator[](int index) { if (index < 0 || index >= size()) throw "OutOfRangException"; return _first[index]; } //#1 迭代器一般实现成容器的嵌套类型 class iterator { public: iterator(T *ptr = nullptr) :_ptr(ptr) {} bool operator!= (const iterator &it) const { return _ptr!= it._ptr; } //后置++会产生临时量 void operator++() { _ptr++; } T& operator*() // int data = *it; *it =20 { return *_ptr; } const T& operator*()const { return *_ptr; } private: T *_ptr; }; //需要给容器提供begin和end方法 iterator begin() { return iterator(_first); } iterator end() { return iterator(_last); } private: T *_first; // 指向数组起始的位置 T *_last ; // 指向数组中有效元素的后继位置 T *_end ; // 指向数组空间的后继位置 Alloc _allocator; //定义容器的空间配置器对象 void expand() { int size = _end - _first; // T *temp = new T[size * 2]; T *temp = _allocator.allocate(2*size); for (int i = 0; i < size; i++) { // temp[i] = _first[i]; _allocator.construct(temp+i,_first[i]); } // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); _first = temp; _last = _first + size; _end = _first + size * 2; } }; class Test { public: Test(){ cout << "Test()" << endl; } Test(const Test &src) { cout << "Test(const Test &src)" << endl; } ~Test() { cout << "~Test()" << endl; } }; int main() { vector<int> vec; for(int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } int size = vec.size(); for(int i = 0; i < size; ++i) { cout << vec[i] << " "; } cout << endl; vector<int>::iterator it = vec.begin(); for(;it != vec.end(); ++it) { cout << *it << " "; } for(int val : vec) //其底层原理,就是通过容器的迭代器来实现容器遍历的 { cout << val << endl; } cout << endl; return 0; }
容器的迭代器失效问题
1.迭代器为什么会失效?
a:当容器调用erase方法后,当前位置到容器末尾的元素所有的迭代器全部都失效了
b:当容器调用insert方法后,当前位置到容器末尾的元素所有的迭代器全部都失效了
首元素-----> 插入点、删除点 ----->末尾元素
c.insert来说,如果引起容器内存扩容,原来容器的所有的迭代器就全部失效了
d.不同容器的迭代器是不能进行比较运算的
2.迭代器失效了,解决的方法: 对插入删除点的迭代器进行更新操作
问题1:删除元素
vector<int> vec; for(int i = 0; i < 20; ++i) { vec.push_back(rand() % 10 + 1); } //把vec容器中的所有的偶数全部删除 auto it = vec.begin(); for(; it != vec.end(); ++it) { if(*it % 2 == 0) { //第一次调用erase以后,迭代器就失效了 vec.erase(it); } }解决方式:
auto it = vec.begin(); while( it != vec.end()) { if(*it % 2 == 0) { //返回新的迭代器,但是后边的元素会往前挪,所以,不能再加加操作了 it = vec.erase(it); }else { ++it; } }问题2:某个位置插入元素
//给容器中的所有的偶数前面添加一个小于偶数值1 的数字 auto it = vec.begin(); for(; it != vec.end(); ++it) { if(*it % 2 == 0) { //这里的迭代器在第一次insert之后,iterator就失效了 vec.insert(it, *it -1); } }解决方式:
//给容器中的所有的偶数前面添加一个小于偶数值1 的数字 auto it = vec.begin(); for(; it != vec.end(); ++it) { if(*it % 2 == 0) { //更新迭代器,记住:插入元素,当前元素的位置以及后边的元素都往后边移, //如果只是++一次的迭代器又会访问到当前元素的,所以需要两次++ it = vec.insert(it, *it -1); ++it; } }模仿vector迭代器失效的源码代码:
#include <iostream> #include <cstring> using namespace std; /**/ //空间适配器 template<typename T> class Allocator { public: T* allocate(size_t size) // 只负责开辟内存 { return (T*)malloc(sizeof(T) * size); } void deallocate(void *p) // 只负责内存释放 { free(p); } //在指定开辟好的内存上进行构造,用定位new void construct(T *p, const T &val) { new (p) T(val); } void destroy(T *p) //负责对象析构 { p->~T(); //调用对象的析构函数 ~T()代表了T类型的析构函数 } }; template<typename T, typename Alloc = Allocator<T> > // Alloc 默认使用Allocator class vector { public: //构造 //空间配置器可以让用户传进来,模板上边就不用 Alloc 那个参数名了 // vector(int size = 10,const &alloc = Allocator<T>()):_allocator(alloc) vector(int size = 10) { //需要把内存开辟和对象构造分开处理 // _first = new T[size]; _first = _allocator.allocate(size); _last = _first; _end = _first + size; } //析构 ~vector() { //析构容器有效地元素,然后再释放_first指针指向的堆内存 // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); _first = _last = _end = nullptr; } //拷贝构造 vector(const vector<T> &rhs) { int size = rhs._end - rhs._first; // _first = new T[size]; _first = _allocator.allocate(size); int len = rhs._last - rhs._first; for(int i = 0; i < len; ++i) { // _first[i] = rhs._first[i]; _allocator.construct(_first+i,rhs._first[i]); } _last = _first + len; _end = _first + size; } //赋值重载 vector<T>& operator=(const vector<T> &rhs) { if(this == &rhs) return *this; // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); int size = rhs._end - rhs._first; int len = rhs._last - rhs._first; // _first = new T[size]; _first = _allocator.allocate(size); for(int i = 0; i < len; ++i) { // _first[i] = rhs._first[i]; _allocator.construct(_first+i,rhs._first[i]); } _last = _first + len; _end = _first + size; return *this; } //向容器末尾添加元素 void push_back(const T &val) { if(full()) expand(); // *_last++ = val; //_last 指针指向的内存构造一个值为val的对象 _allocator.construct(_last,val); _last ++; } //从容器末尾删除元素 void pop_back() { if(empty()) return; //erase(it); verfy(it._ptr, _last); verify(_last - 1, _last); --_last; //不仅仅要把_last指针--,还需要析构删除的元素 _allocator.destroy(_last); } //返回容器末尾的值 T back() const { return *(_last - 1); } //检查容器是否满了 bool full() const {return _last == _end;} //检查容器是否为空 bool empty() const { return _last == _first;} //返回容器的个数 int size() const {return _last - _first;} //中括号运算符重载 T& operator[](int index) { if (index < 0 || index >= size()) throw "OutOfRangException"; return _first[index]; } //#1 迭代器一般实现成容器的嵌套类型 class iterator { public: friend class vector<T,Alloc>; iterator(vector<T,Alloc> *pvec=nullptr,T *ptr = nullptr) :_ptr(ptr),_pVec(pvec) { Iterator_Base *itb = new Iterator_Base(this,_pVec->_head._next); _pVec->_head._next = itb; } bool operator!= (const iterator &it) const { //检查迭代器的有效性 if(_pVec == nullptr || _pVec != it._pVec) { throw "iterator incompatable!"; } return _ptr!= it._ptr; } //后置++会产生临时量 void operator++() { //检查迭代器有效性 if(_pVec == nullptr) { throw "iterator invalid!"; } _ptr++; } T& operator*() // int data = *it; *it =20 { if(_pVec == nullptr) { throw "iterator invalid!"; } return *_ptr; } const T& operator*()const { return *_ptr; } private: T *_ptr; //当前迭代器是哪个容器对象 vector<T,Alloc> *_pVec; }; //检查迭代器失效 void verify(T *first, T *last) { Iterator_Base *pre = &this->_head; Iterator_Base *it = this->_head._next; while(it != nullptr) { if(it->_cur->_ptr > first && it ->_cur->_ptr <= last) { //迭代器失效,把iterator持有的容器指针置为nullptr it->_cur->_pVec = nullptr; //删除当前迭代器节点,继续判断后面的迭代器节点是否失效 pre->_next = it -> _next; delete it; it = pre->_next; } else { pre = it; it = it->_next; } } } //需要给容器提供begin和end方法 iterator begin() { return iterator(this,_first); } iterator end() { return iterator(this,_last); } //自己定义vector容器的insert方法的实现 iterator insert(iterator it, const T &val) { /* 1.不考虑扩容 verify(it._first - 1,_last); 2.不考虑it._ptr - 1指针的合法性 */ verify(it._ptr - 1,_last); T *p = _last; while(p > it._ptr) { _allocator.construct(p,*(p-1)); _allocator.destroy(p-1); p--; } _allocator.construct(p,val); _last ++; return iterator(this,p); } //自己定义vector容器的erase方法的实现 iterator erase(iterator it) { verify(it._ptr - 1,_last); T *p = it._ptr; while(p < _last -1) { _allocator.destroy(p); _allocator.construct(p,*(p+1)); p++; } _allocator.destroy(p); _last --; return iterator(this,it._ptr); } private: T *_first; // 指向数组起始的位置 T *_last ; // 指向数组中有效元素的后继位置 T *_end ; // 指向数组空间的后继位置 Alloc _allocator; //定义容器的空间配置器对象 //容器迭代器失效增加代码 struct Iterator_Base { Iterator_Base(Iterator *c = nullptr, Iterator_Base *n = nullptr) :_cur(c), _next(n){} iterator *_cur; Iterator_Base *_next; }; Iterator_Base _head; void expand() { int size = _end - _first; // T *temp = new T[size * 2]; T *temp = _allocator.allocate(2*size); for (int i = 0; i < size; i++) { // temp[i] = _first[i]; _allocator.construct(temp+i,_first[i]); } // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); _first = temp; _last = _first + size; _end = _first + size * 2; } };
new 和 delete 重载
重载new delete 的应用: 比如检测内存泄漏,在重载函数中增加一个内存映射,释放内存的时候删除这个映射的内存就行。然后程序结束之后打印就可以看到有没有释放完全了。
1.malloc 和 new 的区别?
a: malloc 按字节开辟内存的;new开辟内存时需要指定类型,new int[]
所以malloc开辟内存返回的都是void* 而new:operator new -> int *
b:malloc 只负责开辟空间, new不仅仅有malloc的功能,可以进行数组的初始化 new int(20); new int[20]();
c:malloc开辟内存失败返回nullptr指针,new抛出的是bad_alloc 类型的异常;
2.free和delete的区别?
delete (int*)p; 先调用析构函数;再析构free(p);
3.new 和 delete能混用吗?
对于普通的编译器内置类型new/delete[] new[]/delete,是没有任何问题的
不能:
Test *p = new Test[]; delete p;
Test *p = new Test(); delete p[];
4. C++为什么区分单个元素和数组的内存分配和释放呢?
自定义的类类型,有析构函数,为了调用正确的析构函数,那开辟对象数组的时候会多开辟4个字节,记录对象的个数
a.如果 new Test[] 用delete释放的话,就会从那多出来的4个字节地址开始释放就会报错
b.用delete[]就会去取4个字节的内存对象的个数,然后就能拿到每个正确的对象地址
//先调用operator new 开辟内存空间、然后调用对象的构造函数(初始化) void* operator new(size_t size) { void *p = malloc(size); if(p == nullptr) throw bad_alloc(); return p; } //delete p; 调用p指向对象的析构函数,再调用operator delete 释放内存空间 void operator delete(void *ptr) { free(ptr); } //数组new void* operator new[](size_t size) { void *p = malloc(size); if(p == nullptr) throw bad_alloc(); return p; } //数组delete void operator delete[](void *ptr) { free(ptr); } class Test { public: Test(int data = 10) : ptr(new int(data)) { cout << "Test()" <<endl; } ~Test() { delete ptr; cout << "~Test()" << endl; } private: int *ptr; };
new 和 delete 重载实现对象池应用
#include <iostream> #include <cstring> using namespace std; template<typename T> class Queue { public: Queue() { _front = _rear = new QueueItem(); } ~Queue() { QueueItem *cur = _front; while(cur != nullptr) { _front = _front->_next; delete cur; cur = _front; } } void push(const T &val) { QueueItem *item = new QueueItem(val); _rear->_next = item; _rear = item; } void pop() { if(empty()) return ; QueueItem *first = _front->_next; _front->_next = first->_next; if(_front->_next == nullptr) { _rear = _front; } delete first; } T front() const { return _front->_next->_data; } bool empty()const { return _front == _rear; } private: struct QueueItem { QueueItem(T data = T()):_data(data),_next(nullptr) {} //提升效率#1 增加给QueueItem提供自定义内存管理 void* operator new (size_t size) { if(_itemPool == nullptr) { _itemPool = (QueueItem *)new char[POOL_ITEM_SIZE * sizeof(QueueItem)]; QueueItem *p = _itemPool; for(;p < _itemPool + POOL_ITEM_SIZE -1; ++p) { p->_next = p + 1; } p->_next = nullptr; } QueueItem *p = _itemPool; _itemPool = _itemPool->_next; return p; } void operator delete(void *ptr) { QueueItem *p = (QueueItem*)ptr; p->_next = _itemPool; _itemPool = p; } T _data; QueueItem *_next; static QueueItem *_itemPool; //指向池内存的首地址 static const int POOL_ITEM_SIZE = 10000; }; QueueItem *_front; //指向队头元素 QueueItem *_rear; //指向队尾 }; template<typename T> typename Queue<T>::QueueItem *Queue<T>::QueueItem::_itemPool = nullptr; int main() { Queue<int> que; for(int i = 0; i < 1000000; ++i) { que.push(i); //申请内存 que.pop(); //释放内存 //以上两操作很频繁,性能不好。现在给QueueItem提供自定义内存管理 } cout << que.empty() << endl; return 0; }
这篇关于C++对象运算符重载,容器迭代器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-25【机器学习(二)】分类和回归任务-决策树(Decision Tree,DT)算法-Sentosa_DSML社区版
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享