类和对象(中)

文章目录

  • 1.类的6个默认成员函数
    • 1.1构造函数
      • 1.概念
      • 2.特性
      • 3.补丁
      • 4.总结
    • 1.2析构函数
      • 1.概念
      • 2.特性
    • 1.3拷贝构造函数
      • 1.概念
      • 2.特性
    • 1.4练习题
      • 1.练习一
      • 2.练习二
      • 3.练习三
      • 4.练习四
      • 5.练习五
    • 1.5赋值重载函数
      • 1.特性
      • 2.赋值运算符重载
        • 重载格式
        • 代码演示
  • 2.日期类Date的实现
    • 2.1Date.h
    • 2.2Date.cpp
    • 2.3test.cpp
    • 2.4不同复用性能比较
  • 3.const成员函数
    • 3.1报错样例
    • 3.2回顾权限的放大与缩小
    • 3.3代码演示
  • 4.取地址重载函数

【本节目标】

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

1.类的6个默认成员函数

空类:一个类中什么成员都没有
空类并不是什么都没有,任何类在什么都不写时,编译器会自动生成6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。
图片来自博主:柠檬叶子C
类和对象(中)_第1张图片

1.1构造函数

1.概念

Date类可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数,名字与类名相同,创建类 类型 对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

下面这个代码:自己编写初始化函数

class Date
{
public:
void Init(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.Init(2022, 7, 5);
d1.Print();
Date d2;
d2.Init(2022, 7, 6);
d2.Print();
return 0;
}

2.特性

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

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载。无参构造函数、全缺省构造函数、编译器默认生成的构造函数,都可以认为是默认构造函数。默认构造函数只能有一个,语法上两个可以同时存在,但是如果有对象定义去调用就会报错,不知道调用哪个函数。
不给参数调用无参构造函数  给参数调用带参构造函数
class Date
{
 public:
   // 1.无参构造函数
   Date()
    {
        _year = 0;
        _month = 1;
        _day = 1;
    }
   // 2.带参构造函数
   Date(int year, int month, int day)
  {
     _year = year;
     _month = month;
     _day = day;
  }
  void Print() 
  {
        printf("%d-%d-%d\n", _year, _month, _day);
    }
 private:
   int _year;
   int _month;
   int _day;
};
 int main()
{
    通过无参构造函数创建对象时,对象后面不用跟括号,      
    否则就成了函数声明
   Date d1; // 调用无参构造函数
   d1.Print();  0-1-1
   
   Date d2(2023, 5, 4); // 调用带参构造函数
   d2.Print();  2023-5-4
   
   // 声明了d3无参函数,返回一个日期类型的对象
    Date d3();//warning C4930: “Date d3(void)”: 
    未调用原型函数
}
  1. 类中没有显式定义构造函数,C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义,编译器将不再生成。
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;这里没有传参数 所以要调用一个无参构造函数
将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
return 0;
}
  1. d对象调用了编译器生成的默认构造函数,但是d对象的_year/_month/_day依旧是随机值。也就说在这里编译器生成的默认构造函数并没有用?
    解答:C++把类型分成内置类型(基本类型)和自定义类型。
    内置类型:语言提供的数据类型:int/char…
    自定义类型:class/struct/union
1.观察下述代码 思考输出结果
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 d;//输出Time()
return 0;

2.对自定义类型处理,会调用默认构造函数(不用参数就可以调的函数)
#include
using namespace std;

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

private:
    int _a;
};

class Date 
{
private:
    int _year;
    int _month;
    int _day;
    A _aa;   
};

int main(void)
{
    Date d1;
    //输出A()
    return 0;
}
}

3.故意写个带参的默认构造函数,让编译器不默认生成
#include 
using namespace std;
class A 
{
public:
    A(int a) 
    {    
        cout << " A() " << endl;
        _a = 0;
    }
private:
    int _a;
};

class Date 
{
private:
    int _year;
    int _month;
    int _day;
    A _aa;
};

int main(void)
{
    Date d1;
    无法引用"Date"的默认构造函数--它是已删除的函数
    return 0;
}

4.不写,让编译器默认生成
#include
using namespace std;

class A 
{
private:
    int _a;
};

class Date 
{
private:
    int _year;
    int _month;
    int _day;

    A _aa;
};

