类的默认成员函数

Posted The August

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的默认成员函数相关的知识,希望对你有一定的参考价值。

构造函数

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
//using namespace std;
class Data

public:
	void Init(int year,int month,int day)
	
		_year = year;
		_month = month;
		_day = day;
	
private:
	int _year;
	int _month;
	int _day;
;
int main()

	Data d1;
	d1.Init(2021, 10, 12);
	return 0;

当上段代码中的d1忘记初始化时,只是构造函数就起了至关重要的作用

构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象

构造函数的特征:

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载(提供多种初始化的方式)。


如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成
没有定义构造函数,对象也可以创建成功,因此此处调用的是编译器生成的默认构造函数

总结:
不写构造函数,编译器默认生成构造函数
编译器默认生成的构造函数:
1.内置类型不会初始化
2.自定义类型它会调用它的无参构造函数进行初始化

上方的代码(c++98)的语法有些繁琐,而在c++11中可以优化


注:这里的成员变量只是定义,不是初始化,就像函数缺省参数一样,给的是缺省值。


注:无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。

默认构造函数可以理解为不传参就可以调用的函数


注:成员变量的命名风格一般都是加个前缀或者后缀标识区分就行。

析构函数

析构函数是特殊的成员函数。
其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值。
  3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

注:上方的代码中Data类中的析构函数可以不写;但Stack中的析构函数必须写


注:
析构函数跟构造函数类似,编译器默认生成的析构函数:
1.内置类型成员不处理
2.自定义类型成员会去调用自身的析构函数

拷贝构造函数

构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用.
拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。


拷贝函数中的参数必须是引用传参或指针,不然会造成死递归
注:

如果类中没写拷贝构造,编译器会默认生成拷贝构造函数,与构造函数和析构函数不同:
1.内置类型,字节序的浅拷贝(按字节序的拷贝)
2.自定义类型,会去调用它的的拷贝构造函数


注:编译器默认生成拷贝构造函数并不能解决所有问题,像Stack这样的类,编译器默认生成拷贝构造完成的就是浅拷贝。解决方法:实现深拷贝的拷贝构造函数。

若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。

赋值运算符重载

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数:operator 运算符
参数:操作符的操作数有几个就有几个参数,参数类型你要操作的类型对象
返回值:看运算符运算后的返回值是什么

总结:

  • 运算符默认都是给内置类型变量用的
  • 自定义类型的变量想用这些操作符,就得自己进行运算符重载
  • 运算符重载的意思是自己去写一个函数定义实现这里的运算符行为
  • 运算符号重载写成全局的可以,但是面临访问私有成员变量的问题

注:

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

按正常运算符重载无法区分前置++和后置++,为了区分,这里就做了一个特殊处理,给后置++增加int参数,这样就构成了函数重载

赋值运算符重载

赋值运算符主要有四点:

  1. 参数类型
  2. 返回值
  3. 检测是否自己给自己赋值
  4. 返回*this
  5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。

如果自己不写,编译器会默认生成赋值重载,跟拷贝构造类似,内置类型成员会完成值拷贝,自定义类型成员会调用它的赋值重载
原则上:编译器默认生成的能完成自己要的功能就可以不写,不能完成就需要我们自己写一个.。
实现数据结构类,基本都要自己写这几个成员函数(构造函数、析构函数、拷贝函数、赋值重载都需要自己写,默认生成的都有问题,不能用)

总结:
常用短小函数,直接在类里面定义,他们默认就是inline
C++为了增强程序的可读性,提出运算符重载,并且让我们可以实现运算符重载函数,控制这个运算符行为
一个类到底要重载哪些运算符,并且要考虑重载了这个运算符有没有意义
运算符重载和函数重载,都用了重载这个词,但是他们之前没有关联

取地址及const取地址操作符重载

class Date
 
public :
 Date* operator&()//取地址操作符重载
 
 return this ;
 
 
 const Date* operator&()const//const取地址操作符重载
 
 return this ;
 
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
;

