C++——类和对象(中)

目录

 一、类的6个默认成员函数

二、构造函数

三、析构函数

四、拷贝构造函数

五、运算符重载

六、赋值重载

七、取地址重载

八、const成员


 一、类的6个默认成员函数

空类中什么都没有吗?在什么都没写的情况下,编译器会自动生成6个默认成员函数。

默认成员函数如果我们写了,编译器就不生成。没写,编译器自动生成。

  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 赋值重载
  5. const成员函数
  6. 取地址及const取地址操作符重载

 

二、构造函数

在c语言中,经常会忘记初始化和destroy。

  • 不初始化对象就插入值,编译器会报错。因为不初始化,对象里面存的都是随机值。
  • 忘记destroy不报错,但可能会造成内存泄露。

 

那能不能在对象定义的时候就自动初始化呢?

有滴,这个函数叫构造函数。

构造函数是一个特殊的成员函数,名字和类相同,无返回值,创建类的对象时编译器自动调用,并且在对象的整个生命周期内只调用一次

构造函数的任务不是为对象开辟空间,而是初始化对象。

 

特性:

  1. 函数名和类名相同
  2. 无返回值(void也不用写)
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载(可以提供多个初始化函数,有参/无参时调用不同函数)

 

1.有参构造函数

class Date
{
public:
	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(2022,10,15);
	d1.Print();

	return 0;
}

b3cecc8fb7ac4d678de4a1b3a4b6cf21.png

 

2.无参构造函数

class Date
{
public:
	Date ()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

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

int main()
{
	Date d1;//无参不要带括号:Date d1();


	d1.Print();

	return 0;
}

 6faf0a493a77441fa7fa71e9b8e19928.png

 

来个更优版的缺省参数

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

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

int main()
{
	Date d1(2022, 10, 15);
	Date d2;
	d1.Print();
	d2.Print();

	return 0;
}

88be60c4a509484cb7a91576369229d5.png

 

全缺省参数和无参的的构造函数可以共存(函数重载),但是在不传参调用的时候存在歧义,编译器不知道调用哪个就会报错。

 

应用:栈的初始化

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int *)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail\n");
			exit(-1);
		}

		_capacity = capacity;
		_top = 0;
	}

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

};

 

5. c++把类型分为内置类型(基本类型)和自定义类型。内置类型由语言提供,如 int/char/double.....指针。自定义类型:class/struct/Stack/Queue/Person

 

6. 如果类中没有定义构造函数,编译器会自动生成一个无参的默认构造函数。

如果我们自己定义了,编译器就不定义了。

自动生成的构造函数内置类型数据不处理自定义类型会调用它的默认构造函数。)/ /默认构造函数定义在下面       

 

 

例:自动生成的构造函数对内置类型不处理

class A
{
public:
	A()
	{
		_a == 0;
		cout << "A()的构造函数"<<endl;
	}

private:
	int _a;
};

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

	A _aa;//增加一个自定义类型
};

int main()
{
	Date d1;
	d1.Print();

	return 0;
}

b6a8deb0da0445229504611c9245a49e.png

 

想用自动生成的构造函数,但是类里面自定义类型和内置类型都有,内置类型怎么初始化?

自定义类型不用我们处理,编译器会自动调用他们的构造函数。内置类型我们可以给缺省值。默认生成的构造函数就会用这个缺省值初始化。

a03ab7499c4b48eab163fedf126f95b9.png

 

缺省值这里建议看了初始化列表后再来回味一遍,就能理解的更加透彻。

 

 如果在自己定义了构造函数,并且给所有成员变量都初始化了,就不用缺省值。

例:

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

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year=0;
	int _month=0;
	int _day=0;
};

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

a1f3840ce1164ffe95fb32dcbaef5bb9.png

 

如果自己写了构造函数,但是没有在构造函数里初始化变量,还是用缺省值初始化。

470269d497ad41f8938231c278e3396c.png

 

 707b839a27de4077a62258099628bf04.png

 

7.无参的构造函数全缺省的构造函数都称为默认构造函数(这两个有且只能存在一个,因为不传参调用的时候存在二义性),编译器自动生成的那个也叫默认构造函数

不传参数就能调用的构造函数,就叫默认构造函数。

 

例:读代码,为什么没找到默认构造函数

class Stack
	{
	public:

	Stack(int capacity)
	{
		_a = (int *)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail\n");
			exit(-1);
		}
	
		_capacity = capacity;
		_top = 0;

		cout << "Stack(int capacity = 4)" << endl;
	}
	
	
