类的6个默认成员函数

一、构造函数:

class date
{
public:
	void display()
	{
		cout << _year <<'-'<< _month <<'-'<< _day << endl;
	}

	void setdate(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	date a1,a2,a3;
	a1.setdate(1997 , 10 , 8);
	a2.setdate(1997 , 11 , 8);
	a3.setdate(1997 , 12 , 8);

	a1.display();
	a2.display();
	a3.display();
	return 0;
}

1.引言:我们分析一下上面的这段代码,对于date类定义的对象a1,我们需要调用setdate函数来设置内容,再用类定义一个对象,需要再次调用setdate函数,那么我们是不是可以再定义类的时候就顺便给对象设置内容,构造函数就可以实现这个功能。
2.定义:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象由编译器自动调用,保证每个类成员都有初始值,并在对象的生命周期内只能调用一次

class date
{
public:
	date()//无参构造函数
	{}
	
	date(int year,int month,int day)//带参构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	date a1;//不能加括号,否则就成了函数声明
	date a2(1997, 10, 8);
	getchar();
	return 0;
}

3.特性:
(1)函数名与类名相同;
(2)无返回值;
(3)对象实例化时,编译器自动调用对应的构造函数(首要任务就是初始化对象);
(4)构造函数可以重载;
(5)有初始化列表;(可以不用)
(6)如果类中没有显式定义构造函数,则编译器会自动生成一个无参的默认构造函数,如果用户已经显式定义,编译器将不再生成;
(7)无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个,注意:无参的构造函数、全缺省的构造函数、用户没有显式给出编译器默认生成的构造函数,均为默认成员函数;

class date
{
public:
	date()
	{
		_year = 1997;
		_month = 10;
		_day = 8;
	}

	date(int year = 1997, int month = 10,int day = 8)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	//date a1;编译时会报错,有多个默认构造函数,编译器不知道调用哪个
	return 0;
}

(8)构造函数不能用const修饰,因为可能在函数内部对成员进行赋值。
4.初始化列表:
(1)格式及说明:以一个冒号开始,接着以一个逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或者表达式。

class date
{
public:
	date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
}

int main()
{
	date a1(1997,10,8);
	return 0;
}

(2)每个成员变量只能再初始化列表出现一次(初始化只能初始化一次)
(3)类中包含以下成员,必须放在初始化列表位置进行初始化:

  • 引用成员变量
  • const成员变量
  • 类类型成员(该类没有默认构造函数)
class A
{
public:
	A(int a)
		:_a(a)
	{}

private:
	int _a;
};

class B
{
public:
	B(int a,int n, int m)
		:_aobj(a)
		, _n(n)
		, _m(m)
	{}
private:
	A _aobj;//类类型成员(该类没有缺省的构造函数,如果不初始化,编译器不知道传什么值)
	const int _n;//const类型成员变量
	int& _m; //引用成员变量
};

int main()
{
	B b(1, 2, 3);
	return 0;
}

(4)尽量使用初始化列表初始化,因为不管你是否使用初始化列表初始化,对于自定义类型成员变量,一定会先使用初始化列表初始化。
(5)成员变量在类中的声明次序就是再初始化列表中的初始化顺序,与其在初始化列表中的顺序无关。
5.引出explicit关键字:
(1)构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用。

class Date
{
public:
	explicit Date(int year)
		:_year(year)
	{}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2018);
	//d1 = 2019;//这条语句会报错
	return 0;
}

编译器给出的错误信息:没有与这些操作数匹配的 “=” 运算符,操作数类型为: Date = int
二、析构函数:
使用场景:如果一个类中管理了资源,(内存、文件、套接字)析构函数一定要由用户显式给出,否则会存在内存泄漏。
1.概念:
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
2.特性:
(1)析构函数名是在类名前加上字符~;
(2)无参数无返回值;
(3)一个类有且只有一个析构函数,如果用户未显式定义,则系统会自动生成默认的构造函数;
(4)对象生命周期结束时,编译系统自动调用析构函数;

class String
{
public:
	String(const char* str = "Jack")
	{
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}

	~String()
	{
		cout << "String()" << endl;
		free(_str);
	}
private:
	char *_str;
};

class Person
{
private:
	String _name;
	int _age;
};

int main()
{
	Person p;
	return 0;
}

说明:Person类中无析构函数,编译器需要自动生成~Person()。对于String类定义的对象,系统会自动调用用户已经显式给出析构函数(在_name生命周期结束之后)。
三、拷贝构造函数:
1.概念:
只有单个形参,该形参时对本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

