hello~ 很高兴见到大家! 这次带来的是C++中关于类和对象这部分的一些知识点,如果对你有所帮助的话,可否留下你的三连呢? 个 人 主 页: 默|笙

续接上文构造函数和析构函数。这次带来的是拷贝构造函数,运算符重载与赋值重载函数以及取地址与const取地址运算符重载。
拷贝构造函数是C++中一种特殊的构造函数,用于用一个已存在的对象来初始化同类型的新对象,默认拷贝构造函数是c++里默认六大函数之一。
#include<iostream>
using namespace std;
class Date
{
public:
//实质:Date(const Date* this)
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
//实质:Date(const Date* this, Date& d)
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
//实质:Date d2(&d2, d1)
Date d2(d1);
return 0;
}接下来我们来解释一下 2 中传值情况下为什么在语法上会出现无穷递归调用:
传值必然发生拷贝,在c++里: 1.内置类型会直接进行拷贝。 2.对自定义类型对象进行拷贝行为会去调用拷贝构造函数。

所以,我们需要传引用,d 就是 d1 的别名,我们可以直接操作原对象,就不用去拷贝。这样也能够节省空间。 除了引用,其实指针也能够解决这个问题,不过,c++规定,传引用才是拷贝构造函数。
浅拷贝:仅复制对象的成员值(包括指针的值),新旧对象共享同一块内存。 深拷贝:复制对象成员值及其指向的资源,为新对象分配独立的内存空间。
对于浅拷贝,就像 Date 类: 它的成员变量都是内置类型,而且没有指向什么资源,由编译器自动生成的拷贝构造函数就可以完成对应的拷贝工作。
class Date
{
public:
//实质:Date(const Date* this)
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
//实质:Date d2(&d2, d1)
Date d2(d1);
return 0;
}

对于深拷贝,就像 Stack类: 成员变量虽然都是内置类型,但是有指向的资源,编译器自动生成的拷贝构造函数无法达到要求,我们需要显式实现拷贝构造函数,原因在之后会讲到。 还有 MyQueue 类: 成员变量都是自定义类型,编译器自动生成的拷贝构造函数会去调用成员变量它们所对应的拷贝构造函数,它不需要我们显式实现拷贝构造函数,但它也是深拷贝。
typedef int SLDataType;
class Stack
{
public:
Stack(int n = 4)
{
_arr = (SLDataType*)malloc(sizeof(SLDataType) * n);
if (_arr == nullptr)
{
perror("malloc fail");
exit(1);
}
_top = 0;
_capacity = n;
}
Stack(const Stack& st)
{
_arr = (SLDataType*)malloc(sizeof(SLDataType) * st._capacity);
if (_arr == nullptr)
{
perror("malloc fail");
exit(1);
}
_top = st._top;
_capacity = st._capacity;
}
~Stack()
{
free(_arr);
_arr = nullptr;
_top = _capacity = 0;
}
private:
SLDataType* _arr;
int _top;
int _capacity;
};
class MyQueue
{
public:
private:
Stack st1;
Stack st2;
};
int main()
{
Stack st1;
//调用拷贝构造函数还可以写成这种格式,清晰且直观
Stack st2 = st1;
return 0;
}拷贝之前:

拷贝之后:


一般来说,需要显式实现析构函数的也需要显式实现拷贝构造函数。
引入:内置类型定义的变量之间可以用运算符实现计算,但是自定义类型对象之间则不能,为了能让自定义类型对象之间也够实现内置类型类似的运算符操作,引出了运算符重载。
运算符重载 是 C++ 中允许为自定义类型重新定义现有运算符的行为的机制,通过编写特殊形式的成员函数或全局函数(如 operator+),使运算符能够根据操作数的类型执行用户定义的逻辑。其核心目标是让自定义类型的操作语法与内置类型一致,提升代码的直观性和可维护性。
1.对于二元运算符,它左侧的对象会传给第一个参数,而右侧的对象会传给第二个参数。 2.若是作为类的成员函数,那么它的第一个运算对象默认会传给 this 指针,因此参数会比运算对象会少一个。
class Date
{
public:
Date(int year = 2025, int month = 5, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
//由于定义在类里面,参数比运算对象少一个
//实质: bool operator==(Date* const this, const Date d2)
bool operator==(const Date d2)
{
return _year == d2._year &&
_month == d2._month &&
_day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
// '<<' 的优先级是要比 '=='高的,要加括号
cout << (d1 == d2) << endl;
return 0;
}重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,我们应该如何区分? C++规定,后置++重载时,增加⼀个 int 形参,跟前置++构成函数重载,⽅便区分。 即:


重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了对象<<cout,不符合使⽤习惯和可读性。
重载为全局函数把 ostream/istream 放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。 即:
//将类里的私有成员变量公有化
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}在 C++ 中,赋值运算符重载用于自定义类对象之间的赋值行为(如 obj1 = obj2;)。其核心目标是实现深拷贝,避免默认浅拷贝导致的资源冲突(如重复释放内存)。系统自动生成的默认赋值运算符是c++里六大默认成员函数之一。
一般来说,需要显式实现析构函数的也需要显式实现赋值重载函数。
class Date
{
public:
// friend 是友元函数,可以避免注释掉 private
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
Date(int year = 2025, int month = 5, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
Date& operator=(const Date d2)
{
if (this != &d2)
{
_year = d2._year;
_month = d2._month;
_day = d2._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out,const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日";
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
int main()
{
Date d1;
Date d2(2025, 5, 12);
cout << "赋值之前:" << d1 << endl;
cout << "赋值之后:" << (d1 = d2) << endl;
return 0;
}int main()
{
Date d1;
//拷贝构造函数的隐式调用,约等于 Date d2(d1);
Date d2 = d1;
//赋值重载函数的调用
d2 = d1;
return 0;
}区分它们的方式:
class Date
{
public:
Date(int year = 2025, int month = 5, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
//即为 void Print(const Date* const this)
void Print()const
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
int _year;
int _month;
int _day;
};我们知道:权限不能被放大,但能够缩小。 就有:
class Date
{
public:
Date(int year = 2025, int month = 5, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
Date* operator&()
{
return this;
//可以把返回值换成 nullptr,防止其他人取到对象地址
//或者胡乱返回一个错误地址,那很坏了
}
const Date* operator&()const
{
return this;
//同上
}
private:
int _year;
int _month;
int _day;
};今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~ 如果可以, 那就让我们共同努力, 一起走下去!