5、 C++ operator overloading makes object-oriented programming more convenient

Time:2022-6-1

Complex type Ccomplex

When the compiler performs object operations, it will call the operator overloaded function of the object (call the member method first); If there is no member method, the global scope is used to find the appropriate operator to overload the function

++and--Operator is a unary operator. Putting an int in the parameter list indicates whether it is before or after the number:operator++()Indicates front,operator++(int)The int in parentheses has no effect.

Specific implementation of plural class:

//
// Created by 26685 on 2022-05-16 13:43.
// Description:CComplex.h
//

#ifndef C___CCOMPLEX_H
#define C___CCOMPLEX_H

#include 

using namespace std;

class CComplex {
    friend CComplex operator+(const CComplex &l, const CComplex &r);

    friend iostream &operator<>(istream& is, CComplex &src);

public:
    explicit CComplex(int r = 0, int i = 0) : _real(r), _image(i) {}

//    CComplex operator+(const CComplex& src) const{
//        return CComplex(this->_real+src._real,this->_image+src._image);
//    }
    void show() {
        cout << "real: " << _real << " image: " << _image << endl;
    }

    CComplex &operator++() {
        ++_real;
        ++_image;
        return *this;
    }

    CComplex operator++(int) {
        return CComplex(_real++, _image++);
    }

private:
    int _real;
    int _image;
};

inline CComplex operator+(const CComplex &l, const CComplex &r) {
    return CComplex(l._real + r._real, l._image + r._image);
}

Inline iostream &operator<> (istream& is, Ccomplex &src) {// overload input operations
    is>>src._real>>src._image;
}

#endif //C___CCOMPLEX_H

Main function:

int main(){
    CComplex cp1(10,15);
    CComplex cp2(20,30);
    CComplex cp3=cp2+cp1;
    cp3.show();

    CComplex cp4=cp3++;
    cp4.show();
    cp3.show();
    CComplex cp5= ++cp3;
    cp5.show();
    cp3.show();

    cout<>cp6;
    cout<

Simulate the code that implements the string class

//
// Created by 26685 on 2022-05-16 14:30.
// Description:String.h
//

#ifndef C___STRING_H
#define C___STRING_H

#include 
#include 

class String {

    friend std::ostream &operator<(const String &str) const {
        return strcmp(_pstr, str._pstr) > 0;
    }

    bool operator

At present, the overloading operation efficiency of addition in code is not high, and it needs to be further improved.

The addition overloaded function in the above code will generate temporary objects and affect performance.

Temporarily improved to:

inline String operator+(const String &l, const String &r) {
//    char *ptmp = new char[strlen(l.c_str()) + strlen(r.c_str()) + 1];
    String temp;
    temp._ pstr=new char[strlen(l.c_str()) + strlen(r.c_str()) + 1];// Avoid opening up memory space twice
    strcpy(temp._pstr, l.c_str());
    strcat(temp._pstr, r.c_str());
//    String temp(ptmp);
//    delete[] ptmp;
    return temp;
}

Implementation of iterator for string object

Iterators can transparently access the values of elements inside containers

Foreach traverses the container, and its underlying layer is implemented with iterators

Iterator functions:Provide a unified way to traverse containers transparently

When adding an iterator, the preceding + + is usually used, because new objects will not be generated, and the efficiency will be higher

/**
     *Implementation of iterator, put in string class
     */
    class Iterator{
    public:
        Iterator(char* p= nullptr):_p(p){}

        bool operator!= (const string:: iterator&it) {// judge whether two iterators are equal
            return _p!=it._p;
        }

        void operator++(){
            ++ _p;
        }

        char& operator*(){return *_p;}

    private:
        char* _p;
    };


    Iterator begin(){
        return {_pstr};
    }
    Iterator end(){
        return {_pstr+length()};
    }

Implement iterators in the vector container

Iterators are generally implemented as nested structures of containers.

Add the following code to the vectort class:

class iterator {
    public:
        iterator(const T *p = nullptr)
                : _ptr((int *) p) {}

        bool operator!=(const VectorT::iterator &it) {
            return _ptr != it._ptr;
        }

        void operator++() {
            ++_ptr;
        }

        T &operator*() { return *_ptr; }

    private:
        T *_ptr;
    };

    iterator begin(){
        return {_first};
    }

    iterator end(){
        return {_last};
    }

Failure of iterators

1. When erase is called, the iterator from the current position to the end element is invalidated.

2. When insert is called, the iterator from the current position to the end of the element is invalidated

3. The iterator will also fail after the container is expanded

The iterator from the first element to the insertion / deletion point is still valid

How to resolve iterator failure? To update the iterator!

Iterators of different containers cannot perform comparison operations

Implementation of iterator in vector(including the judgment of iterator failure)

//
// Created by 26685 on 2022-05-15 20:33.
// Description:
//

#ifndef C___VECTORT_H
#define C___VECTORT_H

#include "AllocatorT.h"

using namespace std;

/**
 *The bottom memory development, memory release, object construction and destruction of the container are all implemented through allocator
 * @tparam T
 * @tparam Alloc
 */
template >
class VectorT {
public:
    VectorT(int size = 10) {
//        _first=new T[size];
        _first = _alloctor.allocate(size);
        _last = _first;
        _end = _first + size;
    }

    ~VectorT() {
//        delete[] _first;
        //Delete vectors one by one using allocator
        for (T *p = _first; p != _last; ++p) {
            _alloctor.destory(p);
        }
        _alloctor.deallocate(_first);
        _first = _last = _end = nullptr;
    }

    VectorT(const VectorT &src) {
        int size = src._end - src._first;
//        _first = new T[size];
        _first = _alloctor.allocate(size);
        int len = src._last - src._first;
        for (int i = 0; i < len; i++) {
//            _first[i] = src._first[i];
            _alloctor.contruct(_first + 1, src._first[i]);
        }
        _last = _first + len;
        _end = _first + size;
    }

    VectorT &operator=(const VectorT &src) {
        if (src == *this) {
            return *this;
        }
        //delete[] _first;
        for (T *p = _first; p != _last; p++) {
            _alloctor.destory(p);
        }
        _alloctor.deallocate(_first);

        int size = src._end - src._first;
        _first = new T[size];
        int len = src._last - src._first;
        for (int i = 0; i < len; i++) {
//            _first[i] = src._first[i];
            _alloctor.contruct(_first + 1, src._first[i]);
        }
        _last = _first + len;
        _end = _first + size;
        return *this;
    }

    T &operator[](int index) {
        if (index < 0 || index >= size()) {
            throw "OutOfRangeException";
        }
        return _first[index];
    }

    void push_back(T val) {
        if (full()) {
            expend();
        }
        //*_last++ = val;
        _alloctor.construct(_last, val);
        _last++;
    }

    void pop_back() {
        if (empty()) { return; }
        verify(_last - 1, _last);
        --_last;
        _alloctor.destory(_last);
    }

    T back() const {
        return *(_last - 1);
    }

    bool full() const {
        return _last == _end;
    }

    bool empty() const {
        return _first == _last;
    }

    int size() const {
        return _last - _first;
    }

    /**
     *Implementation iterator
     */
    class iterator {
        friend void VectorT::verify(T *first, T *last);
        friend iterator VectorT::insert(iterator it,const T& val);
        friend iterator VectorT::erase(iterator it);

    public:
        /*iterator(const T *p = nullptr)
                : _ptr((int *) p) {}*/
        /**
         *Implement a new constructor based on the new member variable to enable iterator invalidation
         *@param pvec container pointer
         *@param PTR position pointer
         */
        iterator(VectorT *pvec = nullptr, T *ptr = nullptr) : _ptr(ptr), _pVec(pvec) {
            Iterator_ Base *itb = new Iterator_ Base(this, _pVec->_head._next);// Construct a new node
            _The pVec->_ head._ next = itb;// Connect the head node to the new node
        }//The next step is to invalidate the iterator in the process of changing the array

        bool operator!=(const VectorT::iterator &it) {
            /**
             *Determine whether the iterator fails
             */
            if (_pVec == nullptr || _pVec != it._pVec) {
                throw "iterator incompatable!";
            }

            return _ptr != it._ptr;
        }

        void operator++() {
            if (_pVec == nullptr) {
                throw "iterator invalid!";
            }
            ++_ptr;
        }

        T &operator*() {
            if (_pVec == nullptr) {
                throw "iterator invalid!";
            }
            return *_ptr;
        }

    private:
        T *_ptr;

        /**
         *Implementation iterator失效,首先要添加一个指向容器的指针
         */
        VectorT *_pVec;
    };

    /**
     *Generate corresponding begin and end methods according to the new member methods
     * @return
     */
    iterator begin() {
        return {this, _first};
    }

    iterator end() {
        return {this, _last};
    }

    /**
     *Last step: determine whether the iterator is invalid
     * @param first
     * @param last
     */
    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) {
                //The iterator is invalid. Set the container pointer held by the iterator to null
                it->_cur->_pVec = nullptr;
                //Delete the current iterator node and continue to judge whether the following iterator nodes are invalid
                pre->_next = it->_next;
                delete it;
                it = pre->_next;
            }else{
                pre=it;
                it=it->_next;
            }
        }
    }
    /**
     *Insert operation
     *@param it iterator location
     *@param Val inserted value
     *@return iterator
     */
    iterator insert(iterator it,const T& val){
        /*
         *Expansion is not considered,
         *Regardless of pointer validity
         */
        verify(it._ptr-1,_last);
        T* p=_last;
        while(p>it._ptr){
            _alloctor.construct(p,*(p-1));
            _alloctor.destory(p-1);
            p--;
        }
        _alloctor.construct(p,val);
        _last++;
        return {this,p};

    }

    iterator erase(iterator it){
        verify(it._ptr-1,_last);
        T* p=it._ptr;
        while(p::Iterator_Base *n = nullptr) : _cur(c), _next(n) {}

        iterator *_cur;
        Iterator_Base *_next;
    };

    /**
     *Head node
     */
    Iterator_Base _head;

    Void expand() {//size double
        int size = _end - _first;
//        T *ptmp = new T[size * 2];
        T *ptmp = _alloctor.allocate(2 * size);
        for (int i = 0; i < size; i++) {
            //ptmp[i] = _first[i];
            _alloctor.construct(ptmp + i, _first[i]);
        }
        //delete[] _first;
        for (T *p = _first; p != _last; p++) {
            _alloctor.destory(p);
        }
        _alloctor.deallocate(_first);

        _first = ptmp;
        _last = _first + size;
        _end = _first + (2 * size);
    }
};


