C++ ------类和对象详解六大默认成员函数

这篇具有很好参考价值的文章主要介绍了C++ ------类和对象详解六大默认成员函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

类的6个默认成员函数

如果我们定义一个类,然后这个类中什么也没有。那么这里的类就什么也没有吗?其实不然,任何类在里面什么都不写时,编译器都会生成6个默认成员函数。

概念

用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
六个默认成员函数
C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

构造函数

我们来看一个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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

我们看到上述代码中我们显式的定义了一个构造函数,编译器会调用我们自己定义的构造函数,也就是说当我们自己定义了一个构造函数,编译器就不会生成无参的默认构造函数。
接下来我们了解一下默认构造函数:
我们再来看一段代码

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++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

我们可以看到,上述代码发生了编译错误,多个默认构造函数和对重载函数的调用不明确。这是因为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++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

我们可以看到运行结果中没有对内置类型初始化,那么默认生成的构造函数会做些什么呢,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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言
以上代码是调用了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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

析构函数

我们来看下面的代码:

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++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

我们可以看到上面的代码调用了显式定义的析构函数,析构函数的特征是什么和构造函数的特征相同吗,我们来简单讨论一下:

特征

从上述代码可知析构函数具有如下特点:

  • 函数名和类名相同且在函数名的前面加上字符~。
  • 没有返回值类型。
  • 一个类只能有一个析构函数。若为显示定义,系统会自动生成默认的析构函数,注意:析构函数不能重载。
  • 对象声明周期结束时,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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

  • 编译器默认生成的析构函数,对自定义类型成员调用它们的析构函数
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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

上面的程序运行结束后输出 ~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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

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

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

  • 若为显式定义,编译器会自动生成默认的拷贝构造函数,默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝或者值拷贝,但是我们在拷贝堆上的数据时需要把堆上的数据也拷贝过来,而不单单是拷贝一个堆上空间的一个指针。这不是真正的拷贝,也算是浅拷贝,上面的这种拷贝叫做深拷贝,这里的深拷贝会在以后详细讲解。我们这里只关心浅拷贝。
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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

该段程序运行崩溃,因为上述程序实例化了一个对象s1,入栈4个数据。然后再使用该对象来拷贝构造一个新的对象,当程序结束时,s2会调用析构函数对资源进行销毁,而s1也会调用析构函数对资源进行销毁,那么这就出现了free两次的情况,解决方案只能是进行深度拷贝,这里的深度拷贝会在以后的文章中讲解
C++ ------类和对象详解六大默认成员函数,C++,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;
		_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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

上面的代码在构造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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

为了解决上述问题,我们需要把运算符重载函数,写到类里面去,但是函数参数会少一个,这是由于成为成员函数就会增加一个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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

特性

  • 不能通过连接其他符号来创建新的操作符:比如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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

格式

  • 赋值运算符重载格式
  • 参数类型: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++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言

如果类中未涉及到资源管理,赋值运算符的重载是否实现都可以,一旦涉及到资源管理则必须要显式实现。

取地址及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;
}

C++ ------类和对象详解六大默认成员函数,C++,c++,开发语言文章来源地址https://www.toymoban.com/news/detail-618709.html

到了这里,关于C++ ------类和对象详解六大默认成员函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++类和对象】类有哪些默认成员函数呢?(下)

    ヾ(๑╹◡╹)ノ\\\" 人总要为过去的懒惰而付出代价 ヾ(๑╹◡╹)ノ\\\" 如果一个类中什么成员都没有,简称为空类。 空类中并不是什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 ​​​​​​​​ 默认成员函数:用户没有显式实现,编译器会生成

    2024年02月12日
    浏览(34)
  • 【C++】类和对象②(类的默认成员函数:构造函数 | 析构函数)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 类的6个默认成员函数 构造函数 概念 构造函数的特性及用法 析构函数 概念 析构函数的特性及用法 结语 本篇主要内容:类的6个默认成员函数中的 构造函数 和 析构函数 进入到类和对象内容的第二节,上篇博客中介绍了

    2024年04月16日
    浏览(40)
  • 【C++初阶】第三站:类和对象(中) -- 类的6个默认成员函数

    目录 前言 类的6个默认成员函数 构造函数 概念 特性 析构函数  概念 特性 拷贝构造函数 概念 特征 赋值运算符重载 运算符重载 赋值运算符重载 const成员 const修饰类成员函数 取地址及const取地址操作符重载 本章总结:         有时候我们写好了一个栈,头脑中第一件事

    2024年02月20日
    浏览(27)
  • 【C++】:类和对象(中)之类的默认成员函数——构造函数and析构函数

    如果一个类中什么成员都没有,简称为空类 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数 由于编译器的优化 我们未给_a赋值 这里是不会报

    2024年02月08日
    浏览(32)
  • 【C++】类和对象③(类的默认成员函数:拷贝构造函数 | 赋值运算符重载)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 拷贝构造函数 概念 拷贝构造函数的特性及用法 赋值运算符重载 运算符重载 赋值运算符重载 结语 本篇主要内容:类的6个默认成员函数中的 拷贝构造函数 和 赋值运算符重载 在上篇文章中我们讲到了类的默认成员函数的

    2024年04月17日
    浏览(34)
  • 【C++精华铺】5.C++类和对象(中)类的六个默认成员函数

    目录 1. 六个默认成员函数 2. 构造函数 2.1 概念 2.2 默认构造 2.2.1 系统生成的默认构造 2.2.2 自定义默认构造函数  2.3 构造函数的重载 3. 析构函数 3.1 概念  3.2 系统生成的析构函数  3.3 自定义析构函数 4. 拷贝构造 4.1 概念  4.2 默认生成的拷贝构造(浅拷贝)  4.3 自定义拷贝构

    2024年02月13日
    浏览(57)
  • 【C++深入浅出】类和对象中篇(六种默认成员函数、运算符重载)

    目录 一. 前言  二. 默认成员函数 三. 构造函数 3.1 概念 3.2 特性 四. 析构函数 4.1 概念 4.2 特性 五. 拷贝构造函数 5.1 概念 5.2 特性 六. 运算符重载 6.1 引入 6.2 概念 6.3 注意事项 6.4 重载示例 6.5 赋值运算符重载 6.6 前置++和后置++运算符重载 七. const成员函数 7.1 问题引入 7.2 定义

    2024年02月09日
    浏览(31)
  • 【C++练级之路】【Lv.3】类和对象(中)(没掌握类的6个默认成员函数,那你根本就没学过C++!)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 在C++的学习中,类和对象章节的学习尤为重要,犹如坚固的地基,基础不牢,地动山摇;而默认成员函数的学习,在类和对象的学习里最为重要。所以要 学好C++,学好默认

    2024年02月04日
    浏览(32)
  • 【C++】类和对象④(类的默认成员函数:取地址及const取地址重载 | 再谈构造函数:初始化列表,隐式类型转换,缺省值)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 取地址及const取地址操作符重载 再谈构造函数 初始化列表 隐式类型转换 explicit 成员变量缺省值 结语 本篇主要内容:类的六个默认成员函数中的 取地址 及 const取地址重载 , 构造函数 初始化列表 , 隐式类型转换

    2024年04月26日
    浏览(31)
  • 【C++干货基地】六大默认成员函数: This指针 | 构造函数 | 析构函数

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 : 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活!   哈喽各位铁汁们好啊,我是博主鸽芷咕《C++干货基地》是由我的襄阳家乡零食基地有感而发,不知道各位的城市有没有这种实惠又全面的零食基地呢?C++ 本身作

    2024年03月11日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包