c++类和对象(3):默认成员函数(下)

1.拷贝构造函数

如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数

c++规定:类类型的传值传参必须用拷贝构造

1.1拷贝构造函数的特点

1.拷贝构造函数是构造函数的⼀个重载

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//同名函数,形参不同,构成重载//参数是自身类类型的引用Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;
};

2.拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。 拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后面的参数必须有缺省值。

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//对类类型对象的引用Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;
};
int main()
{Date d1(2024,6,6);Date d2(d1);return 0;
}
第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为编译器会不断地调用Date函数

3. C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返 回都会调用拷贝构造完成。
#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, 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(Date* d){_year = d->_year;_month = d->_month;_day = d->_day;}private:int _year;int _month;int _day;
};Date F1()
{Date ret;//..return ret;
}int main()
{Date d1(2024, 2, 3);// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉//都是拷贝构造Date d2(d1);Date d3 = d1;Date d4(F1());Date d5 = F1();return 0;
}

4. 若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成 员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的拷贝构 造。
#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//Date(Date& d)//{//	_year = d._year;//	_month = d._month;//	_day = d._day;//}private:int _year;int _month;int _day;
};int main()
{Date d1(2024,2,3);// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉Date d2(d1);return 0;
}
将显示的拷贝构造函数注释之后我们可以看到,d2依旧完成了拷贝。编译器自动生成了拷贝构造函数,调用自定义类型Date的拷贝构造函数,完成对内置类型int的成员变量的拷贝。
5.像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完成需要的拷贝,所以不需要我们显示实现拷贝构造。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的拷贝构造完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型
Stack成员,编译器自动生成的拷贝构造会调用Stack的拷贝构造,也不需要我们显示实现
MyQueue的拷贝构造。这里还有⼀个小技巧,如果⼀个类显示实现了析构并释放资源,那么他就
需要显示写拷贝构造,否则就不需要。
#include<iostream>
using namespace std;typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}Stack(const Stack& st){// 需要对_a指向资源创建同样⼤的资源再拷⻉值_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");return;}memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;}void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity *sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
private:Stack pushst;Stack popst;
};
int main()
{Stack st1;st1.Push(1);st1.Push(2);// Stack不显⽰实现拷⻉构造,⽤⾃动⽣成的拷⻉构造完成浅拷⻉// 会导致st1和st2⾥⾯的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃Stack st2 = st1;MyQueue mq1;// MyQueue⾃动⽣成的拷⻉构造,会⾃动调⽤Stack拷⻉构造完成pushst/popst// 的拷⻉,只要Stack拷⻉构造⾃⼰实现了深拷⻉,他就没问题MyQueue mq2 = mq1;return 0;
}

6。传值返回会产生⼀个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),好处是没有产生拷贝。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使用引用返回是有问题的,这时的引用相当于⼀个野引用,类似⼀个野指针⼀样。传引用返回可以减少拷贝,但是⼀定要确保返回对象,在当前函数结束后还在,才能用引用返回。

 

typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}Stack(const Stack& st){// 需要对_a指向资源创建同样⼤的资源再拷⻉值_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");return;}memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;}STDataType Top(){return _a[_top - 1];}void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity *sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
private:Stack pushst;Stack popst;
};
Stack& Func()
{Stack st;st.Push(1);st.Push(2);st.Push(3);return st;
}int main()
{Stack ret = Func();cout << ret.Top() << endl;return 0;
}

上面代码会程序崩溃。因为当函数Func结束时会调用Stack的析构函数,会把局部变量st销毁。

 

当我们把用static修饰st,使其存储到静态区,不会因函数结束而被销毁时,代码正常运行:

typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}Stack(const Stack& st){// 需要对_a指向资源创建同样⼤的资源再拷⻉值_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");return;}memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;}STDataType Top(){return _a[_top - 1];}void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity *sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
private:Stack pushst;Stack popst;
};
Stack& Func()
{static Stack st;st.Push(1);st.Push(2);st.Push(3);return st;
}int main()
{Stack ret = Func();cout << ret.Top() << endl;return 0;
}

 

