C++运算符重载实现(加号、左移、递增、赋值、关系运算符、函数调用)

先来复习一下C++的内存分区问题~

C++内存分区

C++程序的内存分区可划分为四大内存分区:堆、栈、全局/静态存储区和代码区。 不同类型的变量存放的区域不同。

在程序被编译为exe可执行文件后,未运行之前,分为两个区域:代码区和全局区。

代码区

存放程序体的二进制代码。比如我们写的函数,都是在代码区的。

int a = 0;//静态全局变量区
char *p1; //编译器默认初始化为NULL
void main(){    
        int b; //栈    
        char s[] = "abc";//栈    
        char *p2 = "123456";//123456在字符串常量区,p2在栈上    
        static int c =0; //c在静态变量区,0为文字常量,在代码区    
        const int d=0; //栈    
        static const int d;//静态常量区    
        p1 = (char *)malloc(10);//分配得来得10字节在堆区。    
        strcpy(p1, "123456"); //123456放在字符串常量区,编译器可能会将它与p2所指向的"123456"优化成一个地方
        }

以上所有代码,编译成二进制后存放于代码区,文字常量存放于代码区,是不可寻址的。

全局区

全局区存储全局变量静态变量,还包括了常量区,字符串常量和其他常量(const修饰的全局变量)也存放在全局区。
全局区的数据在程序结束后由操作系统释放。

程序运行之后

栈区

由编译器自动释放,存放函数的参数值,局部变量等。
整个程序的栈区的大小可以在编译器中由用户自行设定。

注意:不要返回局部变量地址。

堆区

由编程人员手动申请,手动释放,若不手动释放,程序结束后由系统回收,生命周期是整个程序运行期间。
使用new进行堆的申请,堆的总大小为机器的虚拟内存的大小。new返回的是该数据类型的指针。

int * p = new int(10);

使用delete进行释放。

重载方式有两种:

1.重载的函数作为成员函数。
2.重载的函数作为全局函数

函数名是规定的:operator+

加号运算符(“+”)重载

对于内置的数据类型,编译器知道如何进行运算。
两个自定义的数据类型,进行相加,就需要进行加号运算符重载。

成员函数实现加号重载:

class Person{
public:
    Person operator+(Person &p){
        Person tmp;
        tmp.a = this->a + p.a;
        tmp.b = this->b + p.b;
        return tmp;
    }
    int a;
    int b;
};

int main(){
    Person A;
    A.a = 10;
    A.b = 20;
    
    Person B;
    B.a = 1;
    B.b = 3;
    
    Person C;
    C = A + B;
    cout << C.a << "  " << C.b << endl;
}

运行结果:


image.png

全局函数实现加号重载:

class Person{
public:
    int a;
    int b;
};

Person operator+(Person &p1,Person &p2){    //需要传入两个参数
    Person tmp;
    tmp.a = p1.a + p2.a;
    tmp.b = p1.b + p2.b;
    return tmp;
}

int main(){
    Person A;
    A.a = 10;
    A.b = 20;

    Person B;
    B.a = 1;
    B.b = 3;

    Person C;
    C = A + B;
    cout << C.a << "  " << C.b << endl;
}

运行结果:


image.png

运算符重载也可以发生函数重载

class Person{
public:
    int a;
    int b;
};

Person operator+(Person &p1,Person &p2){
    Person tmp;
    tmp.a = p1.a + p2.a;
    tmp.b = p1.b + p2.b;
    return tmp;
}

Person operator+(Person &p1,int n){  //函数重载
    Person tmp;
    tmp.a = p1.a + n;
    tmp.b = p1.b + n;
    return tmp;
}

int main(){
    Person A;
    A.a = 10;
    A.b = 20;

    Person B;
    B.a = 1;
    B.b = 3;

    Person C;
    C = A + B;
    cout << C.a << "  " << C.b << endl;

    Person D;
    D = A + 10;
    cout << D.a << "  " << D.b << endl;
}

运行结果:


image.png

左移运算符(“<<”)符重载

可以输出自定义的数据类型。

class Person{

public:
    int a;
    int b;
};

ostream& operator<<(ostream &cout , Person &p){
    cout << p.a << "   " << p.b << endl;
    return cout;   //返回cout 可以实现链式编程
}


int main(){
    Person A;
    A.a = 10;
    A.b = 20;

    cout << A << endl;

}

