文章目录
- 类的6个默认成员函数
 - 概念
 
- 构造函数
 - 概念
 - 特点
 
- 析构函数
 - 概念
 - 特征
 
- 拷贝构造函数
 - 概念
 - 特点
 
- 赋值运算符重载
 - 运算符重载的概念
 - 特性
 - 赋值运算符重载
 - 格式
 - 特性
 
- 取地址及const取地址操作符重载
 
类的6个默认成员函数
如果我们定义一个类,然后这个类中什么也没有。那么这里的类就什么也没有吗?其实不然,任何类在里面什么都不写时,编译器都会生成6个默认成员函数。
概念
用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
六个默认成员函数
构造函数
我们来看一个Date类
#include <iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Init(2023, 7, 23);
	d1.Print();
	Date d2;
	d2.Init(2023, 7, 24);
	d2.Print();
	return 0;
}
 
观察上述代码,我们需要显式的调用Init函数,这个函数可以给私有的成员变量赋值,但是如果每次都使用这个方法设置信息,就显得有点麻烦了,这里我们引出了构造函数的概念:
概念
构造函数时特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。
特点
函数名和类名相同,没有返回值,可以发生重载,对象实例化时编译器自动调用构造函数。除此之外我们还需要了解一下的特点,我们看一个代码:
class Date
{
public:
	
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//实例化对象
	Date d;
	d.Print();
	return 0;
}
 

我们看到上述代码中我们显式的定义了一个构造函数,编译器会调用我们自己定义的构造函数,也就是说当我们自己定义了一个构造函数,编译器就不会生成无参的默认构造函数。
接下来我们了解一下默认构造函数:
我们再来看一段代码
class Date
{
public:
	//构造函数 任务初始化对象
	//无参构造函数
	//编译器会自动调用构造函数。
	//需要注意,我们在显式的定义构造函数时,编译器不会生成默认的构造函数。
	//编译器生成的默认构造函数不会对内置类型进行初始化,生成的构造函数是
	//无参的构造函数。
	//默认构造函数包括一下三类:我们自己写的无参构造函数,还有我们不写时编译器默认生成的无参构造函数。
	//全缺省构造函数,这三个构造函数只能在类中出现一个,否则就会出现调用的二义性。
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//实例化对象
	Date d;
	d.Print();
	return 0;
}
 

我们可以看到,上述代码发生了编译错误,多个默认构造函数和对重载函数的调用不明确。这是因为C++规定默认构造函数分为编译器自动生成的构造函数,自己定义的构造函数和全缺省构造函数,这三个构造函数只能在类中出现一个,所以我们在定义构造函数时,不能将两个默认构造函数同时写在同一个类中。
我们再来思考一个问题,编译器自动生成的构造函数,会对内置类型进行初始化吗?答案是不会,我们通过以下代码来验证这一点
class Date
{
public:
	//需要注意,我们在显式的定义构造函数时,编译器不会生成默认的构造函数。
	//编译器生成的默认构造函数不会对内置类型进行初始化,生成的构造函数是
	//无参的构造函数。
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//实例化对象
	Date d;
	d.Print();
	return 0;
}
 

我们可以看到运行结果中没有对内置类型初始化,那么默认生成的构造函数会做些什么呢,C++把类型分为内置类型和自定义类型,内置类型就是语言提供的数据类型,如int,char等,自定义类型就是我们使用class/struct关键字自己定义的类型,编译器默认生成的构造函数会对自定义类型成员_t 调用它的默认构造函数。
class Time
{
public:
	//自己定义的默认构造函数
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	//基本类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	Time _t;
};
int main()
{
	//实例化对象
	Date d;
	return 0;
}
 

 以上代码是调用了Time的构造函数的。那么对于内置类型成员不初始化的缺陷,C++11又定义:内置类型成员变量在类中声明时可以给默认值。
