类和对象(中)

 原文再续,书接上回!! 继续类和对象的学习。

目录

 构造函数

 析构函数

拷贝构造 

赋值重载

运算符重载

 const成员

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


当我们没有向类中写入任何成员的时候(也就是空类),类中就什么也没有吗?答案是否定的,任何类在什么都不写时,编译器会自动生成6个默认成员函数

这六个默认成员函数,是用户不显示实现,编译器会自动生成的成员函数。你可以想象它们六兄弟是替你守江山的忠诚卫士,前4个兄弟用户实现的情景还比较多见。老五老六很少需要用户动手实现。

 构造函数

注意:构造函数名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

构造函数主要完成初始化工作,它有如下特性:(代码演示以日期类为例)

●函数名与类名相同,没有返回值。
注意:如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成!
不显示定义构造函数:
#include <iostream>

using namespace std;

class Date
{
public:
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1;
    d1.Print();
    return 0;
}

在没有写构造函数的情况下,应该调用默认的构造函数初始化对象。但是从结果来看好像编译器并没有帮我们完成这件事,这里先带着疑问继续探索!

无参构造:
  //无参构造
    Date()
    {
        //...
    }

注意通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明 。

    Date d1;
    d1.Print();

效果和不显示定义是一样的!

有参构造:
   //有参构造
    Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date d3(2020,10,3);
    d3.Print();

全缺省构造:
 //全缺省构造   
   Date(int year = 2020, int month = 12, int day = 26)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date d4;
    d4.Print();

    Date d2(1,1,1);
    d2.Print();

半缺省构造:

    //半缺省构造
    Date(int year, int month = 10, int day = 3)
    {
       _year = year;
        _month = month;
        _day = day;
    }
    Date d4(1999);
    d4.Print();

●构造函数可以重载。

class Date
{
public:
	Date()
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Print();
	Date d2(2000,2,3);
	d2.Print();
	return 0;
}

●对象实例化时编译器自动调用对应的构造函数,保证每个数据成员都有 一个合适的初值。

问题:在未显示定义构造的情景下,编译器生成的默认构造好像并没有什么作用。

答:默认生成的构造函数只处理自定义类型,内置类型不做处理。

内置类型内置类型就是语言提供的数据类 型,如:int/char...
自定义类型:使用class/struct/union等自己定义的类型。
#include <iostream>

using namespace std;

class Time
{
public:
	Time()
	{
		cout << "调用无参构造Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	Time _t;
};
int main()
{
	Date d1;
	return 0;
}

 通过调试发现,编译器生成默认的构造函数会对自定类型成员_t调用它的默认成员函数!!内置类型确实没有做任何处理。根据这一现象,内置类型成员变量在类中声明时可以给默认值

	//内置类型
	int _year = 2001;
	int _month = 10;
	int _day = 3;
	//自定义类型
	Time _t;

注意:这里给的是缺省值,并不是初始化!

小总结:

♠构造函数在不显示定义的情况下,会自动生成。

♠默认构造函数对内置类型不做处理,自定义成员调用它自己的默认构造函数。为了同时处理内置类型,可以在声明的时候给缺省值。

构造函数在对象整个生命周期内只调用一次

注意:不仅仅是编译器自动生成的构造函数才是默认的无参、全缺省、用户没显示定义编译器自动生成的构造函数,都是默认构造函数!

