类和对象(中)

news2024/9/29 23:39:02

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

目录

 构造函数

 析构函数

拷贝构造 

赋值重载

运算符重载

 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 ;
 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日一题Day72】LC855考场就座 | 构造数据结构 动态数组+二分查找

考场就座【LC855】 There is an exam room with n seats in a single row labeled from 0 to n - 1. When a student enters the room, they must sit in the seat that maximizes the distance to the closest person. If there are multiple such seats, they sit in the sea…

宝藏又小众的东方行走rpg制作大师素材网站分享

看到大家都在问东方行走rpg制作大师素材&#xff0c;既要免费又要质量好&#xff0c;数量还要多&#xff0c;小编好不容易挖到了宝藏素材网站哦&#xff0c;资源优质数量庞大&#xff0c;使用体验也很好&#xff0c;要是需要的话&#xff0c;赶紧看一看&#xff0c;小编会给大家…

深潜价值互联网蓝海,2022中国区块链产业发展报告发布|陀螺研究院年终献礼...

2022年&#xff0c;是全球发展史上重要的一年&#xff0c;在俄乌冲突以及全球通胀的大背景下&#xff0c;全球经济环境风高浪急、风云诡谲&#xff0c;数字经济正以前所未有的速度冲击着世界固有格局&#xff0c;并成为撬动全球经济复苏和快速增长的新杠杆。围绕数字经济的科技…

软考在哪可以报名?

软考每年有两次考试&#xff0c;分别安排在上半年和下半年&#xff0c;上半年考试时间为5月下旬&#xff0c;下半年考试时间为11月上旬&#xff0c;每年考试时间并不是固定的。 2023年软考考试时间 根据往年软考时间安排来看&#xff0c;预计2023年软考考试时间上半年在5月中…

vue - - - - - 你不知道的技巧

vue - 你不知道的技巧1. 路由参数获取1. 路由参数获取 关于路由参数的获取&#xff0c;相信如下操作很常见: <script> export default {data() {return {};},mounted() {console.log("路由参数", this.$route.params.id);} }; </script>还有一种不太常…

C Primer Plus 第六版(中文版)第十五章(完美修订版)编程练习答案

//本博主所写的代码仅为阅读者提供参考&#xff1b; //若有不足之处请提出&#xff0c;博主会尽其所能修改&#xff1b; //附上课后编程练习题目&#xff1b; //若是对您有用的话请点赞或分享提供给它人&#xff1b; //---------------------------------------------------…

操作系统——进程与线程

进程与线程一、进程的概念1、进程和进程实体2、进程的组织方式3、进程的特征二、进程的状态与转换1、进程的状态2、进程的转换三、进程控制1、定义2、原语控制①创建原语②撤销原语③阻塞原语④唤醒原语⑤切换原语四、进程通信方法一&#xff1a;共享内存方法二&#xff1a;管道…

惠州学院采购JKTD-1000型铁电材料测试仪

惠州学院采购JKTD-1000型铁电材料测试仪 JKTD-1000型铁电材料测试仪 模式测量电路&#xff0c;与传统的 Sawyer-Tawer-模式相比&#xff0c;此电路取消了外接电容&#xff0c;可减小寄生元件的影响。此电路的测试精度取决于积分器积分电容的精度&#xff0c;减少了对测试的影响…

软件测试概念篇

目录 1.软件测试 2.需求 2.1 用户需求 2.2 软件需求 2.3 测试人员眼里的需求 2.4 需求的重要性 3.测试用例 3.1 什么是测试用例 3.2 为什么有测试用例 4.BUG 4.1 BUG的概念 4.2 如何描述一个BUG 4.3 如何定义BUG的优先级 4.4 BUG的生命周期 5.软件生命周期 6. …

RocketMQ 基本概念与工作原理

一、基本概念 消息(Message) 消息&#xff08;Message&#xff09;就是要传输的信息。一条消息必须有一个主题&#xff08;Topic&#xff09;&#xff0c;主题可以看做是你的信件要邮寄的地址。 主题(Topic) Topic表示一类消息的集合&#xff0c;每个主题包含若干条消息&am…

A机器连接同一局域网下的B机器的虚拟机中的mysql

