类的六个默认成员函数

 

目录

一、构造函数

构造函数的特点:

类的三种默认构造函数

使用默认生成的默认构造函数的注意事项:

二、析构函数

析构函数的特点:

三、拷贝构造函数

this指针:

拷贝构造函数的特点:

拷贝构造要使用传引用传参:

四、赋值运算符重载

运算符重载关键字:operator

五、普通对象和const修饰对象&运算符重载

const修饰成员

普通对象和const修饰对象&运算符重载使用场景


1类在创建时,即使是一个空类,其中也包含了六个默认的成员函数,这六个成员函数既可以自定义,也可以不写,直接使用默认的函数。但是在某些特殊的情况下可能会发生意想不到的事情。这里我们介绍一下这六个默认成员函数的功能:

一、构造函数

构造函数的功能:构造函数是用来初始化成员变量,并不是用来开辟成员的空间的函数

构造函数的特点:

  • 函数名和类名相同
  • 函数没有返回值
  • 对象实例化时自动调用
  • 可以进行函数重载
  • 类中必须创建默认构造函数;如果没创建默认构造函数,会自动生成默认构造函数

类的三种默认构造函数

类有三种默认构造函数,分别是:无参类型、全缺省值、自动生成的默认构造函数。这三种默认函数一个类中只能出现一种,出现一种以上系统会报错

//类中的默认构造函数
class Time
{
public:
    //无参,默认构造函数
	Time()
	{
		_hours = 0;
		_minutes = 0;
		_second = 0;
	}

    //全缺省,默认构造函数
    Time(int hours = 0, int minutes = 0, int second = 0)
	{
		_hours = hours;
		_minutes = minutes;
		_second = second;
	}

    //不写默认构造函数,编译器会默认生成一个
private:
	int _hours;
	int _minutes;
	int _second;
};

使用默认生成的默认构造函数的注意事项:

默认生成的默认构造函数不会处理内置类型会处理自定义类型;会调用自定义类型的默认构造函数对自定义类型的数据进行初始化。

二、析构函数

析构函数的功能:完成对象中的成员变量的销毁工作,不是释放对象的空间。

析构函数的特点:

  • 析构函数就是类名前面加按位取反操作符:~Time()
  • 析构函数在对象销毁时会自动调用
  • 析构函数不能重载
  • 析构函数不写会默认生成,默认生成的析构函数:内置类型不处理,自定义类型调用自定义类型的析构函数
  • 一般情况下不需要写析构函数;当开辟新空间时,一定要写析构函数,防止出现内存泄漏
class Time
{
public:
	~Time()
    {
        //类成员没有开辟空间时,不需要处理……
    }    
	
private:
	int _hours;
	int _minutes;
	int _second;
};
class Stack
{
public:
	Stack(int capacity = 4)
	{
        //构造函数开辟了新空间
		int* tmp = (int*)malloc(sizeof(int) * capacity);
		if (tmp == nullptr)
		{
			perror("malloc fail\n");
			exit(-1);
		}
		_a = tmp;
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
        //需要析构函数释放掉开辟的新空间
		free(_a);
	}

private:
	int* _a;
	int _top;
	int _capacity;
};

三、拷贝构造函数

拷贝构造函数功能:将一个自定义对象中的数据拷贝给其他自定义对象,它的用法:

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

    //这个地方是否可以使用传值传参呢?
    //	Date(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(2023, 6, 29);
    Date d3;

    //将d1拷贝到d2
	Date d2(d1);

	return 0;
}

this指针:

在进入拷贝构造正题之前,我想问一下各位读者。在以上例子中,d1要拷贝给d2,但是传参时仅仅传递了d1的值,函数是如何确保将d1拷贝给d2,而不是d3的呢?

答案是:类的成员函数中包含了隐藏的this指针,这个this指针是默认存在的,但对用户是透明的,用户可以进行使用。函数的实际写法其实是:

class Date
{
public:
    //……
    //用户不能写成这种形式,会报错
	Date(Date* const this, const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

private:
    //……
};

int main()
{
	Date d1(2023, 6, 29);
    Date d3;

	Date d2(&d2, d1);
    Date d4 = d1;//在对象初始化的过程中使用赋值符号,会调用拷贝构造函数

	return 0;
}

拷贝构造函数的特点:

  • 是构造函数的重载
  • 不写系统会默认生成
  • 生成的拷贝构造函数,内置类型会按照字节依次进行拷贝;自定义类型会调用自定义类型的拷贝构造函数

拷贝构造要使用传引用传参:

传值传参时,形参时实参的临时拷贝,形参会在栈上开辟一块空间,假设开辟空间名字是tmp。在将变量d传给tmp,就相当于Date tmp(d)。是不是很眼熟?对了这就是调用了一次拷贝构造函数。调用拷贝构造函数之后,在进行传值传参,就实现了无穷递归。因此不能使用传值传参。

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
        //……
	}
    
    //传值传参时,形参时实参的临时拷贝,形参会在栈上开辟一块空间,假设开辟空间名字是tmp。在将变量d传给tmp,就相当于Date tmp(d)。是不是很眼熟?对了这就是调用了一次拷贝构造函数。调用拷贝构造函数之后,在进行传值传参,就实现了无穷递归。因此不能使用传值传参。
	Date(Date d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

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

四、赋值运算符重载

赋值运算符功能:在初始化两个对象的前提下,将其中一个对象的值赋给另一个对象。

运算符重载关键字:operator

函数返回值 operator=(Date d1, Date d2, ……)

运算符重载函数返回值不固定,要根据运算符的结果自定义

=是要重载的运算符 

Date d1, Date d2, ……运算符操作数,按照从左到右的顺序依次传参,别忘记隐藏的this指针

//传引用返回,避免拷贝返回值,提高效率	
Date& operator=(const Date& d)
	{
		//可以先判断一下 *this != d
		_year = d._year;
		_month = d._month;
		_day = d._day;

		return *this;
	}

 operator关键字除了能重载赋值运算符,也能重载其他运算符,其他运算符各位小伙伴们可以自行尝试呦!

五、普通对象和const修饰对象&运算符重载

const修饰成员

访问const修饰的变量中的函数,传递过去的this指针具有常属性。而函数中隐藏的this指针并不具有常属性,导致权限放大,编译器报错。而不用const修饰的变量也可以使用const修饰的成员函数,权限缩小不会产生影响

class Date
{
public:
    //解决办法:在函数后面加const会修饰this指针	void Print()const
	void Print()
	{
		std::cout << _year << "-" << _month << "-" << _day << std::endl;
	}

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

int main()
{
	Date d1(2023, 6, 29);
	const Date d2 = d1;
	
	d1.Print();
	d2.Print();//编译器报错,const修饰的变量具有常性,this指针存在权限放大

	return 0;
}

普通对象和const修饰对象&运算符重载使用场景

普通对象和const修饰对象&运算符重载这两个默认函数的功能还是取对象所在位置的地址,一般的场景不会用到,使用默认的就可以。当需要隐藏地址时,可以自己写

	Date* operator&()
	{
		return nullptr;
	}
	Date* operator&()const
	{
		return nullptr;
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ljiyu0506

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值