【C++】拷贝构造函数与运算符重载
写在前面
拷贝构造函数、赋值运算符重载、取地址运算符
都是属于类的默认成员函数!
默认成员函数是程序猿不显示声明定义,编译器会中生成。
在程序编写中,我们也经常使用拷贝的方式来获取到对应的值,例如整形变量拷贝int a = 0; int b = a;
等等。在程序的编写中,我们也会需要进行对象的拷贝,这样来获取到对应对象的值。
文章目录
- 写在前面
- 一、拷贝构造函数
- 1.1、若未显式定义,编译器会生成默认的拷贝构造函数。
- 二、运算符重载
- 2.1、赋值运算符重载(类的默认成员函数)
- 2.2、重载运算符中的特殊:前置++和后置++的重载
- 2.3、流插入与流提取的重载运算符
- 2.4、重载取地址操作符(是类的默认成员)
一、拷贝构造函数
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。是一个构造函数
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
我们先创建一个标准的拷贝构造函数
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}Date(const Date& d1) { //拷贝构造函数_year = d1._year;_month = d1._month;_day = d1._day;}private:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2024,1,1);printf("d1:>");d1.Print();Date d2(d1);printf("d2:>");d2.Print();return 0;
}
程序运行结果:
如果我们不使用类引用作为参数,而是使用传值的方式作为参数的话,会导致无穷递归调用
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}Date(Date d1) { //拷贝构造函数,使用传值方式_year = d1._year;_month = d1._month;_day = d1._day;}private:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2024,1,1);printf("d1:>");d1.Print();Date d2(d1);printf("d2:>");d2.Print();return 0;
}
- 我们知道,函数参数使用传值,那么形参就是实参的一份临时拷贝。
C++规定的拷贝:
- 内置类型直接拷贝
- 自定义类型必须调用该自定义类型对应的拷贝构造函数完成拷贝。
- 此时,形参是
stack
类的对象,我们实参也是stack
类的对象d1
,形参接收就需要进行拷贝,而这个时候又涉及到了类的拷贝,那么也是调用stack
类的拷贝构造函数,但是拷贝构造函数又时需要传值……无限套娃,如下图
程序运行结果:
- 编译器会检查拷贝构造函数是否正确的编写。
在拷贝构造中,参数要加上const
,加上const
的好处:
- 防止在拷贝构造中写反,导致原对象被修改为其他值。
- 如果原对象是
const
修饰的对象,也可以进行拷贝构造,不会造成权限放大。 - 可以作为传值返回函数的接收对象。
class Date
{
public:Date() {//cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date(const Date& d1) {cout << "Date(const Date& d1)" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}private:int _year = 1;int _month = 1;int _day = 1;
};Date getDate() {Date d(2222, 1, 1);return d;
}int main() {Date d1(2022, 5, 4);Date d2 = getDate();return 0;
}
- 在拷贝构造的形参中加入
const
后,就可以使用getDate();
函数的返回值,作为拷贝构造的形参。 - 我们知道函数的返回值返回的是一个临时变量,临时变量中具有常性。所以我们在拷贝构造函数中加入
const
后就可以接收常性的参数
1.1、若未显式定义,编译器会生成默认的拷贝构造函数。
默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
拷贝构造对类型的处理:
- 对内置类型成员进行值拷贝/浅拷贝
- 对自定义类型成员会调用它的拷贝构造函数
当类中的属性全部都是内置类型成员,我们可以使用默认生成的拷贝构造函数完成拷贝操作
我们把Date
类自己编写的拷贝构造删除后,尝试使用默认的拷贝构造函数。
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2024,1,1);printf("d1:>");d1.Print();Date d2(d1);printf("d2:>");d2.Print();return 0;
}
程序运行结果:
在上图中,我们也可以看出,程序没有编写拷贝构造函数,使用默认拷贝构造函数也完成了任务。但这并不代表可以一直使用默认构造函数。
我们使用stack
类来尝试一下,默认拷贝构造函数是否可以完成我们预想的结果。
class stack {
public:stack(int defintCapacity = 4) {_arr = (int*)calloc(defintCapacity, sizeof(int));if (nullptr == _arr){perror("malloc申请空间失败");return;}_capacity = defintCapacity;_size = 0;}stack(int* arr, int defintCapacity) {if (nullptr == arr) {perror("malloc申请空间失败");return;}_arr = arr;_capacity = defintCapacity;_size = 0;}void push(int x) {//....扩容等_arr[_size++] = x;}~stack() {free(_arr);_arr = nullptr;}
private:int* _arr;int _size;int _capacity;
};int main() {stack s1;stack s2(s1);return 0;
}
程序运行结果:
- 因为在默认的拷贝构造函数对象按内存存储按字节序完成拷贝,在拷贝结束后发现
s2
对象的_arr
数组地址和s1
对象的_arr
数组地址一样。 - 这时候我们如果我们往
s2
存储内容,也会改变s1
对象的内容,这不是我们想要的,而且在对象结束生命周期之后,对象会自动调用自己对应的析构函数。这时候析构函数就多次释放同一个空间程序崩溃,如下图。
使用浅拷贝的内存图布局如下图:
要解决这种问题,就需要涉及深拷贝(不才后面会专门写一篇笔记),下面代码是针对这种情况的特殊解决办法。
class stack {
public:stack(int defintCapacity = 4) {_arr = (int*)calloc(defintCapacity, sizeof(int));if (nullptr == _arr){perror("malloc申请空间失败");return;}_capacity = defintCapacity;_size = 0;}stack(int* arr, int defintCapacity) {if (nullptr == arr) {perror("malloc申请空间失败");return;}_arr = arr;_capacity = defintCapacity;_size = 0;}stack(stack& s) { //需要程序猿自己编写拷贝构造函数(深拷贝)_arr = (int*)calloc(s._capacity, sizeof(int));if (nullptr == _arr){perror("malloc申请空间失败");return;}memcpy(_arr, s._arr, s._capacity * sizeof(int));_capacity = s._capacity;_size = s._size;}void push(int x) {//....扩容等_arr[_size++] = x;}~stack() {free(_arr);_arr = nullptr;}
private:int* _arr;int _size;int _capacity;
};
我们解决了内置类型拷贝构造的问题后,我们自定义类型是否需要每个都写拷贝构造函数呢?
我们使用栈实现队列
class stack {
public:stack(int defintCapacity = 4) {_arr = (int*)calloc(defintCapacity, sizeof(int));if (nullptr == _arr){perror("malloc申请空间失败");return;}_capacity = defintCapacity;_size = 0;}stack(int* arr, int defintCapacity) {if (nullptr == arr) {perror("malloc申请空间失败");return;}_arr = arr;_capacity = defintCapacity;_size = 0;}void push(int x) {//....扩容等_arr[_size++] = x;}~stack() {free(_arr);_arr = nullptr;}stack(stack& s) {_arr = (int*)calloc(s._capacity, sizeof(int));if (nullptr == _arr){perror("malloc申请空间失败");return;}memcpy(_arr, s._arr, s._capacity * sizeof(int));_capacity = s._capacity;_size = s._size;}
private:int* _arr;int _size;int _capacity;
};class MyQueue{private:stack s1;stack s2;
};int main() {stack s1;s1.push(1);s1.push(2);s1.push(3);stack s2(s1);return 0;
}
程序运行结果:
- 这时,
MyQueue
类就不需要编写拷贝构造,因为在MyQueue
对象进行拷贝时,会自动调用stack
类的拷贝构造。 - 不写拷贝构造函数与不需要写构造函数与析构函数的逻辑是一样的。
- 拷贝构造函数的形参也尽可能的加上
const
修饰权限范围,这样可以防止形参对象的属性被修改,而且也可以防止实参的权限放大的问题
二、运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,其中在自定义类型中,所有的运算符都需要程序员进行运算符重载
日期类比较大小
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}public:int _year = 1;int _month = 1;int _day = 1;
};bool Less(const Date& s1, const Date& s2) {if (s1._year < s2._year) {return true;}else if (s1._year == s2._year && s1._month < s2._month) {return true;}else if (s1._year == s2._year && s1._month == s2._month && s1._day < s2._day) {return true;}return false;
}bool Larger(const Date& s1, const Date& s2) {if (s1._year > s2._year) {return true;}else if (s1._year == s2._year && s1._month > s2._month) {return true;}else if (s1._year == s2._year && s1._month == s2._month && s1._day > s2._day) {return true;}return false;
}
int main() {Date d1(2022,5,4); Date d2(2022,5,5);cout << Less(d1, d2) << endl;cout << Larger(d1, d2) << endl;return 0;
}
- 在我们自定义的日期类中,我们想比较两个类的大小只能通过函数的形式来比较
- 如果函数命名不规范时,我们难以比较
所以在C++中增加了赋值运算符重载,方便程序猿的使用
运算符重载关键字:
operator
后面接需要重载的运算符符号。
我们使用运算符重载来改善上述代码:
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}public:int _year = 1;int _month = 1;int _day = 1;
};bool operator<(const Date& s1, const Date& s2) {if (s1._year < s2._year) {return true;}else if (s1._year == s2._year && s1._month < s2._month) {return true;}else if (s1._year == s2._year && s1._month == s2._month && s1._day < s2._day) {return true;}return false;
}bool operator>(const Date& s1, const Date& s2) {if (s1._year > s2._year) {return true;}else if (s1._year == s2._year && s1._month > s2._month) {return true;}else if (s1._year == s2._year && s1._month == s2._month && s1._day > s2._day) {return true;}return false;
}
int main() {Date d1(2022, 5, 4);Date d2(2022, 5, 5);cout << operator<(d1, d2) << endl;cout << operator>(d1, d2) << endl;return 0;
}
我们只需要把函数名修改为:operator<
和operator>
。这样就完成了 <
和>
的运算符重载。我们在main
函数中调用也是如此,使用operator<
和operator>
来调用此函数。但是这样就和我们编写函数没什么区别。
运算符重载的作用是可以直接在main
函数中使用<
和>
来进行比较。如下:
int main() {Date d1(2022, 5, 4);Date d2(2022, 5, 5);cout << operator<(d1, d2) << endl;cout << operator>(d1, d2) << endl;printf("\n");cout << (d1 < d2) << endl;cout << (d1 > d2) << endl;return 0;
}
程序运行结果:
-
虽然我们是使用了
<
和>
来进行比较,但是我们通过operater
来重载<
和>
后,我们就可以直接使用<
和>
来进行比较。本质上还是使用operator<
和operator>
来调用此函数,编译器会自己处理的过程。如下图: -
这时,
operater
重载的<
和>
是全局的,对类中的属性要求就一定是public
类型,如果是私有的就无法访问,所以我们要重载类对象的<
和>
时,可以把operater
函数作为类的成员函数。
运算符重载必须使用满足C++对运算符重载的规定:
- 不能通过连接其他符号来创建新的操作符:比如
operator@
- 重载操作符必须有一个类类型参数,即不能全部都是内置类型,比如
bool operator+(int& a ,int& b){...}
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型
+
,不能改变其含义,如改为减… - 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的
this
.*
::
(域作用限定符)sizeof
?:
(三目).
注意以上5个运算符不能重载。
我们把上述例子的全局运算符重载函数改为Date
类的内置成员函数。
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}bool operator<(const Date& s2) {if (_year < s2._year) {return true;}else if (_year == s2._year && _month < s2._month) {return true;}else if (_year == s2._year && _month == s2._month && _day < s2._day) {return true;}return false;}bool operator>( const Date& s2) {if (_year > s2._year) {return true;}else if (_year == s2._year && _month > s2._month) {return true;}else if (_year == s2._year && _month == s2._month && _day > s2._day) {return true;}return false;}
public:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2022, 5, 4);Date d2(2022, 5, 5);cout << (d1 < d2) << endl;cout << (d1 > d2) << endl;printf("\n");cout << d1.operator<(d2) << endl;cout << d1.operator>(d2) << endl;return 0;
}
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的
this
- 而且作为类成员函数重载时,就算属性是私有的,我们也可以进行比较。
- 类成员函数重载时,在
main
函数中,也可以直接使用<
和>
来进行比较,编译器最终也是会转换为d1.operator<(d2)
2.1、赋值运算符重载(类的默认成员函数)
赋值运算符重载与 拷贝构造函数不同处:
- 拷贝构造函数适用于:用一个已经存在的对象初始化另外一个对象。
- 赋值运算符重载函数适用于:已经存在的两个对象之间赋值拷贝。
class Date
{
public:Date() {cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date(const Date& d1) {cout << "Date(const Date& d1)" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}bool operator<(const Date& s2) {if (_year < s2._year) {return true;}else if (_year == s2._year && _month < s2._month) {return true;}else if (_year == s2._year && _month == s2._month && _day < s2._day) {return true;}return false;}bool operator>(const Date& s2) {if (_year > s2._year) {return true;}else if (_year == s2._year && _month > s2._month) {return true;}else if (_year == s2._year && _month == s2._month && _day > s2._day) {return true;}return false;}void operator=(const Date& s2) {_year = s2._year;_month = s2._month;_day = s2._day;}
public:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2022, 5, 4);Date d2(2000, 1, 1);d1 = d2;return 0;
}
程序运行结果
- 程序运行结果没有问题,但是我们在内置类型赋值的时候可以连续赋值的,如
int a,b; a=b=12;
,但是我们把赋值运算符重载
的返回值设置了void
,程序运行不了连续赋值的结果。如下图:
我们可以把返回值设置为Date
类作为返回值。如下程序:
Date operator=(const Date& s2) {_year = s2._year;_month = s2._month;_day = s2._day;return *this;
}
- 这样我们就可以完成连续赋值的处理,如下图
但是,我们直接使用传值返回,会造成大量无用的拷贝构造,这样会对性能造成一点影响:
我们就可以把传值返回改为传引用返回。虽然this
指针是形参,会随着函数的生命周期结束而销毁,但是我们可以把*this
作为返回,这样我们返回的是对象的地址,对象的生命周期不会随着函数的生命周期结束而销毁。
Date& operator=(const Date& s2) {_year = s2._year;_month = s2._month;_day = s2._day;return *this;
}
程序运行结果:
d1 = d2
其底层与d1.operator=(d2)
是一样的- 当然,在使用重载赋值运算符时,程序猿会出现自己与自己赋值的情况。这时候我们可以在重载运算符中加入自己给自己赋值的条件判断。这样可以避免自己给自己赋值。
Date& operator=(const Date& s2) {if (this != &s2) {_year = s2._year;_month = s2._month;_day = s2._day;}return *this;
}
赋值运算符重载格式:
- 参数类型:
const T&
,传递引用可以提高传参效率 - **返回值类型:**T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回
*this
: 要复合连续赋值的含义
因为赋值运算符重载是类的默认成员函数,即用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意编译器默认生成的赋值运算符重载函数:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。与拷贝构造的行为一样。
需要注意的是,编译器生成的赋值运算符拷贝是逐字节拷贝,即浅拷贝的方式。 遇到指针等相关拷贝的情况,需要程序猿编写深拷贝。
赋值运算符只能重载成类的成员函数不能重载成全局函数
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
2.2、重载运算符中的特殊:前置++和后置++的重载
在重载运算符中有前置++
和后置++
两个特殊的运算符重载。因为在程序中这两个运算符的函数名都是一样的。需要函数重载来区别前置++
和后置++
。(重载运算符--
同理)
我们先实现前置++
,还是使用上述的Date
类为例。
class Date
{
public:Date() {//cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date(const Date& d1) {cout << "Date(const Date& d1)" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}bool operator<(const Date& s2) {if (_year < s2._year) {return true;}else if (_year == s2._year && _month < s2._month) {return true;}else if (_year == s2._year && _month == s2._month && _day < s2._day) {return true;}return false;}bool operator>(const Date& s2) {if (_year > s2._year) {return true;}else if (_year == s2._year && _month > s2._month) {return true;}else if (_year == s2._year && _month == s2._month && _day > s2._day) {return true;}return false;}Date& operator=(const Date& s2) {_year = s2._year;_month = s2._month;_day = s2._day;return *this;}Date& operator++() {//前置++_day++;return *this;//因为是前置++,可以把加完后的结果直接当做返回值。}public:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2022, 5, 4);++d1;d1.Print();return 0;
}
程序运行结果:
operator++()
:因为++
是自增不需要参数,所以我们把形参列表设为空。
我们把operator++()
当做为前置++
,但后置++
也是相同的函数名operator++
,所以在C++中我们在后置++
的形参中增加一个参数,让其形成函数重载,以达到前置++
和后置++
的区别。
但是 ++
是自增,不需要接收任何形参,但是需要完成函数重载,所以我们只需要在形参列表中增加一个int
类型即可完成重载,该类型可以不添加变量名称。如下
Date& operator++() {//前置++_day++;return *this;//因为是前置++,可以把加完后的结果直接当做返回值。}Date operator++(int) {//后置++Date d1 = *this;//因为是后置加加,所以要把原来的结果先储存为一个临时变量。之后,我们才可以进行自增处理_day++;return d1;//返回的是临时变量的值。}
- 后置
++
中,operator++(int)
的int
参数不是为了接收具体的值。仅仅是占位,跟前置++
构成函数重载。
其中前置++
和后置++
的调用会由编译器区分。我们程序员不需要理会如何进行调用。
在我们程序员自己编写的前置++
和后置++
中。会有性能的区别。因为 后置++
需要进行拷贝构造,创建一个临时变量来作为返回值,而且也是传值返回。但是在内置类型的++
或--
中性能区别不大。
2.3、流插入与流提取的重载运算符
在上面例中,我们Date
类对象的内容打印是使用print
函数来进行Date
类属性的显示,为了更好的打印,我们可以使用流的重载运算符来进行自定义类型的打印。
不才在文档查询网站中,查询到cin
是istream
类的对象和cout
是osterem
类的对象,那么我们在Date
类中,就可以通过istream
类和osterem
类来自定义引用对象来进行标准输入输出。
我们先以打印为例,即重载<<
运算符。
class Date
{
public:Date() {//cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date(const Date& d1) {cout << "Date(const Date& d1)" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}void operator<<(ostream& out) {out << _year << "年" << _month << "月" << _day << "日";}private:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2022, 5, 4);//d1.Print();cout << d1;Date n1 = d1;//n1.Print();cout << d1 << endl << n1 << endl;return 0;
}
运行结果:
- 因为我们是把重载运算符写入了类中,成为了类的方法,所以我们直接使用
cout << d1
是无法访问的 cout << d1
其本质是cout.operator(d1)
,我们在库中是没有自定义类型重载的。如下图- 所以我们在
Date
类中重载的<<
运算符,需要d1 << cout
这样来使用,因为这才符合d1.operator(cout)
的函数调用
但是d1.operator(cout)
明显不符合我们是使用习惯,但是在类中,第一个形参默认是this
指针,无法改变的。所以我们就把operator<<
定义在类外面,作为全局函数。
作为全局重载运算符函数时,我们久可以把第一个参数设置为ostrem
,第二次参数设置为我们自定义类型Date
,这样久可以完成cout << d1
的使用。如下图
- 此时虽然,
cout << d1
没有报错,但是我们类中的属性时私有的,为了访问到我们私有的属性,我们在类中可以把全局重载运算符函数operater<<
声明为友元函数。(不才后面会专门出一篇笔记讲解)
class Date
{friend void operator<<(ostream& out, const Date& d1);//把operator<<函数声明为友元函数public:Date() {//cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date(const Date& d1) {//cout << "Date(const Date& d1)" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}private:int _year = 1;int _month = 1;int _day = 1;
};void operator<<(ostream& out, const Date& d1) {out << d1._year << "年" << d1._month << "月" << d1._day << "日";
}
- 在类中声明前加上
friend
关键字,声明void operator<<(ostream& out, const Date& d1)
函数时友元函数。
运行结果:
和之前的运算符重载函数相同,我们为了多次运用<<
,我们的返回值久设置为ostrem
,这样我们就可以实现复合使用cout << d1 << endl
。又因为cout
是iostream
创建的对象,而且cout
的生命周期是程序的声明周期,所以我们可以把ostream
设置为ostream&
引用返回
ostream& operator<<(ostream& out, const Date& d1) {out << d1._year << "年" << d1._month << "月" << d1._day << "日";return out;
}
运行结果:
同理,流插入也是如此
istream& operator>>(istream& in, Date& d1) {in >> d1._year >> d1._month >> d1._day;return in;
}
- 也需要在类中声明为友元函数。
2.4、重载取地址操作符(是类的默认成员)
因为也只是取地址,所以函数设计就可以非常简单。只需要返回值是:类类型指针
。返回值是this
指针即可
Date* operator&() {cout << "Date* operator&()" << endl;return this;
}
如果遇到const
修饰的话,我们也需要this
指针设置为const
成员,而且返回值也是const+类类型指针
。如下程序:
const Date* operator&() const {cout << "Date* operator&() const" << endl;return this;}
编译器会根据是否为const
修饰的对象来调用对应的取地址运算符重载函数。
class Date
{
public:Date() {//cout << "Date()" << endl;}Date(int year, int month, int day) {_year = year;_month = month;_day = day;}Date(const Date& d1) {//cout << "Date(const Date& d1)" << endl;_year = d1._year;_month = d1._month;_day = d1._day;}Date* operator&() {cout << "Date* operator&()" << endl;return this;}const Date* operator&() const {cout << "Date* operator&() const" << endl;return this;}private:int _year = 1;int _month = 1;int _day = 1;
};int main() {Date d1(2022, 5, 4);const Date d2(200, 1, 1);Date* pd1 = &d1;const Date* pd2 = &d2;return 0;
}
程序运行结果为:
重载取地址运算符并没有什么实际上的作用。因为属于类的默认成员函数,不需要程序员写会默认生成。所达到的效果是一样的。
以上就是本章所有内容。若有勘误请私信不才。万分感激💖💖 如果对大家有用的话,就请多多为我点赞收藏吧~~~💖💖
ps:表情包来自网络,侵删🌹
相关文章:

【C++】拷贝构造函数与运算符重载
写在前面 拷贝构造函数、赋值运算符重载、取地址运算符都是属于类的默认成员函数! 默认成员函数是程序猿不显示声明定义,编译器会中生成。 在程序编写中,我们也经常使用拷贝的方式来获取到对应的值,例如整形变量拷贝int a 0; i…...
2024年开发语言热度排名
随着技术的不断发展和变化,编程语言的热度也在不断演变。2024年即将到来,我们有必要回顾和展望当前和未来的开发语言市场。本文将基于多个因素,包括行业需求、社区支持、流行度以及新兴趋势,对2024年的开发语言热度进行排名和分析…...

CryptoMamba:利用状态空间模型实现精确的比特币价格预测
“CryptoMamba: Leveraging State Space Models for Accurate Bitcoin Price Prediction” 论文地址:https://arxiv.org/pdf/2501.01010 Github地址:https://github.com/MShahabSepehri/CryptoMamba 摘要 预测比特币价格由于市场的高波动性和复杂的非线…...

MQTTX客户端使用
一、MQTT服务器( emqx )搭建 (1) 下载服务器MQTT Broker 从https://www.emqx.com/zh/downloads/broker/5.3.0/emqx-5.3.0-windows-amd64.zip下载MQTT Broker。 这里我使用的windows系统,下载对应版本工具:emqx-5.3.0-windows-a…...
网管平台(进阶篇):路由器的管理实践
在当今数字化时代,路由器作为网络连接的核心设备,其管理对于确保网络的稳定、高效和安全至关重要。本文旨在深入探讨路由器管理的重要性、基本设置步骤、高级功能配置以及日常维护,帮助读者构建一个高效且安全的网络环境。 一、路由器管理的…...

基于微信小程序的智能停车场管理系统设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
【Vue】父组件向子组件传递参数;子组件向父组件触发自定义事件
父组件向子组件传递参数 方法一:props 在 Vue 中,父组件向子组件传递数据主要通过props来实现,以下是具体的步骤: 父组件中传递数据 在父组件中,当需要调用子组件 AddSampleDialog 时,通过 v-bind 或其…...

搜广推校招面经七
抖音推荐算法 一、广告系统中的数据流处理方法,怎么避免延迟回流问题 延迟回流问题是指,实时系统(如广告点击预估)中,历史数据未及时更新或发生延迟,导致系统的实时预测偏离实际情况。避免延迟回流的方法有…...

Leetcode 518. 零钱兑换 II 动态规划
原题链接:Leetcode 518. 零钱兑换 II 可参考官解:零钱兑换 II 和这个解答:[Java/Python3/C]动态规划:拆分零钱兑换子问题(嵌套循环的秘密)【图解】 此题需要仔细想象和Leetcode 377. 组合总和 Ⅳ 动态规划…...

【EI 会议征稿】第四届材料工程与应用力学国际学术会议(ICMEAAE 2025)
2025 4th International Conference on Materials Engineering and Applied Mechanics 重要信息 大会官网:www.icmeaae.com 大会时间:2025年3月7-9日 大会地点:中国西安 截稿时间:2025年1月24日23:59 接受/拒稿通知…...
集合的线程安全
在多线程环境中,Java 的集合框架(Collection Framework)面临着线程安全的问题。当多个线程同时访问同一个集合对象时,可能会导致数据不一致、丢失更新或程序崩溃等严重问题。因此,在并发编程中确保集合操作的安全性至关…...

《深入理解Mybatis原理》Mybatis中的缓存实现原理
一级缓存实现 什么是一级缓存? 为什么使用一级缓存? 每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。 在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句&…...
C# 数据拟合教程:使用 Math.NET Numerics 的简单实现
C# 数据拟合实战:使用 Math.NET Numerics 快速实现 引言 在科学计算、工程建模或数据分析中,数据拟合是一个非常重要的技术。无论是线性拟合还是非线性拟合,借助适当的工具都可以快速解决问题。本文将向您展示如何使用 C# 和强大的数值计算…...
C# 中对 Task 中的异常进行捕获
以下是在 C# 中对 Task 中的异常进行捕获的几种常见方法: 方法一:使用 try-catch 语句 你可以使用 try-catch 语句来捕获 Task 中的异常,尤其是当你使用 await 关键字等待任务完成时。 using System; using System.Threading.Tasks;class …...

Android车机DIY开发之软件篇(九)默认应用和服务修改
Android车机DIY开发之软件篇(九)默认应用和服务修改 Car默认应用位置 ~/packages/apps/Car 增加APP 1.增加 XXXX.app 和Android.mk 2. 修改~/build/make/target/product/handheld_system_ext.mk Android默认APK位置 ~/packages/apps 1.增加文件夹 app和mk文件 2.build/mak…...

SimpleFOC01|基于STM32F103+CubeMX,移植核心的common代码
导言 如上图所示,进入SimpleFOC官网,点击Github下载源代码。 如上图所示,找到仓库。 comom代码的移植后,simpleFOC的移植算是完成一大半。simpleFOC源码分为如下5个部分,其中communication是跟simpleFOC上位机通讯&a…...
web.xml常用配置
web.xml是Java Web应用程序的部署描述文件,它位于WEB-INF目录下。web.xml文件主要用于配置Servlet、Filter、Listener、MIME类型、欢迎页面等组件,以及一些Web应用的上下文参数。以下是一些常见的web.xml配置说明: Servlet配置: …...

代码随想录刷题day07|(数组篇)58.区间和
目录 一、数组理论基础 二、前缀和 三、相关算法题目 四、总结 五、待解决问题 一、数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 代码随想录 (programmercarl.com) 特点: 1.下标从0开始,内存中地址空间是连续的 2.查询快&…...

【Linux】进程结束和进程等待
进程的结束 退出码的认识 在我们学习C/C的时候我们通常在进行写main函数时,main函数主体写完后通常会进行写一条语句 " return 0 " ,这里的这条语句到底是什么意思呢?? 我们知道当在主函数中调用其他函数或者在其他函…...

可编辑精品PPT | 城投集团(行业)数字化解决方案
这个PPT详细介绍了城投集团的数字化转型解决方案。首先,它概述了数字化转型的背景,包括政策要求和行业趋势,并指出集团在信息化方面取得的阶段性成果及存在的不足。方案提出了数字化转型的总体规划,明确了总体目标、思路和推进策略…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...