int main(void)
{
    Date d1;
    Date定义了一个类d1 此时编译器自动调用Date的构造函数去初始化他的成员 
    但是_year\_month\_day都是内置类型 编译器对他们不做处理
    _aa是自定义类型所以会对它进行初始化 此时就去调用类A的构造函数
    而用户并没有自己编写 所以编译器会默认生成一个构造函数对其成员进行初始化
    同样的  _a是内置类型 编译器对其不做处理
    但是类A中没有自定义类型 所以有一下调试结果
    return 0;
}

类和对象(中)_第2张图片

3.补丁

C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值(注意:此处是声明 给了一个缺省值 并非是初始化)。

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 = 1970;
int _month = 1;
int _day = 1;

Time _t;// 自定义类型
};

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

调试结果
运行结果:Time()
类和对象(中)_第3张图片
由此可以看到 因为这个补丁 此时想要初始化的已经初始化了

4.总结

综上所述,我们可以这么去认识构造函数:

class A
{
public:
	A()
	{
		cout << "A()" << endl;
		X = 0;
		Y = 0;
		Z = 0;
	}
private:
	int X;
	int Y;
	int Z;
};

class B
{
private:
	
	int x = 1;
	int y = 1;
	int z = 1;

	A _a;
};

int main()
{
	B b;
	return 0;
}

调试结果

类和对象(中)_第4张图片

运行结果

类和对象(中)_第5张图片

理解代码

类和对象(中)_第6张图片

1.2析构函数

1.概念

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

2.特性

析构函数是特殊的成员函数。

  1. 析构函数名在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
  5. 编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
  6. 类中没有申请资源时,析构函数可以不写,使用编译器生成的默认析构函数如:Date类
    有资源申请时,一定要写,否则会造成资源泄漏,如Stack类。
  7. 构造函数是特殊的成员函数,主要任务是初始化,而不是开空间;
    析构函数主要任务是清理,而不是做对象销毁的工作。(局部对象销毁工作是由编译器完成的)
class Time
{
public:
~Time()
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}

类和对象(中)_第7张图片

1.3拷贝构造函数

1.概念

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

2.特性

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
  3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
  4. 在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请
    时,则拷贝构造函数是一定要写的
  5. 拷贝构造函数典型调用场景:
    使用已存在对象创建新对象
    函数参数类型为类类型对象
    函数返回值类型为类类型对象

特性2代码演示

class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
  Date(const Date& d)  // 正确写法
  Date(const Date d)  // 错误写法:编译报错,会引发无穷递归
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}

类和对象(中)_第8张图片

特性3

class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time(const Time& t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
cout << "Time::Time(const Time&)" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
1.用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
2.Date类没有显式定义拷贝构造函数,编译器会给Date类生成一个默认的拷贝构造函数

特性4

typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_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);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);
return 0;
}

类和对象(中)_第9张图片

特性5

class Date
{
public:
Date(int year, int minute, int day)
{
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date& d)
{
cout << "Date(const Date& d):" << this << endl;
}
~Date()
{
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
Date Test(Date d)
{
Date temp(d);
return temp;
}
int main()
{
Date d1(2022,1,13);
Test(d1);
return 0;
}

类和对象(中)_第10张图片
类和对象(中)_第11张图片

vs2022运行结果把Test函数中temp返回时使用的拷贝构造函数(这个函数是用来构造临时变量的)以及销毁这个临时变量的析构函数给优化了 所以运行结果只有6行代码

类和对象(中)_第12张图片

1.4练习题

1.练习一

#include
using namespace std;
class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)->" << _a << endl;
	}
	
	~A()
	{
		cout << "~A()->" << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A aa1(1);
	A aa2(2);
	return 0;
}

类和对象(中)_第13张图片

先调用的后析构
后调用的先析构

2.练习二

#include
using namespace std;
class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)->" << _a << endl;
	}
	
	~A()
	{
		cout << "~A()->" << _a << endl;
	}
private:
	int _a;
};
A aa0(0);
int main()
{
	static A aa3(3);
	A aa1(1);
	A aa2(2);
	static A aa4(4);

	return 0;
}

类和对象(中)_第14张图片