private:
	int *_a;
	int _top;
	int _capacity;
	
};


class MyQueue
{
public:


private:
	Stack _pushST;
	Stack _popST;
	size_t _size = 0;//这里不是初始化,而是缺省值。
};


int main()
{
	MyQueue q;

	return 0;
}

a4212c1bf9c64cdea101cb1bc6889166.png

 

_size给了缺省值,没问题。问题出在_pushST和_popST。

 

MyQueue类没有自己写构造函数,编译器自动生成它的默认构造。构造函数对内置类型不处理,可以在定义变量的时候给缺省值。自定义类型去找它的默认构造函数,Stck没有默认构造。

 

Stack的构造函数我们自己写了,就没有编译器定义的构造函数

fc38700a62fd480abedff926c287f531.png

 也没有无参或全缺省。所以找不到Stack的默认构造函数。                                             

 

解决方法:

1.在stack里定义无参或全缺省的默认构造

2.stack用自动生成的默认构造

如果Stack也不写构造函数:

 

方法一:

8f3e8830c32e405780ad564d286bcb47.png

 

方法二:

ed5478acae87499db42581256d6f5150.png

 都能编译过去

 

 

 

三、析构函数

和构造函数功能相反,不是销毁对象本身(局部变量的空间释放由编译器完成),而是完成对象中的资源清理(清理对象里动态开辟的空间,和Destroy功能相似)。生命周期结束后,对象会自动调用构造函数。

 

特性:

  1. 函数名:~类名
  2. 没有参数,也没有返回值类型
  3. 不能重载
  4. 对象生命周期结束的时候自动调用
  5. 自动生成的析构函数对内置类型不处理,对自定义类型调用它的析构函数

 

生命周期的影响因素(作用域不一定影响生命周期,比如命名空间域不影响生命周期)

1.局部对象,函数结束就销毁

2.全局对象,main函数结束就销毁

3.malloc出来的,要手动free才销毁,或程序正常运行结束会自动销毁

4.类对象生命周期结束时,自动调用析构函数,会清理资源。

 

出生命周期自动调用析构:

class Stack
	{
	public:

	Stack(int capacity = 4)
	{
		_a = (int *)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail\n");
			exit(-1);
		}
	
		_capacity = capacity;
		_top = 0;

		cout << "Stack(int capacity = 4)" << endl;
	}
	
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity=0;
		cout << "~Stack" << endl;
	}
	
	void Push(int x)
	{
			//扩容
			_a[_top++] = x;
	}
	
private:
	int *_a;
	int _top;
	int _capacity;
	
};


class MyQueue
{
public:

private:
	Stack _pushST;
	Stack _popST;
};

void Func()
{
	MyQueue q;
}

int main()
{
	Func();

	return 0;
}

d366b935713a4b1b985f264c8e0d683b.png

 

什么时候需要自己写析构函数?

 

日期类都是内置类型,没动态开辟空间,也不用资源清理,生命周期到了由编译器销毁回收,所以不用我们自己写析构函数

而栈需要释放动态开辟的空间且是内置类型的,它的析构函数需要我们自己写。因为编译器生成的默认析构函数对内置类型不处理(比如栈里面malloc出来的空间,用int*接收地址)。

MyQueue的析构函数也要释放资源,但是因为都是自定义类型,调用自定义类型:栈  的析构函数,不用自己写析构函数。

成员是指针类型或迭代器,无论是内置类型还是自定义类型的,默认生成的析构函数不会自动处理。

 

 

生命周期相同的类对象谁先析构?

后定义的,先析构。

 

例:

****设已经有A,B,C,D,4个类的定义,程序中A,B,C,D析构函数调用顺序为?( )


C c;

int main()

{

	A a;

	B b;

	static D d;

  return 0;

}

分析:1、类的析构函数调用一般按照构造函数调用的相反顺序进行调用,但是要注意static对象的存在,因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象

       2、全局对象先于局部对象进行构造

       3、局部对象按照出现的顺序进行构造,无论是否为static

       4、所以构造的顺序为 c a b d

       5、析构的顺序按照构造的相反顺序析构,只需注意static改变对象的生命周期之后,会放在局部对象之后进行析构

       6、因此析构顺序为B A D C

 

 

四、拷贝构造函数

 

特性

1.拷贝构造是构造函数的重载,没有返回值,函数名是类名