class Date
{
public:
	Date(int year , int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)//若不加&符号,则会出现复制拷贝构造函数。
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(1997, 10, 8);
	Date d2(d1);
	return 0;
}

2.特性:
(1)拷贝构造函数是构造函数的重载形式,构造函数的性质拷贝构造函数均满足;
(2)参数传递必须使用类类型对象引用传参(必须加&),否则会出现复制构造函数(无限递归调用);
(3)若未显式定义,系统生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝;
四、赋值运算符重载:
1.运算符重载:
(1)概念引入:C++为了增强代码的可读性,引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型、函数名字以及参数列表,其返回值类型和参数列表与普通函数类似;
(2)函数名字:关键字operator加上要重载的运算符;
(3)函数原型:返回值类型 operator操作符(参数列表);
2.使用规则:
(1)不能通过连接其他符号来创建新的操作符:比如operator@;
(2)重载操作符必须有一个类类型或者枚举类型的操作数;
(3)用于内置类型的操作符,其含义不能改变,例如:内置的类型+,不能改变其含义;
(4)当重载函数作为类成员函数显式给出时,形参列表的第一个参数是this指针;

class Date
{
public:
	Date(int year = 1997, int month = 10, int day = 8)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator==(const Date& d2)//第一个参数为this指针
	{
		return _year == d2._year
			&&_month == d2._month
			&&_day == d2._day;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(1997, 10, 8);
	Date d2(1997, 10, 8);
	cout << (d1 == d2) << endl;
	return 0;
}

(5)五个运算符不能重载:

运算符作用
.*成员对象选择符
::作用域限定符
sizeof计算类型大小
?:条件操作符
.成员选择符

3.赋值运算符重载:
(1)先来看一段代码:

class Date
{
public:
	Date(int year = 1997, int month = 10, int day = 8)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	Date& operator=(const Date& d)//赋值操作符重载
	{
		if(this != &d)
		{
			_year = d._year;
       	 	_month = d._month;
        	_day = d._day;
		}
		
        return *this;//返回引用
	}

private:
	int _year;
	int _month;
	int _day;
};

(2)赋值运算符主要有以下几点:

  • 参数类型
  • 返回值
  • 检测是否自己给自己赋值
  • 返回*this
  • 一个类如果没有显式定义赋值运算符重载,编译器也会自动生成一个,完成对象按字节序的值拷贝(当然不是所有的问题都可以通过赋值重载函数来完成,有些问题需要深拷贝去解决,后续会更博)
class Date
{
public:
	Date(int year = 1997, int month = 10, int day = 8)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	Date(const Date& d)
		:_year(d._year)
		,_month(d._month)
		, _day(d._day)
	{}

	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		return *this;//返回引用
	}

	Date& operator++()//前置++
	{
		++_day;
		return *this;
	}

	const Date& operator++(int)//后置++,为与前置++形成重载
	{
		Date d(*this);//用当前对象拷贝构造d
		++_day;

		return d;//返回d
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(1, 2, 3);
	Date d3;
	d3 = d2++;
	d3 = ++d1;
	return 0;
}

五、取地址及const取地址操作符重载和const成员:
1.取地址操作符重载和const取地址操作符重载:

class Date
{
public:
	Date(int year = 1997, int month = 10, int day = 8)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	Date(const Date& d)
		:_year(d._year)
		,_month(d._month)
		,_day(d._day)
	{}

	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		return *this;//返回引用
	}

	Date* operator&()//取地址操作符重载,this指针为 Date* const this(指向不能改变)
	{
		return this;
	}

	const Date* operator&()const //const修饰的取地址操作符重载,this指针类型为
							     //const Date* const this(除了指向不能改变,指针指向的内容也不能改变)
	{
		return this;
	}							

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
		Date d1;
		const Date d2(1997, 10, 8);

		cout << &d1 << endl;//调用取地址操作符重载
		cout << &d2 << endl;//调用const修饰的取地址操作符重载
		return 0;
}

2.const修饰类的成员函数:
将const修饰的成员函数称为const成员函数,const修饰成员函数,实际修饰的是该成员函数的this指针,表明在该成员函数内部不能对类的成员进行修改。
3.特征:
(1)const修饰的成员函数可与普通函数形成重载;
(2)若要对成员变量进行修改,需要加mutable关键字;
(3)const对象可以调用const成员函数(普通函数可能修改对象中的成员变量);
(4)非const对象可以调用const成员函数(若有普通函数,优先考虑);
(5)const成员函数不可以调用其他非const成员函数;
(6)非const成员函数可以调用const成员函数;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值