2.赋值运算符重载 

赋值运算符重载是c++的一种操作,它允许程序员为自定义类型重新定义赋值运算符(=)的行为。

2.1运算符重载

运算符重载是c++的一种强大的特性,它允许程序员为自定义类型定义已有的运算符行为。

(1)运算符重载是具有特殊名字的函数,它的名字是由operator后面要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
(2)重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多。⼀元运算符有⼀个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
//私有成员函数外部运算符重载函数不可访问
//private:int _year;int _month;int _day;
};
//除了注释private的三种方法
//1.提供对应的getxxx函数
//2.友元
//3.重载成为成员函数bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}bool operator<(const Date& d1, const Date& d2)
{if (d1._year < d2._year){return true;}else if (d1._year == d2._year && d1._month < d2._month){return true;}else if (d1._year == d2._year && d1._month == d2._month&& d1._day < d2._day){return true;}return false;
}int main()
{Date d1(2024, 6, 6);Date d2(2024, 8, 8);// 运算符重载函数可以显⽰调⽤bool ret1 = operator==(d1, d2);// 编译器会转换成调用对应的// 运算符重载函数 operator==(d1, d2);//operator==(d1, d2)与d1 == d2;效果相同bool ret2 = d1 == d2;bool ret3 = d1 < d2;//内置类型调用简单int i = 1, j = 2;bool ret4 = i < j;cout << ret1 << endl;cout << ret2 << endl;cout << ret3 << endl;cout << ret4 << endl;return 0;
}
(3)如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个
(4)运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
(5)不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}bool operator<(const Date& d){if (_year < d._year){return true;}else if (_year == d._year&& _month < d._month){return true;}else if (_year == d._year&& _month == d._month&& _day < d._day){return true;}return false;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 6, 6);Date d2(2024, 8, 8);bool ret1 = d1.operator==(d2);bool ret2 = d1.operator<(d2);cout << ret1 << endl;cout << ret2 << endl;return 0;
}

(6).*    ::   sizeof   ?:   .这5个运算符不能重载。

(7)重载操作符至少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,内置类型入int,double等,不能把两个整数相加的“+”运算符重载为两个整数相减的操作

//错误示范
int operator+( int x, int y)
{return x - y;
}
(8)⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义,但是重载operator*就没有意义。对于operator-可能是计算两个日期之间的天数差,二operator+则没有明确的、普遍接受的作用。
(9)重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//前置++,返回++后的Date& operator++(){cout << "前置++" << endl;// *this就是d1//*this += 1;return *this;}//后置++,返回++前的Date  operator++(int){cout << "后置++" << endl;Date tmp;//Date tmp(*this);//*this += 1;return tmp;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 9, 9);//编译器会将其转换成d1.operator();d1++;//编译器会将其转换成d1.operator(0);++d1;return 0;
}

(10)在 C++中,重载 << (左移运算符)和 >> (右移运算符)可以实现对自定义类型的输入输出操作, 重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调用时就变成了对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第二个形参位置当类类型对象
一、重载 << 运算符
 
 
#include <iostream>
using namespace std;//1. 作用:通常用于将自定义类型的对象输出到标准输出流(如 cout )或其他输出流对象。
//2. 语法:
ostream& operator<<(ostream& os, const YourClass& obj);
//其中 ostream& 是返回类型,表示可以进行链式输出。 
//os 是输出流对象, 
//const YourClass& obj 是要输出的自定义类型对象。
class Point
{
public:int _x, _y;Point(int x, int y){_x = x;_y = y;}
};
//重载为全局函数
ostream& operator<<(ostream& os, const Point& p)
{os << "(" << p._x << "," << p._y << ")" << endl;return os;
}int main()
{Point p(3, 4);cout << p << endl;return 0;
}



 
二、重载 >> 运算符
 