不想让别人获取到地址信息

class Date
 
public :
 Date* operator&()//取地址操作符重载
 
 return nullptr ;
 
 
 const Date* operator&()const//const取地址操作符重载
 
 return nullptr ;
 
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
;

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容。

练习(类的默认成员函数)

Date.h

#pragma once
#include<iostream>
#include<assert.h>

using namespace std;
class Date

public:
	int GetMonthDay(int year, int month)
	
		static int date[13] =  0,31,28,31,30,31,30,31,31,30,31,30,31 ;
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
		
			return 29;
		
		return date[month];
	
	Date(int year = 0, int month = 1, int day = 1)
	
		_year = year;
		_month = month;
		_day = day;
		if (_year < 0 || _month < 1 || _month>12 || _day<=0 || _day>GetMonthDay(_year, _month))
		
			cout << this->_year << "/" << this->_month << "/" << this->_day;
			cout << "非法日期" << endl;
		
	
	void print()const
	
		cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
	
	Date& operator += (int day);
	Date operator +(int day)const;
	Date& operator -=(int day);
	Date operator -(int day)const;
	Date& operator ++();
	Date operator ++(int);
	Date& operator --();
	Date operator --(int);
	int operator-(const Date& d)const;
	bool operator >(const Date& d)const
	
		if (_year > d._year)
			return true;
		else if (_year == d._year && _month > d._month)
			return true;
		else if (_year == d._year && _month == d._month && _day > d._day)
			return true;
		else
			return false;
	
	bool operator >=(const Date& d)const
	
		return *this > d || *this == d;
	
	bool operator <(const Date& d)const
	
		return !(*this >= d);
	
	bool operator <=(const Date& d)const
	
		return *this < d || *this == d;
	
	bool operator ==(const Date& d)const
	
		return _day == d._day && _month == d._month && _year == d._year;
	
	bool operator !=(const Date& d)const
	
		return !(*this == d);
	
	Date* operator &()
	
		return this;
	
	const Date* operator &()const
	
		return this;
	
private:
	int _year;
	int _month;
	int _day;
;

Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
Date& Date::operator+=(int day)

	if (day < 0)
	
		return *this -= -day;
	
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month > 12)
		
			_month = 1;
			_year++;
		
	
	return *this;

Date Date::operator +(int day)const

	Date tmp(*this);
	tmp += day;
	return tmp;



Date& Date::operator -=(int day)

	if (day < 0)
	
		return *this += -day;
	
	_day -= day;
	while (_day <= 0)
	
		_month--;
		if (_month < 1)
		
			_year--;
			_month = 12;
		
		_day += GetMonthDay(_year, _month);
	
	return *this;

Date Date::operator -(int day)const

	Date ret(*this);
	ret -= day;
	return ret;


Date& Date::operator ++()

	return *this += 1;

Date Date::operator ++(int)

	Date tmp(*this);
	*this += 1;
	return tmp;

Date& Date::operator --()

	return *this -= 1;

Date Date::operator --(int)

	Date tmp(*this);
	*this -= 1;
	return tmp;

int Date::operator-(const Date& d)const

	Date max(*this);
	Date min(d);
	int flag = 1;
	if (*this < d)
	
		max = d;
		min = *this;
		flag = -1;
	
	int m = 0;
	while (max != min)
	
		min++;
		m++;
	
	return m * flag;



以上是关于类的默认成员函数的主要内容,如果未能解决你的问题,请参考以下文章

类的默认成员函数

C++类和对象—— 类的6个默认成员函数及日期类的实现

类和对象类的6个默认成员函数

类和对象类的6个默认成员函数

Kotlin类的初始化 ② ( 主构造函数 | 主构造函数定义临时变量 | 主构造函数中定义成员属性 | 次构造函数 | 构造函数默认参数 )

具有引用数据成员的类的默认构造函数?