初始化顺序(即构造函数的调用顺序):
全局变量aa0(0)
局部变量aa3(3) aa1(1) aa2(2) aa4(4)
析构顺序:
1.栈帧先销毁
2.main函数结束后
3.静态局部变量随之
4.全局变量最后
局部变量aa2(2) aa1(1)
静态局部变量aa4(4) aa3(3)
全局变量aa0(0)

3.练习三

#include
using namespace std;
class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)->" << _a << endl;
	}
	
	~A()
	{
		cout << "~A()->" << _a << endl;
	}
private:
	int _a;
};
A aa0(0);
void f()
{
	static A aa3(3);
	A aa1(1);
	A aa2(2);
	static A aa4(4);
}
int main()
{
	f();
	f();

	return 0;
}

类和对象(中)_第15张图片

1.程序执行—aa0创建(调用构造)–main函数建立栈帧–调用f()–f函数建立栈帧
2.aa3、aa1、aa2、aa4创建–f函数调用结束–f函数栈帧销毁
3.aa2析构–aa1析构
4.二次调用f()函数–f函数建立栈帧aa1、aa2创建–f函数调用结束–f函数栈帧销毁
5.main函数调用结束–main函数栈帧销毁
6. 局部静态变量aa4、aa3析构–全局变量aa0析构–静态变量、全局变量销毁–程序结束
7.如果再定义一个全局静态变量二者谁在程序前谁先构造,销毁相反。

在这里插入图片描述
类和对象(中)_第16张图片

4.练习四

#include
using namespace std;
class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)->" << _a << endl;
	}

	A(const A& aa)
	{
		_a = aa._a;
		cout << "A(const A& aa)->" << _a << endl;
	}
	
	~A()
	{
		cout << "~A()->" << _a << endl;
	}
private:
	int _a;
};
void func1(A aa)
{

}
void func2(A& aa)
{

}
int main()
{
	A aa1(1);
	func1(aa1);
	func2(aa1);
	return 0;
}

类和对象(中)_第17张图片

func2函数调不调用运行结果一样
说明传引用时没有调用拷贝构造函数
这里不明白的再看一下拷贝构造函数的讲解

5.练习五

#include
using namespace std;
class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)->" << _a << endl;
	}

	A(const A& aa)
	{
		_a = aa._a;
		cout << "A(const A& aa)->" << _a << endl;
	}
	
	~A()
	{
		cout << "~A()->" << _a << endl;
	}
private:
	int _a;
};
A func3()
{
	static A aa;
	return aa;
}

A& func4()
{
	static A aa;
	return aa;
}

int main()
{
	func3();
	cout << endl << endl;
	func4();
	return 0;
}

类和对象(中)_第18张图片

稍微修改一下函数

类和对象(中)_第19张图片

修改函数后的运行结果

类和对象(中)_第20张图片
有的同学蒙圈了,到底是怎么回事呢?好烦…呜呜呜…
类和对象(中)_第21张图片
一起来看看吧!!!
只调用func3:
类和对象(中)_第22张图片

进入func3函数—aa创建—调用构造函数—返回aa是值返回----要创建一个临时变量aa‘—调用拷贝构造函数创建aa’—func3函数调用结束—调用析构函数清理临时变量aa’–main函数调用结束—调用析构函数清理静态变量aa

只调用func3:
在这里插入图片描述

进入func4函数—aa创建—调用构造函数—返回的是引用—不调用拷贝构造函数—main函数结束—调用析构函数清理静态变量aa

此时,func3和func4函数的调用就不难理解了吧!嘿嘿…
类和对象(中)_第23张图片

1.5赋值重载函数

1.特性

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数。
1.具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名:关键字operator 运算符符号
函数原型:返回值类型 operator操作符(参数列表)
2.不能通过连接其他符号来创建新的操作符:operator@
3.重载操作符必须有一个类类型参数
4.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
5.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
6. .* :: sizeof ?: . 注意以上5个运算符不能重载。

判断相等

#include
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//bool operator==(Date x1, Date x2)
	//bool operator==(const Date& x1, const Date& x2)
	bool operator==(const Date& x)
	{
		return _year == x._year
			&& _month == x._month
			&& _day == x._day;
	}
	//通过在类外调用此函数可以访问私有成员
	//int GetYear()
	//{
	//	return _year;
	//}