class Time
{
public:
	//自己定义的默认构造函数
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	//基本类型
	//缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
	//自定义类型
	Time _t;
};
int main()
{
	//实例化对象
	Date d;
	d.Print();
	return 0;
}
 

析构函数
我们来看下面的代码:
typedef int DateType;
class Stack
{
public:
	//默认构造函数
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_a = (DateType*)malloc(sizeof(DateType) * capacity);
		if (NULL == _a)
		{
			perror("malloc failed!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	void Push(DateType data)
	{
		//扩容
		_a[_top] = data;
		_top++;
	}
	//其他方法……
	void Destory()
	{
		free(_a);
		_a = NULL;
		_top = _capacity = 0;
	}
private:
	size_t _capacity;
	size_t _top;
	DateType* _a;
};
int main()
{
	Stack st;
	st.Push(1);
	st.Push(2);
	st.Push(3);
	st.Destory();
	return 0;
}
 
上面的代码我们会发现,在最后我们写了一个函数叫做Destory,这里的这个函数的作用是销毁资源,需要我们显式调用,有点麻烦,那么我们怎么样可以让系统自动调用呢?我们就引出了析构函数的概念。
概念
析构函数:析构函数和构造函数的功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。看以下代码:
typedef int DateType;
class Stack
{
public:
	//默认构造函数
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_a = (DateType*)malloc(sizeof(DateType) * capacity);
		if (NULL == _a)
		{
			perror("malloc failed!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	void Push(DateType data)
	{
		//扩容
		_a[_top] = data;
		_top++;
	}
	//其他方法……
	//析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = NULL;
		_top = _capacity = 0;
	}
	/*void Destory()
	{
		free(_a);
		_a = NULL;
		_top = _capacity = 0;
	}*/
private:
	size_t _capacity;
	size_t _top;
	DateType* _a;
};
int main()
{
	Stack st;
	st.Push(1);
	st.Push(2);
	st.Push(3);
	//st.Destory();
	return 0;
}
 

我们可以看到上面的代码调用了显式定义的析构函数,析构函数的特征是什么和构造函数的特征相同吗,我们来简单讨论一下:
特征
从上述代码可知析构函数具有如下特点:
- 函数名和类名相同且在函数名的前面加上字符~。
 - 没有返回值类型。
 - 一个类只能有一个析构函数。若为显示定义,系统会自动生成默认的析构函数,注意:析构函数不能重载。
 - 对象声明周期结束时,C++编译系统自动调用析构函数。编译器默认生成的析构函数不会free堆上的空间。
 
typedef int DateType;
class Stack
{
public:
	//默认构造函数
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_a = (DateType*)malloc(sizeof(DateType) * capacity);
		if (NULL == _a)
		{
			perror("malloc failed!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	void Push(DateType data)
	{
		//扩容
		_a[_top] = data;
		_top++;
	}
	//其他方法……
	//析构函数
	//~Stack()
	//{
	//	cout << "~Stack()" << endl;
	//	free(_a);
	//	_a = NULL;
	//	_top = _capacity = 0;
	//}
	/*void Destory()
	{
		free(_a);
		_a = NULL;
		_top = _capacity = 0;
	}*/
private:
	size_t _capacity;
	size_t _top;
	DateType* _a;
};
int main()
{
	Stack st;
	//st.Destory();
	return 0;
}
 

- 编译器默认生成的析构函数,对自定义类型成员调用它们的析构函数
 
class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	//基本类型
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	//自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}
 

上面的程序运行结束后输出 ~Time()
在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month,
_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数。
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。
- 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
 
拷贝构造函数
我们来看下面的代码引出拷贝构造函数
class Date
{
public:
	//全缺省默认构造
	Date(int year = 2023, int month = 7, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	//基本类型
	int _year = 1970;
	int _month = 1;
	int _day = 1;
};
int main()
{
	Date d;
	//我们如何用已存在的对象来构造一个和它相同的对象呢?显然,
	//我们使用拷贝构造可以完成这个任务。
	//调用拷贝构造
	Date d1(d);
	return 0;
}
 
上述代码我们知道想要利用已存在的对象,实例化一个和它相同的对象我们需要调用拷贝构造函数。
概念
拷贝构造函数:只有单个形参,该形参是对类类型对象的应用一般常用const修饰,目的是为了不能对原有的对象进行修改,在用已存在的类类型对象创建新对象时由编译器自动调用。
特点
- 拷贝构造函数时构造函数的一个重载形式。
 - 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用拷贝构造。我们来看下面的代码;
 
class Date
{
public:
	//全缺省默认构造
	Date(int year = 2023, int month = 7, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(Date d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	//基本类型
	int _year = 1970;
	int _month = 1;
	int _day = 1;
};
int main()
{
	Date d;
	//我们如何用已存在的对象来构造一个和它相同的对象呢?显然,
	//我们使用拷贝构造可以完成这个任务。
	//调用拷贝构造
	Date d1(d);
	return 0;
}
 

上面的拷贝构造函数参数为传值调用,这会引发编译器报错,这是因为在传值传参时会进行拷贝构造,而调用拷贝构造还是需要传参,这就会导致函数一直传参,传值传参就会拷贝构造。那么就造成了无穷递归调用

- 若为显式定义,编译器会自动生成默认的拷贝构造函数,默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝或者值拷贝,但是我们在拷贝堆上的数据时需要把堆上的数据也拷贝过来,而不单单是拷贝一个堆上空间的一个指针。这不是真正的拷贝,也算是浅拷贝,上面的这种拷贝叫做深拷贝,这里的深拷贝会在以后详细讲解。我们这里只关心浅拷贝。
 
class Date
{
public:
	//全缺省默认构造
	Date(int year = 2023, int month = 7, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//基本类型
	int _year = 1970;
	int _month = 1;
	int _day = 1;
};
int main()
{
	Date d;
	//我们如何用已存在的对象来构造一个和它相同的对象呢?显然,
	//我们使用拷贝构造可以完成这个任务。
	//调用拷贝构造,我们没有显式定义拷贝构造函数,编译器会默认生成拷贝构造。
	Date d1(d);
	return 0;
}
 
这里还是一样,对于自定义类型,编译器默认生成的拷贝构造函数会去调用自定义类型的拷贝构造函数和普通的构造函数相同。
- 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,我们对于没有涉及到资源申请的类,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝,我们需要深拷贝。
 
我们来看下面的代码:
typedef int DataType;
class Stack
{
public:
	//全缺省默认构造函数
	Stack(size_t capacity = 10)
	{
		_a = (DataType*)malloc(capacity * sizeof(int));
		if (NULL == _a)
		{
			perror("malloc failed!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	void Push(const DataType x)
	{
		//扩容
		_a[_top] = x;
		_top++;
	}
	//析构函数
	~Stack()
	{
		if (_a)
		{
			free(_a);
			_a = NULL;
			_capacity = _top = 0;
		}
	}
private:
	DataType* _a;
	size_t _top;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}
 

该段程序运行崩溃,因为上述程序实例化了一个对象s1,入栈4个数据。然后再使用该对象来拷贝构造一个新的对象,当程序结束时,s2会调用析构函数对资源进行销毁,而s1也会调用析构函数对资源进行销毁,那么这就出现了free两次的情况,解决方案只能是进行深度拷贝,这里的深度拷贝会在以后的文章中讲解
- 拷贝构造的应用场景
 
使用已存在对象创建新对象,上面的代码就是这种场景
函数参数类型为类类型
函数返回值类型为类类型对象
看一下下面的代码
class Date
{
public:
	//不是默认构造函数
	Date(int year, int minute, int day)
	{
		cout << "Date(int year, int minute, int day)" << this << endl;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
Date Test(Date d)
{
	Date temp(d);
	return temp;
}
int main()
{
	//调用构造函数
	Date d1(2023, 7, 30);
	Test(d1);
	return 0;
}
 

上面的代码在构造d1对象时使用了构造函数,在给类类型形参传参的时候调用了一次拷贝构造,在用d构造temp对象时使用了一次拷贝构造,在返回temp对象时调用了一次拷贝构造,程序运行结束后销毁Test中的temp对象使用了一次析构函数,销毁Test函数的参数d使用了析构函数,销毁Test函数返回时创建的临时对象使用了一次析构函数,销毁main函数中的d1使用了一次析构函数。为了提高效率我们在做类类型传参和类类型作为返回值的时候,尽量使用引用。
赋值运算符重载
在这一节我们需要先了解运算符重载的概念和实现
运算符重载的概念
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回类型,函数名字以及参数列表,其返回值类型和参数列表与普通的函数类似,下面是一个运算符重载的例子:
class Date
{
public:
	//不是默认构造函数
	Date(int year, int minute, int day)
	{
		cout << "Date(int year, int minute, int day)" << this << endl;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
public:
	int _year;
	int _month;
	int _day;
};
//恒等运算符重载
bool operator==(const Date& d1, const Date& d2)
{
	//这里访问不了的类的私有成员,我们需要把类的私有成员变成公有的,但是这就不符合我们的封装思想了
	//因为我们这里只是讲解运算符的重载就先不要管这个东西了,下面我们会介绍怎么样才能避免这种情况
	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{
	Date d1(2023,7,30);
	Date d2(2023,7,30);
	//可以隐式调用也可以显式调用
	cout << (d1 == d2) << endl;
	//显式调用
	cout << operator==(d1, d2) << endl;
}
 

为了解决上述问题,我们需要把运算符重载函数,写到类里面去,但是函数参数会少一个,这是由于成为成员函数就会增加一个this指针,而运算符重载函数的参数有几个是由操作符的操作数决定的。也可以用友元函数进行解决,这个我们后面会说。
class Date
{
public:
	//不是默认构造函数
	Date(int year, int minute, int day)
	{
		cout << "Date(int year, int minute, int day)" << this << endl;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//运算符重载
	bool operator==(const Date& d1)
	{
		return (*this)._year == d1._year && (*this)._month == d1._month && (*this)._day == d1._day;
	}
public:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023,7,30);
	Date d2(2023,7,30);
	//可以隐式调用也可以显式调用
	cout << (d1 == d2) << endl;
	//显式调用
	cout <<d1.operator==(d2) << endl;
}
 

特性
- 不能通过连接其他符号来创建新的操作符:比如operator@
 - 重载操作符必须有一个类类型参数
 - 用于内置类型的运算符,其含义不能改变,例如:内置类型的+,不能改变其含义。
 - 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
 .* :: sizeof ?: .不能被重载。
赋值运算符重载
class Date
{
public:
	//不是默认构造函数
	Date(int year, int minute, int day)
	{
		cout << "Date(int year, int minute, int day)" << this << endl;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//运算符重载
	bool operator==(const Date& d1)
	{
		return _year == d1._year && 
			_month == d1._month && 
			_day == d1._day;
	}
	//赋值运算符重载
	//赋值运算符不能在类外定义,因为我们没有在类中显式定义赋值运算符重载函数
	//编译器会自动生成,这就和类外定义的赋值运算符重载函数冲突了。另外定义成全局函数
	//就没有this指针这一说法了,就必须写两个参数
	Date& operator=(const Date& d1)
	{
		//我们需要确定不是两个相同的对象赋值
		if (this != &d1)
		{
			cout << "Date& operator=(const Date& d1)" << endl;
			_year = d1._year;
			_month = d1._month;
			_day = d1._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//Date d1(2023, 7, 30);
	//Date d2(2023, 7, 30);
	可以隐式调用也可以显式调用
	//cout << (d1 == d2) << endl;
	显式调用
	//cout << d1.operator==(d2) << endl;
	//两个对象赋值操作就要用到赋值运算符重载
	Date d3(1,1,1);
	Date d4(1,1,1);
	d3 = d4;
	//注意写成这样为调用拷贝构造
	Date d5 = d3;
}
 

格式
- 赋值运算符重载格式
 - 参数类型:const T&,传递引用可以提高传参效率
 - 返回值类型:T&: 返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
 - 检测是否自己给自己赋值
 - 返回* this:要符合连续赋值的含义。 a = b = c
 
特性
赋值运算符只能重载成类的成员函数不能重载成全局函数,因为赋值运算符重载函数是一个默认成员函数,所以编译器会默认生成赋值重载函数,跟构造函数和拷贝函数的行为一样。Date和MyQueue不需要我们自己实现赋值重载,stack需要我们自己实现,因为默认生成的是浅拷贝。
class Date
{
	friend Date& operator=(const Date& d1, const Date& d2);
public:
	//不是默认构造函数
	Date(int year, int minute, int day)
	{
		cout << "Date(int year, int minute, int day)" << this << endl;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//运算符重载
	bool operator==(const Date& d1)
	{
		return _year == d1._year &&
			_month == d1._month &&
			_day == d1._day;
	}
	//赋值运算符重载
	//赋值运算符不能在类外定义,因为我们没有在类中显式定义赋值运算符重载函数
	//编译器会自动生成,这就和类外定义的赋值运算符重载函数冲突了。另外定义成全局函数
	//就没有this指针这一说法了,就必须写两个参数
private:
	int _year;
	int _month;
	int _day;
};
Date& operator=(const Date& d1,const Date& d2)
{
	//我们需要确定不是两个相同的对象赋值
	if (&d2 != &d1)
	{
		cout << "Date& operator=(const Date& d1)" << endl;
		d1._year = d2._year;
		d1._month = d2._month;
		d1._day = d2._day;
	}
	return d1;
}
int main()
{
	//Date d1(2023, 7, 30);
	//Date d2(2023, 7, 30);
	可以隐式调用也可以显式调用
	//cout << (d1 == d2) << endl;
	显式调用
	//cout << d1.operator==(d2) << endl;
	//两个对象赋值操作就要用到赋值运算符重载
	Date d3(1, 1, 1);
	Date d4(1, 1, 1);
	d3 = d4;
	//注意写成这样为调用拷贝构造
	Date d5 = d3;
}
 

C++ primer中有一句话说:我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。
用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值得方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
class Time
{
public:
	//构造函数
	Time(int hour = 0, int minute = 0, int second = 0)
	{
		cout << "Date(int hour = 0, int minute = 0, int second = 0)" << endl;
	}
	Time& operator=(const Time& t)
	{
		//判断是不是同一个对象
		if (this != &t)
		{
			cout << "Time& operator=(const Time& t)" << endl;
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}
		return *this;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	//不是默认构造函数
	Date(int year, int month, int day)
	{
		cout << "Date(int year, int month, int day)" << this << endl;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	
private:
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	Time _t;
};
int main()
{
	Date d3(1, 1, 1);
	Date d4(1, 1, 1);
	d3 = d4;
	//注意写成这样为调用拷贝构造
	Date d5 = d3;
}
 

如果类中未涉及到资源管理,赋值运算符的重载是否实现都可以,一旦涉及到资源管理则必须要显式实现。
取地址及const取地址操作符重载
这里就不做太多的介绍,这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容。
class Date
{
public:
	Date* operator&()
	{
		return this;
	}
	const Date* operator&()const //这里的const代表了不能修改对象
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	cout << &d1 << endl;
	Date* p1 = &d1;
	cout << p1 << endl;
}
 












![#P0999. [NOIP2008普及组] 排座椅](https://img-blog.csdnimg.cn/img_convert/ebf4d06eb8a9ce27d7a8e123902b6271.png)









