• C++运算符重载


    运算符重载

    C++将运算符重载扩展到自定义的数据类型,它可以让对象操作更美观。
    例如字符串string用加号(+)拼接,cout用两个左尖括号(<<)输出。

    语法:返回值 operator 运算符(参数列表)
    非成员函数版本的重载运算符函数:形参个数与运算符操作数个数相同;
    成员函数版本的重载运算符函数:形参个数比运算符的操作数个数少一个,其中一个操作数隐式传递了调用对象。

    一、重载‘+’运算符

    1.非成员函数版本重载

    #include
    using namespace std;
    class CStudent
    {
        public:
        int score;
        int ID;
        CStudent(){score=80;ID=0;}
        void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
    };
    void operator+(CStudent &s,int a)
    {
        s.score=s.score+a;
    }
    int main()
    {
        CStudent s;
        operator+(s,1);
        s.show();
        s+2;
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    执行结果:
    在这里插入图片描述

    空返回值的运算符重载有个弊端,就是在同一条语句下不能连续使用,如:
    在这里插入图片描述
    如果需要在同一条语句中连续使用,返回值类型不能为空。如下所示:

    #include
    using namespace std;
    class CStudent
    {
        public:
        int score;
        int ID;
        CStudent(){score=80;ID=0;}
        void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
    };
    CStudent& operator+(CStudent &s,int a)//返回值类型不为空
    {
        s.score=s.score+a;
        return s;
    }
    int main()
    {
        CStudent s;
        operator+(s,1);
        s.show();
        s+2+3 ;
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行结果:
    在这里插入图片描述

    2.成员函数版本

    看下面的例子:
    在这里插入图片描述
    说明:成员函数运算符重载里只能有一个参数。

    对于+号运算符来说,类外运算符重载和运算符重载同时存在时,系统会不知道调用哪一个,所以要去掉类外运算符重载。
    在这里插入图片描述

    
    #include
    using namespace std;
    class CStudent
    {
        public:
        int score;
        int ID;
        CStudent(){score=80;ID=0;}
        void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
        void operator+(int b){this->score=this->score+b;}
    };
    // CStudent& operator+(CStudent &s,int a)
    // {
    //     s.score=s.score+a;
    //     return s;
    // }
    int main()
    {
        CStudent s;
        s.operator+(1);
        s.show();
        s+5;
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    执行结果:
    在这里插入图片描述

    同理,返回值类型为void 时,运算符在同一条语句中不能连用,需将返回值返回一个对象引用

    #include
    using namespace std;
    class CStudent
    {
        public:
        int score;
        int ID;
        CStudent(){score=80;ID=0;}
        void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
        CStudent& operator+(int b){this->score=this->score+b;return *this;}
    };
    int main()
    {
        CStudent s;
        (s.operator+(1)).operator+(2);
        s.show();
        s+5+2;
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行结果:
    在这里插入图片描述

    注意事项:
    返回自定义数据类型的引用可以让多个运算符表达式串联起来。(不要返回局部变量的引用)
    重载函数参数列表中的顺序决定了操作数的位置。
    重载函数的参数列表中至少有一个是用户自定义的类型,防止程序员为内置数据类型重载运算符。
    如果运算符重载既可以是成员函数也可以是全局函数,应该优先考虑成员函数,这样更符合运算符重载的初衷。
    重载函数不能违背运算符原来的含义和优先级。
    不能创建新的运算符。

    以下运算符只通过成员函数进行重载:
    = 赋值运算符
    ()函数调用运算符
    []下标运算符
    ->通过指针访问类成员的运算符

    二、重载关系运算符

    关系运算符有:==、!=、>、>=、<、<=用于比较两个自定义数据类型的大小。重载关系运算符时尽量使用成员函数版本。

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {}
        bool operator==(CStudent &rhs)
        {
            if((this->chinese+this->math+this->english)==(rhs.chinese+rhs.english+rhs.math))
            return true;
            return false;
        }
    };
    int main()
    {
        CStudent s1("李华",85,92,95);
        CStudent s2("李明",82,94,96);
        if(s1==s2)
        {
            cout<<"李华和李明成绩一样!"<<endl;
        }
        else
        cout<<"李华和李明成绩不一样!"<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    执行结果:
    在这里插入图片描述

    三、重载左移运算符

    重载左移运算符(<<)用于输出自定义对象的成员变量,在实际开发中很有价值(调试和日志) 只能使用非成员函数版本。
    如果输出对象的私有成员,可以配合友元一起使用。

    语法:

    ostream& operator<<(ostream& cout,constant type& rhs)
    {
    cout<<rhs.xxx<<……<<endl;
    return cout;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {}
        
    };
    ostream &operator<<(ostream& cout,const CStudent&s)
    {
        cout<<"姓名:"<<s.name<<"\n语文成绩:"<<s.chinese<<"\n数学成绩:"<<s.math<<"\n英语成绩:"<<s.english<<endl;
        return cout;
    }
    int main()
    {
       CStudent s("小明",88,79,92);
       cout<<s;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行结果:
    在这里插入图片描述

    四、重载下标运算符

    如果对象中有数组,重载下标运算符[],操作对象中的数组将像操作普通数组一样方便。
    下标运算符必须以成员函数的形式进行重载。

    语法:
    返回值类型 &operator 或者:const 返回值类型 &operator[](参数)const
    使用第一种声明方式,[]不公可以访问数组元素,还可以修改数组元素。 使用第二种声明方式,[]只能访问不能修改数组元素。
    在实际开发中,两种都要提供,目的是为了迁就常对象,因为常对象不能访问非常函数。

    看下面的例子:

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        string &operator[](int i)
        {
            return this->friends[i];
        }
        
    };
    
    int main()
    {
       const CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述
    结论:常对象不能访问非常成员函数

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        string &operator[](int i)
        {
            return this->friends[i];
        }
        
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述

    
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
         string &operator[](int i)
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       s[2]="小北";
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    执行结果:
    在这里插入图片描述> 说明:可以通过重载下标运算符来访问和修改成员变量。

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        const string &operator[](int i)//表示返回值不允许修改
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       s[2]="小北";
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述
    结论:重载下标运算符的返回值为常引用时,不允许修改成员变量。

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        string &operator[](int i)const//常成员函数,注意const的位置。
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       s[2]="小北";
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述
    结论:常成员函数不允许访问非常成员变量。

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        const string &operator[](int i)//表示返回值不允许修改,但重载函数内部可以修改
        {
            this->friends[i]="小豪";
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        const string &operator[](int i)const//表示返回值不允许修改,重载函数内部也不可以修改
        {
            this->friends[i]="小豪";
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        const string &operator[](int i)const
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    执行结果:
    在这里插入图片描述
    结论:非常对象可以访问常成员函数

    
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        string &operator[](int i)const
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述
    没有首const 不能访问非常成员变量

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        const string &operator[](int i)
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       const CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    执行结果:
    在这里插入图片描述
    常对象不能访问非常成员函数

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        string &operator[](int i)
        {
            return this->friends[i];
        }
        const string &operator[](int i)const 
        {
            return this->friends[i];
        }
    };
    
    int main()
    {
       const CStudent s("小明",88,79,92);
       cout<<s[2]<<endl;
       CStudent s1("小叶",92,85,93);
       s1[2]="小明";
       cout<<s1[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    执行结果:
    在这里插入图片描述

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        string &operator[](int i)
        {
            return this->friends[i];
        }
        const string &operator[](int i)const 
        {
            return this->friends[i+1];
        }
    };
    
    int main()
    {
       const CStudent s("小明",88,79,92);
       cout<<s[1]<<endl;
       CStudent s1("小叶",92,85,93);
       cout<<s1[1]<<endl;
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    执行结果:
    在这里插入图片描述
    说明:非常对象优先调用非常成员函数重载,常对象优先调用常成员函数重载。

    五、重载赋值运算符

    C++编译器可能会给类型添加四个函数:
    默认构造函数,空实现。
    默认析构函数,空实现。
    默认挎贝构造函数,对成员变量进行浅挎贝。
    默认赋值函数,对成员变量进行浅挎贝。
    对象的赋值运算是用一个已经存在的对象,给另一个已经存在的对象赋值。
    如果类的定义中没有重载赋值函数,编译器就会提供一个默认的赋值函数。
    如果类中重载了赋值函数,编译器将不提供默认赋值函数。

    请看下面的例子:

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       s1=s;
       cout<<s1.chinese<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    执行结果:
    在这里插入图片描述
    定义了带参构造函数,默认的空无参构造函数失效。

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        CStudent(){}
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       s1=s;
       cout<<s1.chinese<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    执行结果:
    在这里插入图片描述
    说明:上述代码对构造函数进行了重载

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        CStudent(){}
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       s1=s;
       cout<<s1.friends[1]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    执行结果:
    在这里插入图片描述
    说明:数组也能赋值

    对数组来说,这是一种深挎贝:

    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       cout<<s.friends<<endl;
       s1=s;
       cout<<s1.friends<<endl;
       cout<<s1.friends[1]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    执行结果:
    在这里插入图片描述

    重载赋值函数语法:类名 & operator=(const 类名 & 源对象)

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        CStudent(){}
        CStudent &operator=(const CStudent& s)
        {
            if(this==&s)return *this;
            this->name=s.name;
            this->english=s.english;
            this->chinese=s.chinese;
            this->math=s.math;
            for(int i=0;i<3;++i)
            {
                this->friends[i]=s.friends[i];
            }
            return *this;
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       s1=s;
       cout<<s1.name<<endl;
       cout<<s1.math<<endl;
       cout<<s1.friends[2]<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    执行结果:
    在这里插入图片描述

    编译器默认的赋值函数是浅挎贝。如果对象中不存在堆区内存空间,默认赋值函数可以满足需求,否则需要深挎贝。
    赋值运算和挎贝构造不同:拷贝构造是指对象不存在,用已存在的对象进行构造;赋值运算是指已经存在了两个对象,把其中一个对象的成员变量的值赋给另一个对象的成员变量。

    #include
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        int* m_ptr;//计划使用堆区内存
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
            m_ptr=nullptr;
        }
        CStudent(){}
        CStudent &operator=(const CStudent& s)
        {
            if(this==&s)return *this;
            this->name=s.name;
            this->english=s.english;
            this->chinese=s.chinese;
            this->math=s.math;
            for(int i=0;i<3;++i)
            {
                this->friends[i]=s.friends[i];
            }
            if(s.m_ptr==nullptr)//如果源对象的指针为空,则清空目标对象的内存和指针
            {
                if(m_ptr!=nullptr){delete m_ptr;m_ptr=nullptr;}
            }
            else //如果源对象的指针不为空
            {
                //如果目标对象的指针为空,先分配内存
                if(m_ptr==nullptr)m_ptr=new int;
                //然后,把源对象内存中的数据复制到目标对象的内存中
                memcpy(m_ptr,s.m_ptr,sizeof(int));
            }
            return *this;
        }
        ~CStudent(){
            delete m_ptr;
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       cout<<s.m_ptr<<endl;
       s1.m_ptr=new int(3);
       cout<<s1.m_ptr<<endl;
       s1=s;
       cout<<s1.m_ptr<<endl;
       return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    执行结果:
    在这里插入图片描述

    如果对象中存在堆内存空间,一定要在构造函数中初始化对象,否则会产生意想不到的异常。如下:

    #include
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        int* m_ptr;//计划使用堆区内存
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            cout<<"调用带参构造函数"<<endl;
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
            m_ptr=nullptr;
            
        }
        CStudent(){
            cout<<"调用无参构造函数"<<endl;
           // m_ptr=nullptr;//没有进行初始化
        }
        CStudent &operator=(const CStudent& s)
        {
            if(this==&s)return *this;
            this->name=s.name;
            this->english=s.english;
            this->chinese=s.chinese;
            this->math=s.math;
            for(int i=0;i<3;++i)
            {
                this->friends[i]=s.friends[i];
            }
            if(s.m_ptr==nullptr)//如果源对象的指针为空,则清空目标对象的内存和指针
            {
                if(m_ptr!=nullptr){delete m_ptr;m_ptr=nullptr;}
            }
            else //如果源对象的指针不为空
            {
                //如果目标对象的指针为空,先分配内存
                if(m_ptr==nullptr)m_ptr=new int;
                //然后,把源对象内存中的数据复制到目标对象的内存中
                memcpy(m_ptr,s.m_ptr,sizeof(int));
            }
            return *this;
        }
        ~CStudent(){
            delete m_ptr;
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       s.m_ptr=new int(3);
       s1=s;
      cout<<s.m_ptr<<endl;
      cout<<s1.m_ptr<<endl;
      cout<<*(s1.m_ptr)<<endl;
       return 0;
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    执行结果:
    在这里插入图片描述
    说明:产生了异常

    改进:

    
    #include
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        int* m_ptr;//计划使用堆区内存
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            cout<<"调用带参构造函数"<<endl;
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
            m_ptr=nullptr;
            
        }
        CStudent(){
            cout<<"调用无参构造函数"<<endl;
            m_ptr=nullptr;
        }
        CStudent &operator=(const CStudent& s)
        {
            if(this==&s)return *this;
            this->name=s.name;
            this->english=s.english;
            this->chinese=s.chinese;
            this->math=s.math;
            for(int i=0;i<3;++i)
            {
                this->friends[i]=s.friends[i];
            }
            if(s.m_ptr==nullptr)//如果源对象的指针为空,则清空目标对象的内存和指针
            {
                if(m_ptr!=nullptr){delete m_ptr;m_ptr=nullptr;}
            }
            else //如果源对象的指针不为空
            {
                //如果目标对象的指针为空,先分配内存
                if(m_ptr==nullptr)m_ptr=new int;
                //然后,把源对象内存中的数据复制到目标对象的内存中
                memcpy(m_ptr,s.m_ptr,sizeof(int));
            }
            return *this;
        }
        ~CStudent(){
            delete m_ptr;
        }
    };
    
    int main()
    {
       CStudent s("小明",88,79,92);
       CStudent s1;
       s.m_ptr=new int(3);
       s1=s;
      cout<<s.m_ptr<<endl;
      cout<<s1.m_ptr<<endl;
      cout<<*(s1.m_ptr)<<endl;
       return 0;
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    执行结果:
    在这里插入图片描述
    说明:在构造函数中初始化对象后就没问题了。

    六、重载new和delete

    1.重载

    重载new和delete运算符的目的是为了自定义内存分配的细节。(内存池:快速分配和归还,无碎片)
    在c++中,使用new时,编译器做了两件事:
    1)调用标准库函数operator new()分配内存;
    2)调用构造函数初始化内存;
    使用delete时,也做了两件事情:
    1)调用析构函数;
    2)调用标准库函数operator delete()释放内存。
    构造函数和析构函数由编译器的调用,我们无法控制。但是可以重载内存分配函数operator new()和释放函数operator delete();
    1.重载内存分配函数语法:void* operator new(size_t size);
    参数必须是size_t,返回值必须是void*.
    2.重载内存释放函数的语法:void operator delete(void* ptr)
    参数必须是void (指向由operator new()分配的内存),返回值必须是void .
    重载的new和delete时,尽管不必显式地使用static,但实际上仍在创建static成员函数。
    void
    表示该指针指向的是一块与类型无关的内存空间

    下面演示为整型变量动态分配内存:

    #include
    using namespace std;
    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        cout<<"调用了重载的new:"<<size<<"字节。\n";
        void* ptr=malloc(size);//申请内存
        cout<<"申请到的内存的地址是:"<<ptr<<endl;
        return ptr;
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        cout<<"调用了重载的delete.\n";
        if(ptr==0)return;//对空指针delete是安全的
        free(ptr);//释放内存
    }
    int main()
    {
        int *p1=new int(3);
        cout<<"p1="<<(void*)p1<<", *p1="<<*p1<<endl;
        delete p1;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    执行结果:
    在这里插入图片描述

    下面演示为类动态分配内存:

    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        string friends[3];
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
            friends[0]="小华";
            friends[1]="小红";
            friends[2]="小方";
        }
        
    };
    
    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        cout<<"调用了重载的new:"<<size<<"字节。\n";
        void* ptr=malloc(size);//申请内存
        cout<<"申请到的内存的地址是:"<<ptr<<endl;
        return ptr;
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        cout<<"调用了重载的delete.\n";
        if(ptr==0)return;//对空指针delete是安全的
        free(ptr);//释放内存
    }
    int main()
    {
        CStudent *p2=new CStudent("小明",89,85,86);
        cout<<"p2的地址是:"<<p2<<"姓名:"<<p2->name<<",朋友2:"<<p2->friends[1]<<endl;
        delete p2;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    执行结果:
    在这里插入图片描述
    说明:调用了那么多次有可能是使用了string的原因。
    以下来验证(删除string数组):

    
    
    
    #include
    using namespace std;
    class CStudent{
        public:
        string name;//这个name是一个指针(8个字节)
        int chinese;
        int math;
        int english;
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
        }
        
    };
    
    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        cout<<"调用了重载的new:"<<size<<"字节。\n";
        void* ptr=malloc(size);//申请内存
        cout<<"申请到的内存的地址是:"<<ptr<<endl;
        return ptr;
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        cout<<"调用了重载的delete.\n";
        if(ptr==0)return;//对空指针delete是安全的
        free(ptr);//释放内存
    }
    int main()
    {
        CStudent *p2=new CStudent("小明",89,85,86);
        cout<<"p2的地址是:"<<p2<<"姓名:"<<p2->name<<endl;
        delete p2;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    执行结果:
    在这里插入图片描述
    说明:的确的string的原因。

    int test[5];
    cout<<sizeof(p2->test)<<endl;
    
    • 1
    • 2

    执行结果:
    在这里插入图片描述
    说明:对于像数组这样的静态指针来说,sizeof是可以识别出它所指向的内存大小的。

    #include
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        vector<int>test{1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
        }
        
    };
    
    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        cout<<"调用了重载的new:"<<size<<"字节。\n";
        void* ptr=malloc(size);//申请内存
        cout<<"申请到的内存的地址是:"<<ptr<<endl;
        return ptr;
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        cout<<"调用了重载的delete.\n";
        if(ptr==0)return;//对空指针delete是安全的
        free(ptr);//释放内存
    }
    int main()
    {
        CStudent *p2=new CStudent("小明小方小蓝",89,85,86);
        cout<<"p2的地址是:"<<p2<<endl<<"姓名:"<<p2->name<<endl;
        cout<<sizeof(*p2)<<endl;
        delete p2;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    执行结果:
    在这里插入图片描述
    说明:STL变量的本质就是一个指针,虽然不是动态的,但sizeof 无法识别。

    #include
    #include
    using namespace std;
    class CStudent{
        public:
        string name;
        int chinese;
        int math;
        int english;
        vector<int>test{1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
        }
        void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        cout<<"调用了类的重载的new:"<<size<<"字节。\n";
        void* ptr=malloc(size);//申请内存
        cout<<"申请到的内存的地址是:"<<ptr<<endl;
        return ptr;
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        cout<<"调用了类的重载的delete.\n";
        if(ptr==0)return;//对空指针delete是安全的
        free(ptr);//释放内存
    }
        
    };
    
        void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        cout<<"调用了全局重载的new:"<<size<<"字节。\n";
        void* ptr=malloc(size);//申请内存
        cout<<"申请到的内存的地址是:"<<ptr<<endl;
        return ptr;
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        cout<<"调用了全局重载的delete.\n";
        if(ptr==0)return;//对空指针delete是安全的
        free(ptr);//释放内存
    }
    
    
    int main()
    {
        CStudent *p2=new CStudent("小明小方小蓝",89,85,86);
        cout<<"p2的地址是:"<<p2<<endl<<"姓名:"<<p2->name<<endl;
        cout<<sizeof(*p2)<<endl;
        delete p2;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    执行结果:
    在这里插入图片描述
    说明:string 用的是全局new
    CStudent用的是类的new

    为一个类重载New和delete时,尽管不必显式地使用static,但实际上仍在创建static成员函数。
    编译器看到使用new创建自定义类的对象时,它选择成员版本的operator new()而不是全局版的new().
    new[]和delete[]也可以重载。

    2.内存池

    预先分配一大块内存空间
    提升分配和归还速度
    减少内存碎片

    #include
    #include
    #include
    using namespace std;
    class CStudent{
    public:
        string name;
        int chinese;
        int math;
        int english;
        static char* m_pool;//内存池的起始地址
        static bool initpool()//初始化内存池的函数
        {
            m_pool=(char*)malloc(50);
            if(m_pool==0)return false;//如果申请失败,返回false
            memset(m_pool,0,50);//把内存池中的内容初始化为0
            cout<<"内存池的起始地址是:"<<(void*)m_pool<<endl;
            return true;
        }
        static void freepool()
        {
            if(m_pool==0)return ;//如果内存池为空,不需要释放,直接返回
            free(m_pool);//把内存池归还给系统
            cout<<"内存池已释放。\n"<<endl;
        }
        CStudent(string s_name,int s_chinese, int s_math, int m_english):
        name(s_name),chinese(s_chinese),math(s_math),english(m_english)
        {
        }
        void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
    {
        if(m_pool[0]==0)//判断第一个位置是否空闲
        {
            cout<<"分配了第一块内存:"<<(void*)(m_pool+1)<<endl;
            m_pool[0]=1;//把第一个位置标记为已分配
            return m_pool+1;//返回第一个存放对象的地址
        }
        if(m_pool[25]==0)
        {
            cout<<"分配了第二块内存:"<<(void*)(m_pool+1)<<endl;
            m_pool[25]=1;//把第一个位置标记为已分配
            return m_pool+26;//返回第一个存放对象的地址
        }
        //如果以上两位置都不可用,那主直接系统申请内存
        void *ptr=malloc(size);//申请内存
        cout<<"申请到的内存地址是:"<<ptr<<endl;
        return ptr;
        
    }
    void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
    {
        if(ptr==0)return;//如果传进来的地址为空 ,直接返回
        if(ptr==m_pool+1)//如果传进来的地址是内存池的第一个位置
        {
            cout<<"释放第一块内存。\n";
            m_pool[0]=0;//把第一个位置标记为空闲
            return ;
        }
        if(ptr==m_pool+26)//如果传进来的地址是内存池的第二个位置
        {
            cout<<"释放第二块内存。\n";
            m_pool[25]=0;
            return ;
        }
        //如果传进来的地址不属于内存池,把它归还给系统
        cout<<"释放其他内存块。\n"<<endl;
        free(ptr);
    }
    };
    char* CStudent::m_pool=0;//初始化内存池的指针
    int main()
    {
        //初始化内存池 
        if(CStudent::initpool()==false){cout<<"初始化内存池失败。\n";return -1;}
        CStudent *p1=new CStudent("小方",89,85,86);
        cout<<"sizeof(*p1):"<<sizeof(*p1)<<endl;
        CStudent *p2=new CStudent("小方",89,85,86);
        cout<<"sizeof(*p2):"<<sizeof(*p2)<<endl;
        CStudent *p3=new CStudent("小方",89,85,86);
        cout<<"sizeof(*p3):"<<sizeof(*p3)<<endl;
        delete p1;
        delete p2;
        delete p3;
        CStudent::freepool();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    执行结果:
    在这里插入图片描述
    上面只是演示,这个内存池是有一堆bug的

    七、重载括号运算符

    括号运算符也可以重载,对象名可以当成函数来使用(函数对象、仿函数)。
    括号运算符重载函数的语法:
    返回值类型 operator()(参数列表)
    注意:
    括号运算符必须以成员函数的形式进行重载。
    括号运算符重载函数具备普通函数全部的特征。
    如函数对象与全局函数同名,按作用域规则选择调用的函数

    请看下面例子:

    #include
    using namespace std;
    void show(string str)
    {
        cout<<"普通函数:"<<str<<endl;
    }
    class CStudent
    {
        public:
        void operator()(string str)//重载()运算符
        {
            cout<<"重载函数:"<<str<<endl;
        }
    };
    int main()
    {
        CStudent s;
        s("我是一只小菜鸟。");//使用重载的括号运算符
        show("我是一只小菜鸟。");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行结果:
    在这里插入图片描述

    #include
    using namespace std;
    void show(string str)
    {
        cout<<"普通函数:"<<str<<endl;
    }
    class CStudent
    {
        public:
        void operator()(string str)//重载()运算符
        {
            cout<<"重载函数:"<<str<<endl;
        }
    };
    int main()
    {
        CStudent show;
        show("我是一只小菜鸟。");//使用重载的括号运算符
        show("我是一只小菜鸟。");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行结果:
    在这里插入图片描述
    当普通函数和重载函数同时出现时,优先调用重载函数。
    如何解决这个问题?
    使用::,表示使用的是全局函数

    #include
    using namespace std;
    void show(string str)
    {
        cout<<"普通函数:"<<str<<endl;
    }
    class CStudent
    {
        public:
        void operator()(string str)//重载()运算符
        {
            cout<<"重载函数:"<<str<<endl;
        }
    };
    int main()
    {
        CStudent show;
        show("我是一只小菜鸟。");//使用重载的括号运算符
        ::show("我是一只小菜鸟。");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行结果:
    在这里插入图片描述

    函数对象的用途:
    1)表面像函数,部分场景中可以代替函数,在STL中得到广泛的应用;
    2)函数对象的本质是类,可以用成员变量存放更多的信息;
    3)函数对象有自己的数据类型;
    4)可以提供继承体系。

    八、重载一元运算符

    下表为可重载的一元运算符:
    在这里插入图片描述

    一元运算符通常出现在它们所操作的对象的左边。
    但是,自增运算符++和自减运算符–有前置和后置之分。
    为了解决这个问题,C++规定,在重载++或–时,允许写一个增加了int形参的版本,编译器处理后置表达式时,

    以下代码编译会报错:

    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        void operator++()
        {
            m_ranking++;
        }
    };
    int main()
    {
        CStudent s;
        s++;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里是引用
    说明:因为没有重载类的后置++

    再看下面的代码:

    
    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        void operator++()
        {
            m_ranking++;
        }
    };
    int main()
    {
        CStudent s;
        ++s;
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行结果:
    在这里插入图片描述
    但是上面的代码不够完美,因为不可以连续前置++

    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        void operator++()
        {
            m_ranking++;
        }
    };
    int main()
    {
        CStudent s;
        ++(++s);
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这里是引用

    如何解决?返回对象的引用:

    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        CStudent& operator++()
        {
            m_ranking++;
        }
    };
    int main()
    {
        CStudent s;
        ++(++s);
        s.show();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行结果:
    在这里插入图片描述

    如何重载后置++?

    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        CStudent& operator++()
        {
            m_ranking++;
            return *this;
        }
        CStudent& operator++(int)//int形参
        {
            m_ranking++;
            return *this;
        }
    };
    int main()
    {
        CStudent s;
        s++;
        s.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行结果:
    在这里插入图片描述
    上面的代码也是有问题的

    请看下面的例子:
    在这里插入图片描述
    标准的c++不支持后置++连续使用

    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        CStudent& operator++()
        {
            m_ranking++;
            return *this;
        }
        CStudent& operator++(int)
        {
            m_ranking++;
            return *this;
        }
    };
    int main()
    {
        CStudent s1,s2;
        int ii=5,jj=5;
        int xx=++(++(++ii));cout<<"xx="<<xx<<",ii="<<ii<<endl;
        int yy=jj++;;cout<<"yy="<<yy<<",jj="<<jj<<endl;
        ((s2++)++)++;
        s2.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    执行结果:
    在这里插入图片描述
    然而上面的定义却支持嵌套后置++,虽然支持,但最好别用

    再看下面的例子:

    #include
    using namespace std;
    class CStudent
    {
        public:
        string m_name;
        int m_ranking;//排名
        //默认构造函数
        CStudent(){m_name="小明";m_ranking=5;}
        //自我介绍的方法
        void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
        CStudent& operator++()
        {
            m_ranking++;
            return *this;
        }
        CStudent& operator++(int)
        {
            m_ranking++;
            return *this;
        }
    };
    int main()
    {
        CStudent s1,s2;
        int ii=5,jj=5;
        int xx=++(++(++ii));cout<<"xx="<<xx<<",ii="<<ii<<endl;
        int yy=jj++;;cout<<"yy="<<yy<<",jj="<<jj<<endl;
        CStudent s3=++(++(++s1));cout<<"s3.m_ranking="<<s3.m_ranking<<",s1.m_ranking="<<s1.m_ranking<<endl;
        CStudent s4=s2++;cout<<"s4.m_ranking="<<s4.m_ranking<<",s2.m_ranking="<<s2.m_ranking<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    执行结果:
    在这里插入图片描述
    说明:显然类重载后的后置++运算符与整数运算的效果不一样,这个结果不符合++运算符的后置语义。

    改进方法:

     CStudent operator++(int)
        {
            CStudent tmp=*this;
            m_ranking++;
            return tmp;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行结果:
    在这里插入图片描述

    但注意函数的返回值不能是引用,否则:

    CStudent &operator++(int)
        {
            CStudent tmp=*this;
            m_ranking++;
            return tmp;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行结果:
    在这里插入图片描述
    总结:
    在这里插入图片描述

    学习链接

  • 相关阅读:
    Node.js | 常用内置模块之 path 路径模块
    MYSQL主从复制
    《数据结构、算法与应用C++语言描述》-栈的应用-列车车厢重排问题
    npm cnpm pnpm yarn 有什么区别? 哪个更好用呢?
    微信小程序开发14 复杂数据库设计:如何用好云开发提供的文档型数据库?
    排查与解决死锁
    Nginx 配置 HTTPS 过程(+反向代理)
    网站的静态资源怎么获取?
    无线充电器出口欧盟CE认证RED指令测试分析
    【附源码】Python计算机毕业设计魔术教学网站
  • 原文地址:https://blog.csdn.net/weixin_44923692/article/details/127833188