 析构函数

注意:析构函数的任务不是完成对对象本身的销毁(局部对象销毁工作是由编译器完成的),而是对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

析构函数主完成对象中资源的清理,其特征如下: (代码演示以顺序表为例)

●析构函数名是在类名前加上字符 ~,无参数无返回值类型。
typedef int DataType;
class SeqList
{
public:
	//全缺省构造
	SeqList(int capacity = 3)
	{
		_arr = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _arr)
		{
			perror("malloc:");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;
	}
	void PushBack(DataType data)
	{
		_arr[_size] = data;
		_size++;
	}
	void Print()
	{
		for (int i = 0; i < _size; i++)
		{
			cout << _arr[i] << " ";
		}

	}
	//析构
	~SeqList()
	{
		if (_arr)
		{
			free(_arr);
			_arr = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _arr;
	int _capacity;
	int _size;
};
int main()
{
	SeqList s;
	s.PushBack(1);
	s.PushBack(2);
	s.PushBack(3);
	s.Print();
	return 0;
}

 默认构造完成初始化工作!

向顺序表中插入数据!

打印顺序表中的数据!

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

一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
还是上面的代码,将显示定义的析构函数删掉,当对象生命周期结束时,自动调用析构函数,但是并没有完成资源清理的任务!

原因:内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可。对象销毁时,要保证每个自定义对象都可以正确销毁,创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。

	class Time
	{
	public:
	 ~Time()
	 {
	 cout << "~Time()析构函数调用" << endl;
	 _hour = 0;
	 _minute = 0;
	 _second = 0;
	 }
	private:
	 int _hour = 1;
	 int _minute = 1;
	 int _second = 1;
	};
	class Date
	{
	private:
		// 基本类型(内置类型)
		int _year = 1970;
		int _month = 1;
		int _day = 1;
		// 自定义类型
		Time _t;
	};
	int main()
	{
		Date d;
		return 0;
	}

注意:析构函数不能重载!
总结:如果类中没有申请资源,可以不写析构函数,使用编译器生成的默认析构函数就可以,就好比日期类。当类中有资源申请时,就要考虑资源泄露的问题,一定要写析构函数,比如顺序表类,栈类等。

拷贝构造 

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

 拷贝构造函数主要完成的工作是,创建对象时,创建一个与已存在对象一某一样的新对象。

先从语法来看,拷贝构造函数的参数只有一个,类类型对象的引用。拷贝构造函数是构造函数的一个重载形式。(代码演示以日期类为例)

class Date
{
public:
	Date(int year = 2000, int month = 10, int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d) 
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day<<endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Print();
	Date d2(d1);
	d2.Print();
	return 0;
}

 拷贝构造函数的函数名也和类名相同,不同点是参数类型不同,所以说和构造函数是函数重  载。

这里肯定有小伙伴存有疑问,拷贝构造函数的参数我一定要传本类类型的引用吗,我想用传值方式试试!!

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

报错的原因是什么呢?

答:参数传值,形参是实参的一份拷贝,所以传值会引发对象的拷贝,倒置无穷递归。

class Date
{
public:
    //构造函数
	Date(int year = 2000, int month = 10, int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //拷贝构造
	Date(const Date& d) 
	{
		cout << "传值,形参拷贝实参,调用拷贝构造Date"<<endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
void Fun1(Date a)
{
	cout << "传值:Fun1()" << endl;
}
void Fun2(Date& b)
{
	cout << "传引用:Fun2()" << endl;
}
int main()
{
	Date d1;
	Fun1(d1);
	return 0;
}

 传引用:

	Fun2(d3);

 传引用,形参是实参的别名。没有引发对象的拷贝!

上面的程序很好的证明,传值方式会调用拷贝构造,因为形参拷贝实参时用已存在的类类型对象创建新对象。而调用拷贝构造传值还会重复这个动作,就引发了无穷递归!

 不显示定义拷贝构造函数。

注意:编译器默认生成的拷贝构造函数,内置类型是按照字节方式直接拷贝的,自定
义类型是调用它自己拷贝构造函数完成拷贝的。
//不显示定义默认拷贝构造
class Time
{
public:
	Time()
	{
		_hour = 12;
		_minute = 30;
		_second = 30;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型
	int _year = 2001;
	int _month = 10;
	int _day = 3;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

通过调试发现,默认生成的拷贝构造和构造、析构不同的地方是,它不仅仅拷贝自定义类型,内置类型的数据也进行了拷贝!

妙哉,妙哉!这样说来不用自己动手写拷贝构造,用编译器自动生成的不就可以了?

像日期类这样不设计资源申请的,用编译器自动生成的拷贝是没有问题的。但是如果涉及到资源申请,则拷贝构造就一定要写!比如下面的场景:(代码以栈为例)

typedef int DataType;
class Stack
{
public:
	//构造函数
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc");
			exit(-1);
		}
			_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		_array[_size] = data;
		_size++;
	}
	//析构函数
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	Stack s2(s1);
	return 0;
}

 内置类型是按照字节方式直接拷贝, 我们通过直观的发现问题似乎也不大,就是s2对象拷贝的空间和对象s1是一块空间。

继续调试,程序崩溃掉了,错误大意是断言失败!

原因是在对象生命周期结束时,C++编译系统系统自动调用析构函数! 先建立的对象后销毁,后建立的对象先销毁!也就是说在析构的过程中,存在同一块地址空间二次释放的问题。上述的拷贝方式称为浅拷贝,面对这种场景,浅拷贝显然是有问题的。需要用户显示定义拷贝构造函数!(针对上述问题,显示定义拷贝构造如下)

	//显示定义拷贝构造
	Stack(const Stack& s)
	{
		_array = (DataType*)malloc(s._capacity * sizeof(DataType));
		for (int i = 0; i < s._size; i++)
		{
			_array[i] = s._array[i];
		}
		_size = s._size;
		_capacity = s._capacity;
	}

 这种拷贝方式叫做深拷贝!

分别向两个对象中插入数据也不会相互影响:

	s1.Push(2);
	s1.Push(3);
	s1.Push(4);

	s2.Push(5);
	s2.Push(6);
	s1.print();
	s2.print();

除了上述的代码情景,还有一种场景没有谈到,当函数返回值类型为类类型对象时,也会调用拷贝构造函数。

class Date
{
public:
	Date(int year, int minute, int day)
	{
		cout << "调用构造Date():" << endl;
	}
	Date(const Date& d)
	{
		cout << "调用拷贝构造Date(const Date& d):" << endl;
	}
	~Date()
	{
		cout <<"调用析构~Date():"<< endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
Date Test(Date d)
{
	return d;
}
int main()
{
	Date d1(2022,12,28);
	Test(d1);
	return 0;
}

 小总结:

涉及资源的申请,就要写拷贝构造,反之可以不写。

拷贝构造调用场景如:

  ♦使用已存在对象创建新对象。
  ♦函数参数类型为类类型对象。
  ♦函数返回值类型为类类型对象。
●拷贝构造函数的参数只有一个 必须是类类型对象的引用
编译器默认生成的拷贝构造函数,内置类型按照字节方式拷贝的,自定 义类型调用其拷贝构造函数。

赋值重载

赋值重载也是默认成员函数之一,相对于其它函数,函数名比较特殊,函数名字为:关键字operator后面接赋值重载的运算符符号

语法:返回值类型  operator 操作符 ( 参数列表 )。
参数类型 const T& ,传递引用可以提高传参效率。
返回值类型 T& ,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值。
#include <iostream>

using namespace std;

class Date
{
public:
	//构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		cout << "构造函数Date()" << endl;
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造
	Date(const Date& d)
	{
		cout << "拷贝构造Date(const Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	//赋值重载
	Date& operator=(const Date& d)
	{
		cout << "赋值重载" << endl;
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2001,10,3);
	Date d2(d1);

	Date d3;
	d3 = d2;


	return 0;
}

 观察上述代码效果,调用了显示定义的赋值重载,完成了对象间的赋值。那么既然赋值重载也是默认成员函数,不显示定义,编译器会自动生成。

屏蔽掉显示定义的赋值重载函数后,对象间依然完成了赋值:

 其实,当用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝注 意:内置类型直接赋值,而自定义类型调用对应类的赋值运算符重载完成赋值。

class Time
{
public:
Time()
{
	_hour = 1;
    _minute = 1;
    _second = 1;
}
 Time& operator=(const Time& t)
 {
	 cout << "_t赋值重载operator=" << endl;
	 if (this != &t)
	 {
		 _hour = t._hour;
		 _minute = t._minute;
		 _second = t._second;
	 }
	 return *this;
 }
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型
	int _year = 2001;
	int _month = 10;
	int _day = 3;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d1;
	Date d2;
	d1 = d2;
	return 0;
}

 

 对于上述日期类,没有涉及到资源的申请,赋值重载是否实现都可以; 一旦涉及到资源管理则必须要实现。 (代码演示以栈为例)

typedef int DataType;
class Stack
{
public:
	Stack(int capacity = 10)
	{
		_arr = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _arr)
		{
			perror("malloc faile:");
			exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		_arr[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_arr)
		{
			free(_arr);
			_arr = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _arr;
	int _size;
	int _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	Stack s2;
	s2 = s1;
	return 0;
}

 出现的错误和拷贝构造资源申请,未显示定义拷贝构造的错误类似。

当s1赋值给s2时,编译器会将s1中内容原封不动的拷贝到s2中,这样会导致两个问题:

s2原本的内存空间丢失,造成了内存泄露。

s1/s2共享同一块内存空间,最后销毁时导致同一快空间释放了两次。

显示定义: 

typedef int DataType;
class Stack
{
public:
	Stack(int capacity = 10)
	{
		_arr = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _arr)
		{
			perror("malloc faile:");
			exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}
	Stack& operator=(const Stack& s)
	{
		if (this != &s)
		{
			_arr = (DataType*)malloc(10 * sizeof(DataType));
			for (int i = 0; i < s._size; i++)
			{
				_arr[i] = s._arr[i];
			}
			_size = s._size;
			_capacity = s._capacity;
		}
		return *this;
	}
	void Push(const DataType& data)
	{
		_arr[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_arr)
		{
			free(_arr);
			_arr = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _arr;
	int _size;
	int _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	Stack s2;
	s2 = s1;
	return 0;
}

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

用户在类外实现全局的赋值运算符重载,会和编译器在类中生成的默认赋值运算符重载冲突,故赋值运算符重载只能是类的成员函数。

运算符重载

除了赋值运算符之外,像+、-、*、/等这样的运算符一直伴随着我们的学习生活,对于我们来说“见其知其意”,为了增强代码的可读性引入了运算符重载。

运算符重载是具有特殊函数名的函数 ,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 

以日期类为例,学习运算符重载:

 Date.h(预览日期类大概功能)

#pragma once

#include <iostream>

using namespace std;

class Date
{
public:
	//全缺省构造
	Date(int year = 2020, int month = 12, int day = 28)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//计算某年某月的天数
	int GetMonthDay(int year, int month)
	{
		//平年各月的天数
		int LeapYear[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		//闰年2月29天
		if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
		{
			return 29;
		}
		return LeapYear[month];
	}
	//赋值重载
	//析构函数
	//拷贝构造
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

    //运算符重载
	bool operator==(const Date& d) ;
	bool operator> (const Date& d);
	bool operator>=(const Date& d);
	bool operator<(const Date& d);
	bool operator<=(const Date& d);
	bool operator!=(const Date& d);
	
	//日期+=天数
	Date& operator+=(int day);
	//日期+天数
	Date operator+(int day);
	//日期-天数
	Date& operator-=(int day);
	//日期-天数
	Date operator-(int day);
    //前后置++
	Date& operator++();
	Date operator++(int);
    //前后置--
	Date& operator--();
	Date operator--(int);
	
	//日期-日期 返回天数
	int operator-(Date date);
private:
	int _year;
	int _month;
	int _day;
};

♥默认成员函数

日期类的实现不涉及资源的申请,拷贝构造,赋值运算符重载,析构函数使用编译器自动生成的即可。

♥计算某年某月的天数

重点在于区分平年和闰年的2月,闰年的2月有29天,平年的2月有28天。其余的月份是相同的。

int GetMonthDay(int year, int month)
	{
		//平年各月的天数
		int LeapYear[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		//闰年2月29天
		if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
		{
			return 29;
		}
		return LeapYear[month];
	}

♥运算符重载

语法: 返回值类型  operator 操作符 ( 参数列表 )。

形参看起来少一个,其实还有隐藏的this指针。

bool  Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
bool  Date::operator> (const Date& d)
{
	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;
	}
	return false;
}
bool  Date::operator>=(const Date& d) 
{
	return *this > d || *this == d;
}
bool  Date::operator<(const Date& d) 
{
	return !(*this >= d);
}
bool  Date::operator<=(const Date& d)
{
	return *this < d || *this == d;
}
bool  Date::operator!=(const Date& d)
{
	return !(*this == d);
}

小技巧:实现了前两个运算符重载后,后面的重载可以复用前面已经实现过的。但是一定要确保已经实现的没有错误!

♥日期+、+=、-、-=天数

这里要注意的环节就是,日期+-过程中的进位、借位问题。生活常识我们都知道,一个日期的天数、月份、年份均不能是负数,是0也不合适,例如:2020年0月0日,-1年-4月-6日,每个月的天数不能超过该年月的天数,其年份的月数也不可能超过12!还有要注意的就是,-和-=,+和+=的区别,-=和+=是改变了自身的数据,-和+是在源数据的基础上通过+、-操作获得一个数据。

//日期+=天数
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;
}
//日期+天数
Date  Date::operator+(int day)
{
	Date ret(*this);
	ret += day;
	return ret;
}

//日期-天数
Date& Date::operator-=(int day)
{
	//要减去的天数可能大于月天数
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		//_day是负数
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
//日期-天数
Date  Date::operator-(int day)
{
	Date tmp(*this);
	tmp -= day;
	return tmp;
}

♥前后置++、--

为了让前置++(--)与后置++(--)形成能正确重载 。 C++规定:后置++(--)重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器 自动传递!!!
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
Date  Date::operator++(int)
{
	Date ret(*this);
	*this += 1;
	return ret;
}
// 前置
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

// 后置
Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;

	return tmp;
}

♥日期-日期

两个日期的相减可以计算出这之间的天数,首先要处理的就是大小日期的问题。当小日期-大日期的时候就说明得出的天数是我们已经度过的过去天数,大日期-小日期就是未来在过多少天才能到达大日期。这里用了一个标记变量来标记是哪一种情况。计算日期的天数,只要小日期还不等于大日期,就计数,最后得到的天数和标记就是我们要得到的综合信息。

//日期-日期 返回天数
int  Date::operator-(Date date)
{
	Date max = *this;
	Date min = date;
	int flag = 1;
	int count = 0;
	if ((*this) < date)
	{
		max = date;
		min = *this;
		int flag = -1;
	}
	while (max != min)
	{
		++min;
		++count;
	}
	return flag * count;
}

 const成员

const 修饰的 成员函数 称之为 const 成员函数 ,const 实际修饰的是该成员函数隐含 this 指针 ,表明在该成员函数中 不能对类的任何成员进行修改。

例如:

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

	void Print() const
	{
		cout << "Print()const" << endl;
		_year = 2021;
		_month = 10;
		_day = 3;

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

const修饰this指针

void Print() const  等价于 void Print(const Date* this)

♣ const对象可以调用非 const 成员函数吗?
权限的放大,不可以!
♣非 const 对象可以调用 const 成员函数吗?
权限的缩小,可以!
♣ 非 const 成员函数内可以调用其它的 const 成员函数
bool  Date::operator==(const Date& d) const
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
bool  Date::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;
	}
	return false;
}
bool  Date::operator>=(const Date& d) 
{
	return *this > d || *this == d;
}

可以,权限的缩小。

♣const成员函数内可以调用其它的非const成员函数吗?

bool  Date::operator> (const Date& d) 
{
	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;
	}
	return false;
}
bool  Date::operator>=(const Date& d) const
{
	return *this > d || *this == d;
}

不可以,权限的放大。

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

在开头就介绍过,这两个默认成员函数使用编译器生成的默认取地址的重载即可,很少需要自己实现,知道有这两兄弟即可。
 Date* operator&()
 {
 return this ;
 }
 const Date* operator&()const
 {
 return this ;
 }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值