默认成员函数

前文提要

本文全文以日期类为例
先简单写一个日期类

class Data{
public:


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

且补充一个小知识

数据类型的划分

内置类型:是编程语言提供的基本数据类型,例如整数、浮点数、字符、布尔值
自定义类型:是开发人员根据应用程序的需求定义的类型,它可以由内置类型组成,也可以由其他自定义类型组成。自定义类型可以通过类、结构体、枚举等方式进行定义。

默认成员函数

默认成员函数是一些 当我们什么都没有编写时,也会自动在类中生成的函数

构造函数

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

特征

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。

由上述三条可知构造函数的基本特质
先写一个简单的构造函数

	//函数名与类名相同 且 无返回值
	Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
  1. 构造函数可以重载。

构造函数可以支持重载的化,便可以支持各种各样的初始化

// 1.无参构造函数
 Date()
 {}
 // 2.带参构造函数
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 //全缺省的构造
  Date(int year = 0, int month = 0, int day = 0)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 //半缺省的构造
  Date(int year, int month = 0, int day = 0)
 {
 _year = year;
 _month = month;
 _day = day;
 }
  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
  2. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
    由于构造函数是对象实例化时编译器自动调用
    虽然无参的构造函数与全缺省的构造函数满足函数重载的要求。
    但不满足构造函数的要求,全缺省与无参的函数会引起歧义
    当函数调用不传参数时,编译器无法识别调用哪个构造函数
    在这里插入图片描述

默认构造函数

是一个在创建对象时被自动调用的特殊的构造函数。它通常没有任何参数,并且不执行任何特定的初始化操作。默认构造函数的作用是初始化对象的成员变量,确保对象在创建时处于一个合理的状态。

无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

默认构造函数由编译器自动生成的构造函数,是一个无参的构造函数
构造函数中对于成员对象的处理根据成员对象的类型分为两类
内置类型不处理
自定义类型调用自定义类型的默认构造函数
也因此,建议每个函数都提供默认构造函数。
此外,因内置类型不予处理,c++也时常被人诟病
在C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

故而,整体上我们日期类的构造函数

Date(int year = 1900, int month = 1, int day = 1)
	// 拷贝构造函数Date::Date(int year , int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

析构函数

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

特征

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  5. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;
  6. 有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类

回到我们的例子来
Date没有申请空间,析构函数是可以不写的

拷贝构造

只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

特征

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

按定义可知,拷贝构造中有且只含有一个参数->类类型对象
如果直接传类类型对象本身,传值的过程本身就是一次类对象的拷贝
也不可避免的会引起无穷递归调用

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
    类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的

  2. 拷贝构造函数典型调用场景:

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

回到例子上来
日期类没有设计资源申请,默认的拷贝函数也足以满足需求

赋值重载

赋值重载也可以是一种特殊的默认成员函数,不手动书写时,会在类中自动生成。具体在操作符重载中细说

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数。也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

原型

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: .注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

赋值运算符的重载

格式

参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要复合连续赋值的含义

特征

  1. 赋值运算符只能重载成类的成员函数不能重载成全局函数

赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的
赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了

2.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝 (与默认拷贝构造类似)

回到例子上来,与拷贝构造同理

前置++ 与 后置++

前置++和后置++都是一元运算符
为了让前置++与后置++形成能正确重载

C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递

注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1
以日期类未例

 	// 前置++
	Date& operator++()
	{
		return (*this) += 1;
	}
	// 后置++
	Date operator++(int)
	{
		Date tmp = *this;
		*this += 1;
		return tmp;
	}

日期类的实现

Date.cpp

#include"Data.h"
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	static int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = days[month];
	if ((month == 2) && (((year % 4 == 0) && (year % 100 != 0)) || year % 400 == 0))
	{
		day++;
	}
	return day;
}
// 全缺省的构造函数
Date::Date(int year , int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
// d2(d1)
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

Date.h

#pragma once
#include<iostream>
using namespace std;
class Date
{

public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	// 拷贝构造函数
  // d2(d1)
	Date(const Date& d);
	// 赋值运算符重载
  // d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
	// 析构函数
	~Date()
	{

	};
	// 日期+=天数
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month == 13)
			{
				_month = 1;
				_year++;
			}
		}
		return *this;
	}
	void days()
	{
		int day = _day;
		for (int i = 1; i < _month; i++)
		{
			day += GetMonthDay(_year, i);
		}
		cout << "侯稼澍" << _year << ":" << _month << ":" << _day << "为第" << day << "天" << endl;

	}
	void print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
	// 日期+天数
	Date operator+(int day)
	{
		Date tmp = *this;
		return tmp += day;
	}
	// 日期-天数
	Date operator-(int day)
	{
		Date tmp = *this;
		return tmp -= day;
	}
	// 日期-=天数
	Date& operator-=(int day)
	{
		_day -= day;
		while (_day <= 0)
		{
			_month--;
			if (_month == 0)
			{
				_month = 12;
				_year--;
			}
			_day += GetMonthDay(_year, _month);
		}
		return *this;
	}
	// 前置++
	Date& operator++()
	{
		return (*this) += 1;
	}
	// 后置++
	Date operator++(int)
	{
		Date tmp = *this;
		*this += 1;
		return tmp;
	}
	// 后置--
	Date operator--(int)
	{
		Date tmp = *this;
		*this -= 1;
		return tmp;
	}
	// 前置--
	Date& operator--()
	{
		return *this -= 1;
	}
	// >运算符重载
	bool operator>(const Date& d)
	{
		if ((_year > d._year)
			|| (_year == d._year && _month > d._month)
			|| (_year == d._year && _month == d._month && _day > d._day))
			return true;
		else
			return false;
	}
	// ==运算符重载
	bool operator==(const Date& d)
	{
		return ((_year == d._year) && (_month == d._month) && (_day == d._day));
	}
	// >=运算符重载
	bool operator >= (const Date& d)
	{
		return (*this > d) || (*this == d);
	}
	// <运算符重载
	bool operator < (const Date& d)
	{
		return !(*this >= d);
	}
	// <=运算符重载
	bool operator <= (const Date& d)
	{
		return (*this < d) || (*this == d);
	}
	// !=运算符重载
	bool operator != (const Date& d)
	{
		return !(*this == d);
	}
	// 日期-日期 返回天数
	int operator-(const Date& d)
	{
		int count = 0;
		Date big = *this;
		Date small = d;
		if (small > big)
		{
			small = *this;
			big = d;
		}
		while (big != small)
		{
			small++;
			count++;
		}
		return count;
	}

private:

	int _year;

	int _month;

	int _day;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值