#include <iostream>
using namespace std;//1. 作用:用于从输入流(如 cin )读取数据并存储到自定义类型的对象中。
//2. 语法:
//istream & operator>>(istream & is, YourClass & obj);
//istream& 是返回类型, is 是输入流对象, 
// YourClass& obj 是要接收输入数据的自定义类型对象。
class Point
{
public:int _x, _y;Point(int x = 0, int y = 0){_x = x;_y = y;}
};istream& operator>>(istream& is, Point& p)
{is >> p._x >> p._y;return is;
}ostream& operator<<(ostream& os, const Point& p)
{os << "(" << p._x << "," << p._y << ")" << endl;return os;
}int main()
{Point p;cout << "输入x和y的值: ";cin >> p;cout << "Point: " << p << endl;return 0;
}


 
通过重载 << 和 >> 运算符,可以使自定义类型的对象像内置类型一样方便地进行输入输出操作。

2.2 赋值运算符重载

赋值运算符重载是⼀个默认成员函数,用于为自定义类型的对象提供自定义的赋值行为,可以完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于⼀个已经存在对象拷贝初始化给另⼀个要创建的对象。
赋值运算符重载的特点:
(1) 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成 const 当前类类型引用,否则会传值传参会有拷贝
(2)有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续值场景。
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1, 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;}//传引用返回减少拷贝//d2 = d3 //d2.operator= d3Date& operator=(const Date& d){// 检查⾃⼰给⾃⼰赋值的情况		if (this != &d){_year = d._year;_month = d._month;_day = d._day;}// d2 = d3表达式的返回对象应该为d1,也就是*thisreturn *this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 9, 9);//拷贝构造用于一个已经存在的对象拷贝初始化给一个要创建的对象Date d2(d1);//赋值运算符重载用于两个已经存在的对象的直接拷贝赋值Date d3(2024, 6, 6);d2 = d3;return 0;
}

(3)没有显式实现时,编译器会自动生成⼀个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数

(4)像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载。像Stack这样的类,虽然也都是 内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部 主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调用Stack的赋值运算符重载, 也不需要我们显示实现MyQueue的赋值运算符重载。这⾥还有⼀个小技巧,如果⼀个类显示实现 了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要。

将赋值运算符注释后,对于Date类成员变量,编译器会自动生成可完成所需要拷贝到赋值运算符,是否显示的写出来没有影响。

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1, 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;}//传引用返回减少拷贝//d2 = d3 //d2.operator= d3//Date& operator=(const Date& d)//{//	// 检查⾃⼰给⾃⼰赋值的情况		if (this != &d)//	{//		_year = d._year;//		_month = d._month;//		_day = d._day;//	}//	// d2 = d3表达式的返回对象应该为d1,也就是*this//	return *this;//}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 9, 9);//拷贝构造用于一个已经存在的对象拷贝初始化给一个要创建的对象Date d2(d1);//赋值运算符重载用于两个已经存在的对象的直接拷贝赋值Date d3(2024, 6, 6);d2 = d3;return 0;
}

显示的写出来的结果:

注释之后的结果:

 

 

2.3日期类实现

Date.h:

#pragma once
#include <assert.h>
#include <iostream>
using namespace std;class Date
{// 友元函数声明friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1);void Print();int GetMonthDay(int year, int month){assert(month > 0 && month <= 13);static int MonthDayArr[] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (year % 4 == 0 & year % 100 != 0) || (year % 400 == 0)){return 29;}else{return MonthDayArr[month];}}bool CheckDate();bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);// d1 += 天数Date& operator+=(int day);Date operator+(int day);// d1 -= 天数Date& operator-=(int day);Date operator-(int day);// d1 - d2int operator-(const Date& d);// ++d1 -> d1.operator++()Date& operator++();// 为了区分,构成重载,给后置++,强⾏增加了⼀个int形参// 这个参数仅仅是为了跟前置++构成重载区分// d1++ -> d1.operator++(0)Date operator++(int);Date& operator--();Date operator--(int);// 流插⼊// 不建议,因为Date* this占据了⼀个参数位置,使⽤d<<cout不符合习惯//void operator<<(ostream& out);private:int _year;int _month;int _day;
};// 重载
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