#endif //C___VECTORT_H

Deeply understand the principle of new and delete

1. The difference between malloc and new:

  • Malloc exploits memory by bytes, and new exploits memory by specifying the type, such as new int[10]Therefore, malloc opens up the memory and returns void*
  • Malloc is only responsible for developing space. New not only has the function of malloc, but also can initialize data
  • Malloc failed to open the memory, returned the nullptr pointer, and new threw bad_ Alloc type exception

2. The difference between free and delete:

  • Delete calls the destructor, and free is the memory release

Check for memory leaks to override new and delete

Can new and delete be mixed? Why should c++ distinguish between memory allocation and release of a single element and an array?

The built-in type int can be mixed. However, custom classes cannot be mixed, because custom class types have destructors. For the correct destructors, 4 bytes will be opened in front of the array to record the number of objects.

//Overloading of two operators
void* operator new(size_t size){
    void* p=malloc(size);
    if(p== nullptr){
        throw bad_alloc();
    }
    cout<

New and delete overloads to implement object pool application

An object pool is a static linked list created on the heap

//
// Created by 26685 on 2022-05-17 9:40.
// Description:
//

#ifndef C___QUEUEWITHITEMPOOL_H
#define C___QUEUEWITHITEMPOOL_H

#include 

using namespace std;

template
class Queue {

public:
    Queue() {// default construction
        _front=_rear=new QueueItem();
    }
    ~Queue(){
        QueueItem* cur=_front;
        While (cur! = nullptr) {// traverse the linked list and delete the elements one by one
            _front=_front->_next;
            delete cur;
            cur=_front;
        }
    }

    void push(const T& val){
        QueueItem* item=new QueueItem(val);
        _rear->_next=item;
        _The rear=item;// The tail element is set to the new value and distinguished from the front
    }

    void pop(){
        if(empty()){
            return;
        }
        QueueItem* first=_front->_next;
        _front->_next=first->_next;
        If (\u front->\u next== nullptr) {// if the queue has only one valid node
            _rear=_front;
        }
        delete first;
    }

    bool empty() const{
        return _front==_rear;
    }

    T front()const {
        return _front->_next->_data;
    }

private:
    /**
     *Implement a chained queue with a header node
     */
    struct QueueItem {
        QueueItem(T data=T()):_data(data),_next(nullptr){}

        //Overload new to implement object pool
        void* operator new (size_t size){
            If (\u itempool== nullptr) {// if the current memory pool is used up and the last element points to nullptr, a new memory pool will be allocated
                _The itemPool=(QueueItem*)new char[POOL_ITEM_SIZE*sizeof(QueueItem)];// Open object pool
                //We use char to open up by bytes, because if we use new queueitem,
                //The current method will be called again,
                //We are now customizing the new operator overload for queueitem
                QueueItem* p=_itemPool;
                For (; p_next=p+1; // initialize the continuous linked list
                }
                p->_next= nullptr;
            }
            //When creating a new queueitem, the unused node in the object pool will be used, and then the next unused node will be pointed to

            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 const int POOL_ITEM_SIZE=100000;

        static QueueItem *_itemPool;

    };

    QueueItem* _ front;// Point to header node
    QueueItem* _ rear;// Point to the tail of the team,
};

template
typename Queue::QueueItem* Queue::QueueItem::_itemPool= nullptr;

#endif //C___QUEUEWITHITEMPOOL_H