2.拷贝构造的参数只有一个,必须是类对象的引用,使用传值编译器会报错(引发无穷递归)

 

以Date类的拷贝构造为例:

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

	Date(const Date& d)//【const哦】防止写反,把d里面的成员给改了 比如:d._day=_day;
	{
		_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(2022,10,16);//构造
	
	//拷贝一份d1
	Date d2(d1);//拷贝构造——拷贝初始化

	d1.Print();
	d2.Print();

	return 0;
}

33b4bb124eac4016a4d4dd0edde50ce5.png

  

为什么会引发无穷递归?

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

	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day= d._day;

		cout << "调用拷贝构造" << endl;
	}


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

void Func1(Date d)//形参是实参的拷贝
{
	cout << "func1 传值" << endl;
}

void Func2(Date& d)//形参是实参的引用
{
	cout << "func2 传引用" << endl;
}

int main()
{
	Date d1(2022,10,16);//构造
	Func1(d1);

	Func2(d1);

	return 0;
}

8b389751f6eb4ca1913856500acde8f9.png

 

因为传 类的值 的时候会调用拷贝构造。

 

自定义类型的拷贝比较复杂,我们需要自己写一个函数来完成拷贝,这个函数就是拷贝构造。 

 

3.如果没有自己定义拷贝构造,编译器会生成默认的拷贝构造函数。

 

注意:编译器生成的拷贝构造  内置类型是按字节拷贝的(浅拷贝),自定义类型调用它的拷贝构造函数。

 

编译器自动生成对内置类型的浅拷贝,对有的类来说并不好用,比如浅拷贝指针就不行。

比如拷贝栈:都是内置类型,自动生成的浅拷贝连指针存储地址都给拷过去了。

其他的拷贝过来了,但是指针存储地址也拷贝的一模一样,指向同一块空间。在生命周期结束时会调用析构函数,第一次释放成功,第二次释放出错,因为指针还指向已经释放的空间,同一块空间不能释放两次,越界了。

还有一个问题就是,拷贝过来的插入删除会影响原来的,因为俩指针指向同一块空间。按理说拷贝只是拷贝,是可以另外操作而不影响原数据的。

 

默认生成的拷贝构造不能完成我们的要求。还得自己写,深拷贝。

代码:

//拷贝构造函数
	Stack(const Stack& st)
	{
		_a = (int *)malloc(sizeof(int)*st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail\n");
			exit(-1);
		}

		memcpy(_a, st._a, sizeof(st._top));
		_top = st._top;
		_capacity = st._capacity;
	}

 

总结:

需要自己写析构函数的类,都需要写深拷贝的拷贝构造。

不需要自己写析构函数的类,用默认生成的浅拷贝拷贝构造就够用。

 

 

 

五、运算符重载

为了增强代码可读性和让自定义对象也能用运算符,c++引入运算符重载。(函数重载和运算符重载不是一个东西)。在学习赋值重载之前要先学一下运算符重载。

例:

37818368a7954451a4785dd2c500f03f.png

这两个函数的效果是一样的。运算符重载真正的意义是代码可读性。别人也能读懂符号是什么意思,调用函数的时候也简单。

 

函数名:关键字operator后加需要重载的运算符符号。

 

函数原型:返回值类型 operator  操作符(参数列表)

 

参数:运算符重载函数是针对自定义类型的,所以必须有一个类类型的参数。(看函数定义在哪,定义在类里面自动就有一个隐藏的this指针,定义在类外,就一定要有一个类类型的参数)

 

注意:

不能创建一个新的运算符(比如:operator @)

以下五个运算符不能重载: .*(很少用)  、 :: sizeof  ? :   、.

 

练习:

1.公有成员变量,在类外面定义运算符重载

class Date
{
public:

	Date(int year = 1, int month = 1, int day = 1)//构造
	{
		_year = year;
		_month = month;
		_day = day;
	}


	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

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

//公共数据可以在外面访问
bool operator==(const Date& d1, const Date& d2)//传引用。传值会调用拷贝构造
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}


int main()
{
	Date d1(2022, 10, 16);//构造
	Date d2(2022, 10, 19);

	cout << (d1 == d2) << endl;//<< 的优先级高于==。所以带括号哦。
	
	operator==(d1, d2);//也可以显示调用,但一般不这么用。上一种可读性很好。

	return 0;
}

 

2.私有成员变量的运算符重载:

方法一:在类里面写get函数,在类外面定义运算符重载。

class Date
{
public:

	Date(int year = 1, int month = 1, int day = 1)//构造
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(Date& d)//拷贝构造
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		cout << "调用拷贝构造" << endl;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	int GetYear()
	{
		return _year;
	}

	int GetMonth()
	{
		return _month;
	}

	int GetDay()
	{
		return _day;
	}
private:
	int _year;
	int _month;
	int _day;
};


bool operator==( Date& d1, Date& d2)//传引用
{
	return d1.GetYear()== d2.GetYear()
		&& d1.GetMonth() == d2.GetMonth()
		&& d1.GetDay() == d2.GetDay();
}


int main()
{
	Date d1(2022, 10, 16);//构造
	Date d2(2022, 10, 19);


	cout << (d1 == d2) << endl;//<< 的优先级高于==。所以带括号哦。
	//
	//operator==(d1, d2);//也可以显示调用,但一般不这么用。上一种可读性很好。

	return 0;
}

 

方法二:把运算符重载函数写到类里面。

运算符有几个操作数,就有几个参数。

两个操作数:显示写一个参数。实际是两个参数,因为有一个隐藏的this指针。

class Date
{
public:

	Date(int year = 1, int month = 1, int day = 1)//构造
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(Date& d)//拷贝构造
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		cout << "调用拷贝构造" << endl;
	}


	bool operator== (const Date& d)//运算符有几个操作数,就有几个参数,包含this指针。
	{
		return _year == d._day
			&& _month == d._month
			&& _day == d._day;
	}

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


int main()
{
	Date d1(2022, 10, 16);//构造
	Date d2(2022, 10, 19);

	cout << (d1 == d2) << endl;//<< 的优先级高于==。所以带括号哦。
	
	d1.operator==(d2);//也可以这样,但没必要

	return 0;
}

 

 

练习:Date类


1.判段d1==d2(等于重载)

	bool operator== (const Date& d)
	{
		return _year == d._day
			&& _month == d._month
			&& _day == d._day;
	}

 

2.d1>d2

//判断d1是否大于d2
	bool 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;
		}
		else
		{
			return false;
		}
	}

 

3.">="  "<"  "<="

//判断d1是否大于等于d2
	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);
	}

 

4.+和+=

//+=
Date& operator +=(int day)
{
	if (day < 0)
	{
		return *this -= day;
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;

		if (_month == 13)
		{
			_month = 1;
			_year++;
		}
	}

	return *this;//对象出了函数不销毁,引用做返回值,可以省去调用拷贝构造函数
}

//+
Date operator+(int day)
{
	Date ret(*this);
	ret += (day);
	return ret;//只能传值返回,ret出了函数会销毁
}

 

5.-和-=

Date& operator -= (int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day<=0)//这个月不够减,往前借。记得一定要有=,10.23减23是10.0。没有十月零号。
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);

	}

	return *this;

}
Date operator - (int day)
{
	Date ret(*this);//拷贝构造
	ret -= day;

	return ret;
}

 

d2-1000,d2本身不变,想要打印出结果有两种方法

int main()
{
	Date d1(2022, 10, 23);
	d1 -= 1000;
	d1.Print();

	Date d2(d1);//拷贝构造
	
	//d2-1000,d2本身不变,想要打印出结果有两种方法
	
	//1.创建新对象接收返回值
	Date d3=d2 - 1000;
	Date d4 (d2 - 1000);//都是拷贝构造。这两种方法一样。
	d3.Print();
	d4.Print();


	//2.返回值是一个对象
	(d2 - 1000).Print();
	return 0;
}

2c2359ed97ca40ad90d7d6a94a262e7c.png

 

6.完善构造函数,检查日期是否合法

Date(int year = 1, int month = 1, int day = 1)//构造
	{
		_year = year;
		_month = month;
		_day = day;

		//检查日期是否合法
		if (!(year >= 1)
			&& (month >= 1 && month <= 12)
			&& (day >= 1 && day <= GetMonthDay(year, month)))
		{
			cout << "非法日期" << endl;
		}
	}

 

7.前置++和后置++

对于内置类型,前置后置差不多,而对于自定义类型用前置比较好。

自定义类型的前置没有拷贝的发生,后置调用了两次拷贝构造。

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

//后置
Date operator++(int)//多一个参数是为了跟前置区分,构成函数重载
{
	Date tmp(*this);//拷贝构造
	*this += 1;
	return tmp;//对象传值返回的时候调拷贝构造
}

 

注意单目运算符重载需要注意是前缀和后缀。