运行结果:


image.png

左移运算符重载无法用成员函数来实现
因为要实现的是cout << A << endl;看例子吧~

#include <iostream>
using namespace std;
#include <string>

class Person{
public:
    int a;
    int b;

    ostream& operator<<(ostream &cout){
        cout << this->a << "   " << this->b << endl;
        return cout;
    }
};


int main(){
    Person A;
    A.a = 10;
    A.b = 20;

    A << cout << endl; //如果用成员函数,最后实现了是这样的。。。(因为是A调用,所以A写在前)

}

运行结果:


image.png

递增运算符(“++”)重载

class Person{

public:
    int a;
    int b;
    Person& operator++(){   //前置递增返回的是引用,这样++(++A)也不会出错
        a++;
        b++;
        return *this;
    }

    //后置递增返回的是值
    Person operator++(int){    //int是占位参数,可以用来区分前置和后置,必须写int,其他类型不可以
        Person tmp = *this;
        a++;
        b++;
        return tmp;
    }

};

ostream& operator<<(ostream &cout , Person &p){
    cout << p.a << "   " << p.b << endl;
    return cout;
}


int main(){
    Person A;
    A.a = 10;
    A.b = 20;

    cout << "原始A的值:" <<A << endl;
    ++A;
    cout << "A++后的值:" << A << endl;
    A++;
    cout << "A++后的值:" << A << endl;

}

运行结果:


image.png

赋值运算符(“=”)重载

存在复制运算符的原因是堆区的数据重复释放,也就是浅拷贝的问题。
详细解释:

image.png

在对象P1中new一个age变量,这个遍历被存储在堆区,需要程序员手动申请,手动释放,把P1赋给p2时,P2也指向这个堆区的地址空间,所以当使用析构函数对堆区数据进行释放时,P1释放了一次,P2又释放了一次,出现了堆区数据重复释放的情况,程序崩溃。

解决方案:
利用深拷贝来解决浅拷贝的问题。
因为出现程序崩溃的主要原因是,当p2 = p1时,p2直接指向了0x0011这块地址,使用赋值运算符重载,使p2 = p1时,p2开辟一个新的地址空间用来存储年龄18这个数据,这样在释放时,就不会重复释放了。


image.png
class Person {
public:
    Person(int age) {
        this->age = new int(age);
    }

    int *age;

    ~Person() {
        if (age != NULL) {
            delete age;
            age = NULL;
        }
    }

    Person &operator=(Person &p) {  //返回类型是Person,可以链式赋值,p3 = p2 = p1
        if (this->age != NULL) {
            delete age;
            age = NULL;
        }
        age = new int(*p.age);
        return *this;
    }

};


int main() {
    Person p1(10);
    cout << *p1.age << endl;  //p1.age是指针

    Person p2(18);
    cout << *p2.age << endl;

    p2 = p1;
    cout << *p2.age << endl;

    Person p3(20);
    cout << *p3.age << endl;

    p1 = p2 = p3;
    cout << *p1.age << "  " << *p2.age << "  " << *p3.age << endl;


}

运行结果:


image.png

关系运算符重载

public:
    Person(string name, int age,int sex) {
        this->name = name;
        this->age = age;
        this->sex = sex;
    }

    string name;
    int age;
    int sex;

    bool operator==(Person &p){
        return this->name == p.name && this->age == p.age && this->sex == p.sex;
    }

    bool operator!=(Person &p){
        return !(this->name == p.name && this->age == p.age && this->sex == p.sex);
    }

};


int main() {
    Person p1("Tom",23,1);
    Person p2("Tom",23,1);
    Person p3("Amy",23,2);

    cout << (p1 == p2 ? "相等" : "不相等")<< endl;
    cout << (p2 == p3 ? "相等" : "不相等")<< endl;

    cout << (p1 != p2 ? "不相等" : "相等")<< endl;
    cout << (p2 != p3 ? "不相等" : "相等")<< endl;


}

运行结果:


image.png

函数调用运算符重载

class Person {


public:
    void operator()(int b) {
        cout << b << endl;
    }


};


int main() {
    Person p1;
    p1.operator()(6);
    // 匿名函数对象
    Person()(9);


}

运行结果:


image.png

菜鸟教程的C++ 重载运算符和重载函数

补充:


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343