text.cpp:

#include "Date.h"Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!d.CheckDate()){cout << "日期非法" << endl;}
}void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}bool Date::CheckDate()
{if (_month < 1 || _month>12 || _day<1 || _day>GetMonthDay(_year, _day)){return false;}else{return true;}}bool Date::operator<(const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year&& _month == d._month&& _day < d._day){return true;}return false;
}
//d1 <= d2
//*this是d1,d是d2
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}bool Date::operator>(const Date& d)
{return !(*this <= d);
}
bool Date::operator>=(const Date& d)
{return *this > d || *this == d;
}
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}
bool Date::operator!=(const Date& d)
{return !(*this == d);
}
//d1+=100
//改变了d1的值
Date& Date::operator+=(int day)
{if (day < 0){return *this -= -day;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_month = 1;_year++;}}return *this;
}
//d1+100
//没有改变d1的值
Date Date::operator+(int day)
{//拷贝构造,将d1的值拷贝给tmpDate tmp = *this;tmp += day;return tmp;
}
//d2-=100
Date& Date::operator-=(int day)
{if (day < 0){return *this += -day;}_day -= day;while (_day <= 0){_day += GetMonthDay(_year, _month);_month--;if (_month == 0){_month = 12;_year--;}}return *this;
}
//d2-100
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;while (*this < d) {max = d;min = *this;flag = -1;}int n = 0;while (min != max){min++;n++;}return n * flag;
}//++d1
Date& Date::operator++()
{*this += 1;return *this;
}
//d1++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
//--d1
Date& Date::operator--()
{*this -= 1;return *this;
}
//d1--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "日期非法" << endl;}return in;
}

text.cpp:

#include "Date.h"int main()
{Date d1(2024, 9, 9);Date d2(2024, 6, 6);d1 += 100;d1.Print();//2024-12-18d1 + 100;d1.Print();//2024-12-18Date ret1 = d1 + 100;ret1.Print();//2025-3-28d2 -= 200;d2.Print();//2023-11-19d2 - 100;d2.Print();//2023-11-19Date ret2 = d2 - 100;ret2.Print();//2023-8-10cout << d1 - d2 << endl;//395++d1;d1.Print();Date ret3 = d1++;ret3.Print();d1.Print();cout << d1 << d2;cin >> d1 >> d2;cout << d1 << d2 << endl;return 0;
}

 3.取地址运算符重载

3.1const成员函数

(1)将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
(2)const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。 const 修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this (不能改变this的指向,可以改变this所执行的内容) 变为 const Date* const this
(3)const成员函数可以与非const的成员函数进行重载。如果一个类同时具有const和非const版本的成员函数,那么根据对象的const性质,编译器会自动选择合适的版本进行调用。建议不修改成员变量的成员函数后都加上。
#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// void Print(const Date* const this) constvoid Print() const{cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{// 这⾥⾮const对象也可以调⽤const成员函数// 这是⼀种权限的缩⼩Date d1(2024, 9, 9);d1.Print();//const的修饰使得d2的内容不能被改变const Date d2(2024, 6, 6);d2.Print();return 0;
}