前缀单目运算符,操作数在运算符左边。后缀单目运算符,操作数在运算符右边。

前置++是前缀单目运算符,后置++是后缀单目运算符。

调用前置++: ++d 或 d.operator++()

调用后置++:d++ 或 d.operator++(0)

 

 

六、赋值重载

注意运算符重载可以声明定义在类外或者类内,但是赋值运算符的重载只能定义在类里。

因为赋值运算符在类中不显式实现时,编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的,就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数。

区分赋值重载和拷贝构造

都在拷贝,赋值重载也是一个默认成员函数。

	//= 赋值重载

	//d1=d2 赋值重载需要注意参数顺序(谁赋值给谁),operator第一个参数是操作符左边的,第二个参数是右边的。这里第一个参数是d1的this指针,第二个参数是d2
	
	//d1=d2=d3,连续赋值时,d2=d3后函数需要一个返回值,再赋值给d1
	Date& operator =(const Date& d)//也可以直接传 Date d,只是会调用拷贝构造。返回值用引用返回,避免拷贝构造。
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		return *this;

		//能不能返回d?    return  d;//权限会放大。除非把返回值变成const Date&。但这样就不能修改返回值了
	}
int main()
{
	Date d1;
	Date d2(2022, 10, 22);

	Date d3(d1);//拷贝构造,一个已经初始化的 拷贝初始化一个马上要创建的对象
	Date d4 = d2;//还是拷贝构造,d4是一个刚创建的对象,由d2拷贝初始化

	d1 = d2;//赋值重载。已经初始化的两个对象间的拷贝。

	return 0;
}

 

构造函数和析构函数类似,对内置类型不处理,自定义类型调用它们的构造函数和析构函数。

拷贝构造和赋值重载类似,内置类型处理(值拷贝),自定义类型调用他们的拷贝构造和赋值重载。

 

一般情况编译器默认生成的就够用。

对于要写析构函数的类,就要自己写赋值重载。因为拷贝的时候指针不能直接值拷贝。详细原因和拷贝构造那里一样。

 

用栈举例:存在内存泄漏(Stack1 和 Stack2都是已经初始化的,malloc开辟了两块空间,直接拷贝指针,两个指针指向同一空间,另一块空间没有释放,造成了内存泄漏),同一块空间析构两次就崩溃了。

Stack& operator=(const Stack& st)
	{	
		if (this != &st)//取地址
        //例如:st1=st;//调用赋值重载
        //地址必须不相等的原因:深拷贝要先free释放掉st1开辟的空间,如何新开空间,再复制内容。如果&st1==&st说明两个地址指向同一个Stack对象,释放掉st1的资源后,从st里拿到内容进行拷贝会形成越界访问。
		{
			free(_a);
			_a = (int*)malloc(sizeof(int)* st._capacity);
			if (_a == nullptr)
			{
				perror("malloc fail\n");
				exit(-1);
			}

			memcpy(_a, st._a, sizeof(st._a));
			_top = st._top;
			_capacity = st._capacity;

		}
		return *this;
	}

 

 

 

七、取地址重载

这两个平时我们不用自己写,编译器自动生成的就能用。

	Date* operator&()
	{
		return this;
	}


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

 

八、const成员

 指针和引用的传递不能放大权限。

const对象的this指针类型为(以日期类为例): const Date*const this

 

例1:void Print(){}函数中的this指针是Date* const this类型。

const对象调非const成员函数,this指针会权限放大,编译不通过。

例2:

int operator-(const Date& d)
{
	Date max = *this;
	Date min = d;
	int flag = 1;

	
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;//d2大,d1小,d1-d2就是负的。一会++到等于max的时候,给n乘个-
	}
	int n = 0;
	while (min != max) 
	{
		++n;
		++min;
	}

	return n * flag;
}

if条件如果改成d>*this编译不通过。因为显示调用的话是d.operator>(*this)

d是const对象,d的this指针也是const的,>的运算符重载没用const修饰,就是this指针的权限放大。

 

用const修饰的成员函数就是const成员函数

如:void Print ()const

这个const修饰的是隐含的this指针,加上后不能对this指针指向对象的成员变量进行修改。

this指针的类型为const Date*const this

注意:如果声明和定义分离编译,const在声明和定义都要加。  

 

const对象不能调用非const成员函数(this指针,权限放大)。

非const对象可以调用const成员函数。this指针权限缩小。凡是成员函数内部不改变*this对象成员变量的,都应该用const修饰成员函数。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值