类的默认成员函数

一、构造函数

1、概念:是一个特殊成员函数,名字与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内只且只调用一次,以保证数据成员都有一个合适的初始值。

2、特征

(1)函数名与类名相同

(2)没有返回值

(3)对象构造(对象实例化)时系统自动调用对应的构造函数

(4)构造函数可以重载

(5)构造函数可以在类中定义,也可以在类外定义

(6)如果类定义中没有给出构造函数,则C++编译器会自动产生一个缺省的构造函数;但只要我们定义了一个构造函数,系统就不会生成缺省的构造函数

(7)无参的构造函数和全缺省的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个

a、无参的构造函数

Date()
{}

b、带参的构造函数

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

c、缺省参数的构造函数

Date(int year = 2018, int month = 3, int day = 15)
{
	_year = year;
	_month = month;
	_day = day;
}

d、半缺省的构造函数

Date(int year, int month = 3)
{
	_year = year;
	_month = month;
	_day = 15;
}


3、作用

(1)构建对象

(2)初始化对象

(3)类型转换

二、拷贝构造函数

1、概念:创建对象时使用已经存在的同类对象来进行初始化,这时所用的函数叫拷贝构造函数。拷贝构造函数是特殊的构造函数。

2、特征

(1)拷贝构造函数其实就是构造函数的重载

(2)拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用

(3)如果没有显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝类成员进行初始化

3、使用场景

(1)对象实例化对象

Date d1(2018, 3, 15);
Date d2(d1);

(2)传值方式作为函数的参数

void Test(const Date date)
{}

(3)传值方式作为函数的返回值

Date Test()
{
	Date date;
	return date;
}

4、实现

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

三、析构函数

1、概念:与构造函数的功能相反,在对象被销毁时由编译器自动调用,完成类的一些资源清理和汕尾工作。

2、特征

(1)析构函数名在类名(即构造函数名)加上字符~

(2)析构函数无参数、无返回值

(3)一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数

(4)对象生命周期结束时,C++编译系统会自动调用析构函数

(5)析构函数体内并不是删除对象,而是做一些清理工作

面试题

怎样理解这里的清理工作?

答:清理的是动态开辟在堆上的内存。

运算符重载

1、功能:增加程序的可读性

2、特征

(1)operator+合法的运算符     构成函数名(重载运算符的函数名:operator<)

(2)重载运算符以后,不能改变运算符的优先级、结合性、操作数个数

面试题

5个C++不能重载的运算符:.*     ::     sizeof     ?:     .

四、赋值运算符重载     

1、赋值运算符重载与拷贝构造函数比较

(1)拷贝构造函数是创建的对象,使用一个已经存在的对象来初始化这个准备创建的对象

(2)赋值运算符的重载是对一个已经存在的对象进行拷贝赋值

2、实现

Date& operator=(const Date& date)
{
	if (this != &date)
	{
		_year = date._year;
		_month = date._month;
		_day = date._day;
	}
	return *this;
}

问题

1、为什么oerator=要用Date&类型的返回值,而不是void?

答:因为Date& 返回的是已经拷贝好了的当前对象,而void返回的是当前对象的地址。我们想要的是拷贝好了的值。

2、if条件判断的是什么?

答:if条件判断的是当前对象和传过来的date是否相等。


3、重载运算符的调用及编译器的处理


深入探索构造函数

类成员变量的初始化方式:

(1)初始化列表

(2)构造函数体内进行赋值

初始化列表以一个冒号开始,接着一个逗号分隔数据列表,每个数据成员都放在一个括号内进行初始化。

#include<iostream>
using namespace std;

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
	Time(const Time& t)
	{
		cout << "Time(const Time& t)" << endl;
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
	}