3.2取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。除非⼀些很特殊的场景,比如我们不想让别人取到当前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。
class Date
{
public://显示的写了,便用写了的,编译器不会再自动生成Date* operator&(){return this;//return nullptr}const Date* operator&()const{return this;//return nullptr}
private:int _year; // 年int _month; // ⽉int _day; // ⽇};

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/423704.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI 刚刚推出 o1 大模型!!突破LLM极限

北京时间 9 月 13 日午夜&#xff0c;OpenAI 正式发布了一系列全新的 AI 大模型&#xff0c;专门用于应对复杂问题。 这一新模型的出现代表了一个重要突破&#xff0c;其具备的复杂推理能力远远超过了以往用于科学、代码和数学等领域的通用模型&#xff0c;能够解决比之前更难的…

近期常见软件测试面试题

1、软件的生命周期&#xff1a; 又称为软件生命期、生存期&#xff0c;是指从形成开发软件概念起&#xff0c;所开发的软件使用以后&#xff0c;直到失去使用价值消亡为止的整个过程。 一般来说&#xff0c;整个生命周期包括&#xff1a;计划&#xff08;定义&#xff09;、开…

上汽大众:存储成本节约85%,查询性能提升5倍|OceanBase案例

近日&#xff0c;上汽大众汽车有限公司&#xff08;简称“上汽大众”&#xff09;的积分卡券等关键业务系统&#xff0c;已成功升级至 OB Cloud 云数据库。借助 OceanBase 原生分布式数据库的卓越性能与先进技术&#xff0c;实现了存储成本的大幅降低&#xff0c;高达85%&#…

初级软件测试面试题汇总

一、请描述如何划分缺陷与错误严重性和优先级别&#xff1f; 给软件缺陷与错误划分严重性和优先级的通用原则&#xff1a; &#xff08;1&#xff09;表示软件缺陷所造成的危害和恶劣程度。 &#xff08;2&#xff09;优先级表示修复缺陷的重要程度和次序。 严重性&#xf…

Python 课程6-Pandas 和 Matplotlib库

前言 在数据科学和数据分析领域&#xff0c;Pandas 和 Matplotlib 是两个最常用的 Python 库。Pandas 主要用于数据处理和分析&#xff0c;而 Matplotlib 则用于数据的可视化。它们的结合能够帮助我们快速、直观地展示数据的趋势和规律。在这篇详细的教程中&#xff0c;教程中将…

自动驾驶:LQR、ILQR和DDP原理、公式推导以及代码演示(六、ILQR正则化和line search)

&#xff08;六&#xff09;ILQR正则化和line search 1. ILQR正则化 在iLQR中&#xff0c;我们通常线性化系统动力学并对目标函数进行二阶近似。在反向传播步骤中&#xff0c;我们需要计算逆矩阵&#xff08;控制变量对目标函数的二阶导数矩阵&#xff09;&#xff0c;用以更…

驰域货车四路监控ts视频格式化恢复方法

不少大货车都使用了驰域货车监控&#xff0c;一般是至少装四路&#xff0c;前后左右&#xff0c;有的还会车顶加一路。驰域货车记录仪特殊的地方在于&#xff1a;其采用了一种上古时期的视频格式----TS视频流。 故障存储: 128G卡/fat32 故障现象: 客户提供的信息是格式化后…

软件安装攻略:EmEditor编辑器下载安装与使用

EmEditor是一款在Windows平台上运行的文字编辑程序。EmEditor以运作轻巧、敏捷而又功能强大、丰富著称&#xff0c;得到许多用户的好评。Windows内建的记事本程式由于功能太过单薄&#xff0c;所以有不少用户直接以EmEditor取代&#xff0c;emeditor是一个跨平台的文本编辑器&a…

【STM32】外部中断

当程序正常运行执行main函数&#xff0c;此时如果外部中断来了&#xff0c;执行外部中断函数&#xff0c;实现相应的功能&#xff0c;然后就可以回到main. 一般stm32芯片每个引脚都有自己的外部中断&#xff0c;但是为了限制&#xff0c;会有一个中断线&#xff0c;对应一个中断…

搭建内网文件服务器(FTP),以及实现内网Gitee

一、实现windows搭建FTP&#xff0c;实现文件共享和管理 具体步骤&#xff1a; 1.打开控制面板&#xff0c;搜索功能 2.打开这几个配置 3.打开IIS&#xff0c;添加FTP站点即可 二、实现内网Gitee 参考博客&#xff1a; Gitblit服务器搭建及Git使用-CSDN博客 jdk1.8.0的安…

零基础国产GD32单片机编程入门(二十五)USB口介绍及CDC类虚拟串口通讯详解及源码

文章目录 一.概要二.USB2.0基本介绍及虚拟串口介绍三.GD32单片机USB模块框图四.GD32单片机USB设备模式五.GD32F103C8T6 USB设备CDC类六.配置一个USB虚拟串口收发例程七.工程源代码下载八.小结 一.概要 GD32F103C8T6 USB虚拟串口是一种采用GD32F103C8T6单片机&#xff0c;通过U…

vscode中使用go环境配置细节

1、在docker容器中下载了go的sdk 2、在/etc/profile.d/go.sh里填入如下内容&#xff1a; #!/bin/bashexport GOROOT/home/ud_dev/go export PATH$GOROOT/bin:$PATH3、设置go env go env -w GOPROXYhttps://goproxy.cn,direct go env -w GO111MODULEon 4、重启这个容器&#…

Java之ArrayList

1.ArrayList的简介 在集合框架中&#xff0c;ArrayList是一个普通的类&#xff0c;实现了List接口&#xff0c;具体框架图如下&#xff1a; ArrayList是以泛型方式实现的&#xff0c;使用时必须要先实例化ArrayList实现了RandomAccess接口&#xff0c;表明ArrayList支持随机…

springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件

首先&#xff0c;对数据库进行备份&#xff0c;用到的命令&#xff1a; mysqldump --opt -h 192.168.1.200 --userroot --passwordxxx --result-fileE://data//20240911141400.sql --default-character-setutf8 xxx&#xff08;数据库名&#xff09; 直接上代码 配置文件部分…

速通GPT-3:Language Models are Few-Shot Learners全文解读

文章目录 GPT系列论文速通论文实验总览1. 任务设置与测试策略2. 任务类别3. 关键实验结果4. 数据污染与实验局限性5. 总结与贡献 Abstract1. 概括2. 具体分析3. 摘要全文翻译4. 为什么不需要梯度更新或微调⭐ Introduction1. 概括2. 具体分析3. 进一步分析 Approach1. 概括2. 具…

app抓包 chrome://inspect/#devices

一、前言&#xff1a; 1.首先不支持flutter框架&#xff0c;可支持ionic、taro 2.初次需要翻墙 3.app为debug包&#xff0c;非release 二、具体步骤 1.谷歌浏览器地址&#xff1a;chrome://inspect/#devices qq浏览器地址&#xff1a;qqbrowser://inspect/#devi…

C++:C++的IO流

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 C语言的输入与输出 二 流 三 CIO流 ​编辑 3.1 C标准IO流 3.2 C文件IO流 3.3 stringstream的简单介绍 四 CIO流的效率问题 总结 前言 本篇详细介绍了进一步介绍C中的IO流&#…

点可云ERP进销存V8版本PHPstudy(小皮)安装讲解

安装视频讲解链接&#xff1a; 点可云ERP教程 - PHPstudy安装篇_哔哩哔哩_bilibili 1、下载准备 PHPstudy可在官网下载&#xff08;https://www.xp.cn/&#xff09;不推荐在Linux系统安装小皮面板&#xff0c;Linux系统还是使用宝塔较好&#xff0c; 在PHPstudy官网下载时需要…

Rust运算符

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) https://blog.csdn.net/brucexia/category_12779443.html 前面已经学习了变量和常量&#xff0c;本节开始对它们进行操作&#xff0c…

第三方软件测评机构分享:软件性能测试的测试方法和内容

软件性能测试是对软件系统在特定负载和条件下的性能进行评估的过程。它旨在确定软件的响应时间、稳定性、资源消耗及其可扩展性&#xff0c;以确保其在实际环境中能够满足用户的需求。通过性能测试&#xff0c;开发团队能够发现潜在的瓶颈问题&#xff0c;优化应用程序架构&…