一 背景描述 1.1 需求描述 B机器windows10 的机器ip为&#xff1a; 172.16.71.13 在其上的虚拟机中安装了mysql&#xff0c;虚拟机的ip为 192.168.152.141 。 A机器的ip为&#xff1a;172.16.71.14 &#xff1b; 需求&#xff1a; A机器想访问 B机器172.16.71.13 这台机…

aardio - 【库】libxl库,一个dll操作excel

经常用到excel操作&#xff0c;也有几个现成的库能实现我需要的功能&#xff0c;但用起来总是感觉不顺手。 于是便抽了两天时间&#xff0c;在aaz.libxl库的基础上&#xff0c;按照我的使用习惯进行了修改。 以后再也不用为操作excel发愁啦。 下载地址&#xff1a;http://che…

广州车展|埃安超跑Hyper GT登场,给年少有为者的时代献礼

近年来&#xff0c;随着中国新能源的技术日趋成熟&#xff0c;中国新能源车开始蓬勃发展&#xff0c;逐渐占据了可观的市场份额&#xff0c;并朝着高端化迈进。有一个有趣的现象&#xff1a;希望进军豪华纯电市场的中国车企&#xff0c;几乎都会与特斯拉对标。然而&#xff0c;…

2023新一代设备管理系统助力企业高效化巡检

设备数量和种类都比较多的情况下&#xff0c;工厂设备管理员的工作往往压力巨大&#xff0c;因为生产环环相扣&#xff0c;每一个环节如果出现问题&#xff0c;都会影响到生产。如果隐患不能及时处理&#xff0c;会影响设备的正常和安全运转&#xff0c;严重的会波及到周围相关…

SpringBoot+Shiro+JWT+Mybatis-Plus搭建admin-shiro管理系统

1、项目背景 从零开始搭建一个项目骨架&#xff0c;最好选择合适&#xff0c;熟悉的技术&#xff0c;并且在未来易拓展&#xff0c;适合微服务化体系等。所以一般以Springboot作为我们的框架基础&#xff0c;这是离不开的了。 然后数据层&#xff0c;我们常用的是Mybatis&…

佳创视讯亮相第十八届文博会,VR直播全民化备受瞩目

2022年12月28日,第十八届中国(深圳)国际文化产业博览交易会正式面向公众开放。本届文博会采取线下为主、线上同步的方式,强化线上线下交互联动,旨在推动中华文化产业高质量发展,加强对外文化合作交流。展会共吸引2532家政府组团、文化机构和企业线下参展,870家机构和企业线上参…

SpringSecurity(二十四)--OAuth2:使用JWT和加密签名(下)非对称密钥加密

一、前言 由于上文对称密钥涉及到的内容比较多&#xff0c;所以这一节的非对称密钥加密拆开成这一节单独讲解。 所以大家尽量先阅读完上一章的内容后再浏览这一章内容会更好。 二、使用通过JWT和非对称密钥签名的令牌 本节将实现OAuth2身份验证的一个示例&#xff0c;其中授…

自定义事件实现rpc的调用C++和蓝图实现举例

参考视频&#xff1a;https://www.youtube.com/watch?vGcZQ2o6LpDI 1.自定义事件的方式实现rpc run on server 修改角色的最大速度&#xff0c;方框1&#xff0c;让客户端先行&#xff0c;速度直接改变&#xff1b;然后方框2&#xff0c;告知服务器&#xff0c;自己的速度已经…

双脚在路上,钢笔在手里,想法在脑中,2023年CSDN将在心头

☔️&#xff08;一&#xff09;行走过的道路 一年的时间说长不长&#xff0c;说短不短&#xff0c;所渡过时光的长短在于你是否留意你曾走过的路。 &#x1f434;① 记得2022年初我所许下的flag&#xff0c;是要在CSDN平台上运用今年一年的时间撰写超50篇的技术文章&#xff0…

MySQL-运算符详解

1. 算数运算符 运算符名称作用示例加法计算两个值或表达式的和SELECT A B-减法计算两个值或表达式的差SELECT A - B*乘法计算两个值或表达式的乘积SELECT A * B/或DIV除法计算两个值或表达式的商SELECT A / B%或MOD求模(求余)计算两个值或表达式的余数SELECT A % B 2. 比较运…