private:
	int _hour;//小时
	int _minute;//分钟
	int _second;//秒
};
class Date
{
public:
	Date(int year , int month, int day,const Time& t)
	{
		cout << "Date()-已初始化列表" << endl;
		_year = year;
		_month = month;
		_day = day;
		_t = t;
	}
	Date(int year, int month, int day, const Time& t)
		:_year(year)
		,_month(month)
		,_day(day)
		,_t (t)
	{
		cout << "Date()-未初始化列表" << endl;
	}
private:
	int _year;//年
	int _month;//月
	int _day;//日
	Time _t;
};
int main()
{
	Time t1;
	Date d1(2018, 3, 15, t1);
	system("pause");
	return 0;
}


所以尽量使用初始化列表进行初始化,因为它更高效

必须放在初始化列表里的成员变量

(1)常量成员变量(const数据成员)

(2)引用类型成员变量

(3)类类型成员变量(没有缺省构造函数的类成员变量)自定义类型的成员变量

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_day(day)
		,_year(year)
		,_month(month)
	{
		cout << "Date()-未初始化列表" << endl;
	}
	void Display()
	{
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl;
	}
private:
	int _year;//年
	int _month;//月
	int _day;//日
};
int main()
{
	Date d1(2018, 3, 15);
	d1.Display();
	system("pause");
	return 0;
}

成员变量按声明顺序依次初始化,而不是初始化列表出现的顺序

尽量将成员变量声明的顺序和初始化列表中出现的顺序写成一致的

五、const修饰类成员

在成员函数后面加const,const修饰this指针所指向的对象,保证了const成员函数的对象在成员函数内不会被改变


class Date
{
public:
	Date(int year, int month, int day)
		:_day(day)
		,_year(year)
		,_month(month)
	{
		cout << "Date()-未初始化列表" << endl;
	}
	void Display()
	{
		cout << "Display()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl;
	}
	void Display()const
	{
		cout << "Display() const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl;
	}
private:
	int _year;//年
	int _month;//月
	int _day;//日
};
int main()
{
	Date d1(2018, 3, 15);
	d1.Display();
	const Date d2(2018, 3, 16);
	d2.Display();
	system("pause");
	return 0;
}

const使用场景:

(1)const修饰形参,一般和引用同时使用

(2)const修饰返回值

(3)const修饰类数据成员,必须在构造函数的初始化列表中使用

(4)const修饰类成员函数,实际修饰隐含的this指针,表示在类中不可以对类的任何成员进行修改

(5)在const修饰的成员函数中要对类的某个数据成员进行修改,该数据成员定义声明是必须加mutable关键字

面试题:

(1)const对象可以调用const成员函数

                      不可以调用非const成员函数

(2)非const对象可以调用非const成员函数,也可以调用const成员函数

(3)const成员函数内可以调用其他的const成员函数

                                不可以调用其他非const成员函数

(4)非const成员函数内可以调用其他非const成员函数,也可以调用其他const成员函数

取地址运算符


不取地址的方法:

(1)返回空

Date* opertor&()
	{
		return NULL;
	}
	const Date* opertor&()const
	{
		return NULL;
	}

(2)只声明不定义

Date* opertor&();

优点:

宏常量:增强可维护性

宏函数:增强效率(没有压栈)

缺点:

(1)不方便调试

(2)没有类型安全的检查

(3)可读性差、可维护性差(宏函数)、容易出错


#define Swap(a,b)\//\为换行符
{\
	int tmp = a; \
	a = b; \
	b = tmp;\
}

六、inline函数

以inline修饰的函数叫内联函数,编译时C++编译器会调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行效率

1、inline是一种以空间换时间的做法,省去调用函数额外的开销。所以代码很长或有循环、递归的函数不适宜用内联函数

2、inline对于编译器而言只是一个建议,编译器会自动优化。如果定义为inline的函数体内有循环、递归,编译器优化时会忽略掉内联函数

3、inline必须函数定义在一起,才能成为内联函数,仅仅将inline放在声明前是没用的

4、定义在类内的成员函数默认定义为内联函数

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
	void Show()//定义在类内的函数默认为内联函数
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	inline void Display();
private:
	int _year;
	int _month;
	int _day;
};
inline void Date::Display()//成员函数定义为内联
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
inline void test()//全局函数定义为内联
{}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值