private:
	int _year;
	int _month;
	int _day;
};
//内置类型直接使用运算符运算 编译器知道如何运算
//自定义类型无法直接使用运算法 编译器不知道如何运算 
//需要自己实现运算符重载
int main()
{
	Date d1(2023, 5, 30);
	Date d2(2023, 2, 23); 
	cout << d1.operator==(d2) << endl; // <==> d1.operator==(&d1, d2)
	cout << (d1 == d2) << endl;
    return 0;
}

+= 和 +

class Date
{
public:
	//判断闰年
	int IsLeapYear(int year)
	{
		if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
			return 1;
	}
	//获得每月天数
	int GetMonthDay(int year, int month)
	{
		static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (month == 2 && (IsLeapYear(year)))
			return 29;
		else
			return days[month];
	}
	//构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//==重载
	bool operator==(const Date& x)
	{
		return _year == x._year
			&& _month == x._month
			&& _day == x._day;
	}
	//+=重载
    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 operator+(int day)
	{
		Date ret(*this);//用d1拷贝构造ret
		ret._day += day;
		while (ret._day > GetMonthDay(ret._year, ret._month))
		{
			ret._day -= GetMonthDay(ret._year, ret._month);
			++ret._month;
			if (ret._month == 13)
			{
				ret._month = 1;
				ret._year++;
			}
		}

		return ret;
	}

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

int main()
{
	Date d1(2022, 7, 23);
	Date d2(2022, 7, 24);
	
	//==
	d1 == d2;
	
	//+=
	d1 += 50;
	
	// +
	Date ret = d1 + 50;//Date ret(d1 + 50);  ==>拷贝构造
	
    return 0;
}
/*bool operator<(const Date& x);
bool operator>(const Date& x);
bool operator>=(const Date& x);
bool operator<=(const Date& x);
bool operator!=(const Date& x);*/

2.赋值运算符重载

重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :符合连续赋值的含义
  • 赋值运算符只能重载成类的成员函数不能重载成全局函数。原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
  • 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

代码演示

观察参数类型、返回值类型

Date.h
#pragma once
#include
using namespace std;

class Date
{
public:
	//构造函数频繁调用 放在类里作为inline
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_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;
	}
	//连续赋值 i = j = k 
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
void TestDate1()
{
	Date d1(2022, 7, 24);
	Date d2(d1);
	Date d3(2022, 8, 24);
	d2 = d1 = d3;//==>d.operator=(&d1,d3);
	d2 = d2;
}
int main()
{
	TestDate1();
	return 0;
}

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

类和对象(中)_第24张图片

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

类和对象(中)_第25张图片

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?
以下代码将崩溃

类和对象(中)_第26张图片
类和对象(中)_第27张图片

对与下面这个类,默认生成的函数就十分方便

类和对象(中)_第28张图片

2.日期类Date的实现

点击 Cpp日期类最新版 即可跳转码云获取代码。(有兴趣的小伙伴也可以关注一下俺的码云呦~

2.1Date.h

类和对象(中)_第29张图片

2.2Date.cpp

类和对象(中)_第30张图片

2.3test.cpp

类和对象(中)_第31张图片

2.4不同复用性能比较

类和对象(中)_第32张图片

+运算里有拷贝构造,+=去复用+时拷贝构造次数变多,性能下降。相比之下,+=里面没有拷贝构造,+去复用+=时,没有多余的拷贝构造,性能提升。

3.const成员函数

3.1报错样例

再讲解这一部分之前,uu们先来看两个报错吧!
类和对象(中)_第33张图片

3.2回顾权限的放大与缩小

对于权限放大和缩小在理解一下吧!!!
类和对象(中)_第34张图片

了解了这一部分,可以进一步修改日期类的函数,当然前提是可以加。

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

3.3代码演示

#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Print() const
	{
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
void Test()
{
	Date d1(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	d2.Print();
}
int main()
{
	Test();
	return 0;
}

类和对象(中)_第35张图片

4.取地址重载函数

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如 return nullptr;不让别人获取你哒地址。

class Date
{
public :

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

Date* operator&()
{
return this ;
}

const Date* operator&() const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
int main(void)
{
	Date d1(2022, 2, 2);
	cout << &d1 << endl;   // 取出d1的地址
 
	const Date d2(2022, 1, 1);
	cout << &d2 << endl;   // 取出d2的地址
 
	return 0;
}

你可能感兴趣的:(遣返回家的C家家,c++,算法,开发语言)