目录
3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)。
注意:
他们之间各论各的,没有关系
运算符重载:让自定义类型可以使用运算符,并且控制运算符的行为,增强可读性
函数重载:可以让函数名相同,参数不同的函数存在。
多个同一运算符的重载可以构成函数重载
- class OB
- {
- public:
- void func()
- {
- cout << "void func()" << endl;
- }
- };
-
- typedef void(OB::* Ptrfunc)();// 成员函数指针类型
-
- int main()
- {
- // 函数指针
- void (*ptr)();
-
- Ptrfunc fp = &OB::func;// 定义成员函数指针p指向函数func
- // 成员函数规定要加&才能取到函数指针
- OB temp;// 定义ob类对象temp
-
-
- (temp.*fp)();// 调用成员函数
-
- return 0;
- }
typedef void(OB::* Ptrfunc)(); // 成员函数指针类型
Ptrfunc fp = &OB::func; // 定义成员函数指针fp指向函数func
(temp.*fp)(); // 调用成员函数
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- //private:
- int _year;
- int _month;
- int _day;
-
- };
-
- // 重载成全局, 无法访问私有成员, 怎么解决?
- // 1.提供这些成员get和set
- // 2.友元
- // 3.重载成成员函数(一般重载成这种)
- //
-
- // 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
- // 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数
- bool operator==(const Date& d1, const Date& d2)
- {
- return d1._year == d2._year
- && d1._month == d2._month
- && d1._day == d2._day;
- }
-
- // d1 - d2
- // d1 + d2 无意义
- // d1 * d2 无意义
- // 一个类要重载哪些运算符是看需求, 看重载有没有价值和意义
-
- int main()
- {
- Date d3(2024, 4, 14);
- Date d4(2024, 4, 15);
-
- // 显示调用(可以正常使用)
- operator==(d3, d4);
-
- // 直接写,转换调用,编译器会转换成operator==(d3, d4)
- d3 == d4;
-
- return 0;
- }
'运行
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
-
- d3.Func(d4);
- //bool Func(const Date& d)
- //{
- // return this->_year == d._year
- // && this->_month == d._month
- // && this->_day == d._day;
- //}
-
- // d3.operator==(d4);
- bool operator==(const Date& d)
- {
- cout << "类中";
- return _year == d._year
- && _month == d._month
- && _day == d._day;
- // 隐藏了this指针
- /*return this->_year == d._year
- && this->_month == d._month
- && this->_day == d._day;*/
- }
-
- //private:
- int _year;
- int _month;
- int _day;
- };
-
- // 如果全局和类中都有运算符重载函数,编译器会选择调用类里的
- bool operator==(const Date& d1, const Date& d2)
- {
- cout << "全局";
- return d1._year == d2._year
- && d1._month == d2._month
- && d1._day == d2._day;
- }
- int main()
- {
- Date d3(2024, 4, 14);
- Date d4(2024, 4, 15);
-
- // 显式调用
- d3.operator==(d4);
-
- // 转换调用 等价于d3.operator==(d4);
- d3 == d4;
-
- return 0;
- }
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
-
- Date(const Date& d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
-
- Date& operator=(const Date& d)
- {
- // 自己给自己赋值
- if (this != &d)
- {
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
- // 需要返回值的原因:支持连续赋值
- return *this;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- Date d1(2024, 4, 14);
-
- // 拷贝构造
- // 一个已经存在的对象,拷贝给另一个要创建初始化的对象
- Date d2(d1);
- Date d3 = d1;
-
- Date d4(2024, 5, 1);
-
- // 赋值拷贝/赋值重载
- // 一个已经存在的对象,拷贝赋值给另一个已经存在的对象
- d1 = d4;
-
- d1 = d2 = d4;
-
- return 0;
- }
'运行
传值返回,返回的是对象的拷贝
引用返回,返回的是对象的别名
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
-
- Date(const Date& d)
- {
- cout << "Date(const Date& d)" << endl;
-
- _year = d._year;
- _month = d._month;
- _day = d._day;
- }
-
- void Print()
- {
- cout << _year << "-" << _month << "-" << _day << endl;
- }
-
- ~Date()
- {
- cout << "~Date()" << endl;
- _year = -1;
- _month = -1;
- _day = -1;
- }
-
- private:
- int _year;
- int _month;
- int _day;
- };
使用引用返回
- Date func()
- {
- Date d(2024, 4, 14);
- return d;
- }
-
- int main()
- {
- const Date& ref = func();
- ref.Print();
-
- return 0;
- }
在main函数中,首先通过调用func函数获取了一个对Date对象的常量引用ref。由于func返回的是一个临时对象,这个对象在表达式结束后就会被销毁。但是,由于ref是对这个临时对象的引用,所以这个临时对象的生命周期会被延长,直到ref的生命周期结束。这是C++11引入的引用折叠和生命周期延长规则的结果。
- Date& func()
- {
- Date d(2024, 4, 14);
- //cout << &d << endl;
- return d;
- }
-
- int fx()
- {
- int a = 1;
- int b = 2;
- int c = 3;
-
- return a + b + c;
- }
-
- int main()
- {
- //Date& ref = func();
- const Date& ref = func();
- cout << &ref << endl;
- fx();
-
- return 0;
- }
在main函数中,通过const Date& ref = func();获取了func函数返回的引用,并将其存储在常量引用ref中。由于func返回的是对局部变量的引用,这里的ref实际上引用了一个已经不存在的对象,因此这是不安全的
- Date& func()
- {
- static Date d(2024, 4, 14);
- return d;
- }
- // 出了作用域,返回对象还在没有析构,那就可以用引用返回,减少拷贝
- // a、返回对象生命周期到了,会析构,传值返回
- // b、返回对象生命周期没到,不会析构,传引用返回
-
- int main()
- {
- const Date& ref = func();
- //ref.Print();
-
- return 0;
- }
func函数返回一个对静态局部变量d的引用,该变量在函数第一次被调用时被初始化,并在程序的整个生命周期内持续存在。由于d是静态的,它不会在func函数返回后被销毁,因此可以安全地返回它的引用。
在main函数中,调用了func函数并将返回的引用赋值给const Date& ref。由于返回的是引用,因此没有发生任何拷贝操作,这是效率更高的做法。
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- int _year;
- int _month;
- int _day;
- };
- // 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
- Date& operator=(Date& left, const Date& right)
- {
- if (&left != &right)
- {
- left._year = right._year;
- left._month = right._month;
- left._day = right._day;
- }
- return left;
- }
- // 编译失败:
- // error C2801: “operator =”必须是非静态成员
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
- class Time
- {
- public:
- Time()
- {
- _hour = 1;
- _minute = 1;
- _second = 1;
- }
- Time& operator=(const Time& t)
- {
- if (this != &t)
- {
- _hour = t._hour;
- _minute = t._minute;
- _second = t._second;
- }
- return *this;
- }
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date
- {
- private:
- // 基本类型(内置类型)
- int _year = 1970;
- int _month = 1;
- int _day = 1;
- // 自定义类型
- Time _t;
- };
- int main()
- {
- Date d1;
- Date d2;
- d1 = d2;
- return 0;
- }
'运行
既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实
现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
- // 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
- typedef int DataType;
- class Stack
- {
- public:
- Stack(size_t capacity = 10)
- {
- _array = (DataType*)malloc(capacity * sizeof(DataType));
- if (nullptr == _array)
- {
- perror("malloc申请空间失败");
- return;
- }
- _size = 0;
- _capacity = capacity;
- }
- void Push(const DataType& data)
- {
- // CheckCapacity();
- _array[_size] = data;
- _size++;
- }
- ~Stack()
- {
- if (_array)
- {
- free(_array);
- _array = nullptr;
- _capacity = 0;
- _size = 0;
- }
- }
- private:
- DataType* _array;
- size_t _size;
- size_t _capacity;
- };
- int main()
- {
- Stack s1;
- s1.Push(1);
- s1.Push(2);
- s1.Push(3);
- s1.Push(4);
- Stack s2;
- s2 = s1;
- return 0;
- }
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
- class Date
- {
- public:
- Date(int year = 1900, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- // 前置++:返回+1之后的结果
- // 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
- Date& operator++()
- {
- _day += 1;
- return *this;
- }
- // 后置++:
- // 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
- // C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
- // 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1
- // 而temp是临时对象,因此只能以值的方式返回,不能返回引用
- Date operator++(int)
- {
- Date temp(*this);
- _day += 1;
- return temp;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
-
-
- void test02()
- {
- Date d1(2024, 4, 14);
- Date d2 = ++d1;
- d1.Print();
- d2.Print();
-
- Date d3 = d1++;
- d1.Print();
- d3.Print();
-
- /*d1.operator++(1);
- d1.operator++(100);
- d1.operator++(0);
- d1.Print();*/
- }
-
- int main()
- {
-
- test02();
-
- return 0;
- }
今天就先到这了!!!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。