类和对象(下)

嘿嘿,家人们,今天咱们进入类和对象(下),好啦,废话多不讲,开干!


目录

1:再谈构造函数

1.1:构造函数体赋值

1.2:初始化列表

1.2.1:概念

1.2.1.1:代码1

1.2.1.2:代码2

1.2.1.3:代码3

1.2.2:注意事项

1.2.2.1:代码1(仅含const成员变量)

1.2.2.2:代码2(仅含引用成员变量)

1.2.2.3:代码3(仅含自定义类型成员且该自定义类型无默认构造函数)

1.2.2.3.1:无默认构造

1.2.2.3.2:有默认构造

1.2.2.4:总代码

1.2.3:初始化列表的细节知识

1.2.3.1:细节知识1(单参数构造函数支持隐式类型的转换)

1.2.3.1.1:代码1

1.2.3.1.2:代码2

1.2.3.1.3:代码3

1.2.3.2:细节知识2(多参数构造函数也支持隐式类型转换)

1.2.3.3:细节知识3

1.3:explicit关键字

1.3.1:代码1(无explicit关键字)

1.3.2:代码2(有explicit关键字)

2:static成员

2.1:概念

2.1.1:代码1(在类里面初始化)

2.1.2:代码2(在类外面初始化)

2.2:特性

2.2.1:特性1:静态成员为被所有对象所共享,不属于某一个具体的对象,静态成员被存储在静态区.

2.2.2:特性2:静态成员变量必须在类外面定义,定义时不添加static关键字,类中只是声明

2.2.3:特性3:类的静态成员可以使用类名::静态成员 或者 对象.静态成员来进行访问.

2.2.4:静态的成员函数没有隐藏的this指针,不能访问任何的非静态成员

2.2.5:静态的成员函数不能够调用非静态的成员函数

2.2.6:非静态的成员函数可以调用类的静态成员函数

2.2.7:静态成员也是类的成员,也受访问限定符的限制.

3:友元

3.1:友元函数

3.1.1:代码1

3.1.2:代码2(<<与>>的正确重载方式)

3.1.2:代码3(<<与>>重载成类的成员函数)

3.2:友元类

3.2.1:代码1(未进行友元声明)

3.2.2:代码2(进行友元声明)

3.2.3:特性

3.2.3.1:特性1:友元关系是单向的,不具有交换性.

3.2.3.2:特性2:友元关系不能传递

3.2.3.3:特性3:友元关系不能够继承

4.内部类

4.1:概念

4.2:特性

4.2.1:内部类可以定义在外部类的public,protected,private都是可以的.

4.2.2:内部类可以直接访问外部类的static成员,不需要外部类的对象/类名

4.2.3:sizeof(外部类) = 外部类,与内部类没有任何关系

4.2.3.1:代码1(无内部类)

4.2.3.2:代码2(有内部类)

4.2.4:内部类受到外部类类域的影响

5:匿名对象

6:拷贝对象时编译器的一些优化

6.1:代码1

6.2:代码2

6.3:代码3

6.4:代码4

6.5:代码5


 

1:再谈构造函数

在类和对象(中)呢,我们学习到了构造函数,这里博主带着uu们简单回顾下.

构造函数是一个特殊的成员函数,主要完成对象的初始化工作,构造函数虽然叫构造,但是构造的函数并不是开辟内存空间创建对象,而是完成的对象的初始化工作.并且有如下特征

  • 函数名与类名相同.
  • 无返回值.
  • 对象实例化的时候,编译器会自动调用构造函数
  • 构造函数可以对其进行函数重载(譬如拷贝构造函数).
  • 当用户不写构造函数时,编译器会帮助我们生成一个默认的构造函数
  • 无参构造函数,全缺省构造函数,编译器自动生成的构造函数不需要用户去进行传参的构造函数都被称为默认的构造函数.
  • 默认的构造函数对于内置类型不做处理,对于自定义类型会调用其对应的构造函数.

1.1:构造函数体赋值

在创建对象的时候,编译器通过调用构造函数,给对象中的各个成员变量一个合适的初始值.

我们来看下面这段代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

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(2023, 6, 9);
	d1.Print();
	return 0;
}

上述代码中,虽然调用了构造函数,对象中有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋值,而不能将其称为初始化.因为初始化只能初始化一次,而构造函数体内可以进行多次赋值.

 那么有的uu就在想,既然构造函数里面的函数体是进行赋值的,那么该如何对对象进行初始化呢,这里就要谈到初始化列表了

1.2:初始化列表

1.2.1:概念

初始化列表:以一个冒号开始,接着是以一个逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或者表达式.

了解了其基本概念后,我们来看几段代码

1.2.1.1:代码1
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <sstream>  
#include <vector> 
using namespace std;

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(2023, 6, 9);
	d1.Print();
	return 0;
}

1.2.1.2:代码2
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	//初始化列表是每个成员变量定义初始化的位置
	Date(int year, int month, int day)
		:_year(2022)
		,_month(3)
		,_day(7)
	{
        //赋值修改
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day<< endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 6, 9);
	d1.Print();
	return 0;
}

1.2.1.3:代码3
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <sstream>  
#include <vector> 
using namespace std;

//初始化列表
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 = 2021;
	int _month = 1;
	int _day = 2;
};

int main()
{
	Date d1(2022, 2, 3);
	return 0;
}

总结:初始化列表是每个成员变量定义并且初始化的位置.

1.2.2:注意事项

1.每一个成员变量在初始化列表中只能出现一次(初始化只能够初始化一次)

2.类里面包含如下成员时,必须放在初始化列表位置进行初始化:

  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认的构造函数时)
1.2.2.1:代码1(仅含const成员变量)
#include <iostream>
using namespace std;


class Date
{
public:
	Date(int year, int month)
		//const修饰的成员变量必须在初始化列表进行初始化
		:_year(year)
	{

	}
private:
	const int _year;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}

1.2.2.2:代码2(仅含引用成员变量)
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month)
		//引用成员变量必须在初始化列表进行初始化
		:_month(month)
	{

	}
private:
	int& _month;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}

1.2.2.3:代码3(仅含自定义类型成员且该自定义类型无默认构造函数)
#include <iostream>
using namespace std;
class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
	}
private:
	int _Hour;
};

class Date
{
public:
	Date(int year, int month)
		//无默认构造函数的自定义类型成员变量时必须在初始化列表进行初始化
		:_t1(1)
	{
	}
private:
	//无默认构造函数的自定义成员变量
	Time _t1;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}#include <iostream>
using namespace std;
class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
	}
private:
	int _Hour;
};

class Date
{
public:
	Date(int year, int month)
		//无默认构造函数的自定义类型成员变量时必须在初始化列表进行初始化
		:_t1(1)
	{
	}
private:
	//无默认构造函数的自定义成员变量
	Time _t1;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}

为什么说对于无默认构造函数的自定义类型成员变量时必须在初始化列表进行初始化呢?

因为初始化列表是每个成员变量定义并且初始化的位置.

对于有默认的构造函数的自定义类型成员变量,在对象实例化的时候,会默认走初始化列表,然后通过初始化列表调用其对应的默认构造函数.

我们来简单对比下

1.2.2.3.1:无默认构造
#include <iostream>
using namespace std;
class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
		cout << "Time(int hour)---->无默认构造函数" << endl;
	}
private:
	int _Hour;
};

class Date
{
public:
	Date(int year, int month)
		:_t1(1)
	{
	}
private:
	Time _t1;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}
1.2.2.3.2:有默认构造
#include <iostream>
using namespace std;
class Time
{
public:
	Time(int hour = 1)
	{
		_Hour = hour;
		cout << "Time(int hour)---->有默认构造函数" << endl;
	}
private:
	int _Hour;
};

class Date
{
public:
	Date(int year, int month)
	{
	}
private:
	Time _t1;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}

1.2.2.4:总代码
#include <iostream>
using namespace std;
class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
	}
private:
	int _Hour;
};

class Date
{
public:
	Date(int year, int month)
		//const修饰的成员变量必须在初始化列表进行初始化
		:_year(year)
		//引用成员变量必须在初始化列表进行初始化
		, _month(month)
		//无默认构造函数的自定义类型成员变量时必须在初始化列表进行初始化
		, _t1(1)
		, _p((int*)malloc(sizeof(int) * 10))
	{
		if (nullptr == _p)
		{
			perror("malloc fail");
			exit(-1);
		}
		for (int i = 0; i < 10; i++)
		{
			_p[i] = i;
		}
	}
private:
	const int _year;
	int& _month;
	//无默认构造函数的自定义成员变量
	Time _t1;
	int* _p;
};

int main()
{
	Date d1(2021, 5);
	return 0;
}

总结:能使用初始化列表就尽量使用初始化列表,其他的成员变量既可以在初始化列表里面,也可以在函数体里面.但是建议在初始化列表里面处理,因为不管是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表对其进行初始化.

1.2.3:初始化列表的细节知识

1.2.3.1:细节知识1(单参数构造函数支持隐式类型的转换)
1.2.3.1.1:代码1
#include <iostream>
using namespace std;

class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
	}
	//拷贝构造:参数类型为类引用类型
	Time(const Time& hour)
	{
		cout << "调用" << endl;
	}
private:
	int _Hour;
};

class Date
{
public:
	Date(int year, int month)
		//const修饰的成员变量必须在初始化列表进行初始化
		:_year(year)
		//引用成员变量必须在初始化列表进行初始化
		, _month(month)
		//无默认构造函数的自定义类型成员变量时必须在初始化列表进行初始化
		, _t1(1)
		, _p((int*)malloc(sizeof(int) * 10))
	{
		if (nullptr == _p)
		{
			perror("malloc fail");
			exit(-1);
		}
		for (int i = 0; i < 10; i++)
		{
			_p[i] = i;
		}
	}
private:
	const int _year;
	int& _month;
	//无默认构造函数的自定义成员变量
	Time _t1;
	int* _p;
};

int main()
{
	Date d1(2021, 5);

	Time t1(1);
	/*
	* 等价于Time t2(2);
	单参数构造函数支持隐式类型的转换
	2构造一个临时对象,再进行拷贝构造----->编译器会进行优化,同一个表达式连续步骤的构造,一般会合二为一
	*/
	Time t2 = 2;
	return 0;
}

1.2.3.1.2:代码2
#include <iostream>
using namespace std;

class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
	}
	//拷贝构造:参数类型为类引用类型
	Time(const Time& hour)
	{
		cout << "调用" << endl;
	}
private:
	int _Hour;
};

int main()
{

	Time& t1 = 3;
	return 0;
}

1.2.3.1.3:代码3
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Time
{
public:
	Time(int hour)
	{
		_Hour = hour;
	}
private:
	int _Hour;
};

class Stack
{
public:
	void Push(const Time& t1)
	{
		;
	}
};

int main()
{
	Stack st;
	Time t1(3);
	st.Push(t1);

	st.Push(1);
	return 0;
}
1.2.3.2:细节知识2(多参数构造函数也支持隐式类型转换)
#include <iostream>
using namespace std;

class Time
{
public:
	Time(int hour,int minute)
	{
		_Hour = hour;
	}
	//拷贝构造:参数类型为类引用类型
	Time(const Time& hour)
	{
		cout << "调用" << endl;
	}
private:
	int _Hour;
};

int main()
{
	Time t1 = { 12,25 };
	return 0;
}

1.2.3.3:细节知识3

成员变量在类中声明的次序(声明的顺序就是在内存当中存储的顺序)其实就是初始化列表中的初始化顺序,与其在初始化列表的先后次序无关.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
/*
* 成员变量在类中声明次序其实就是初始化列表中的初始化顺序,与其在初始化列表的先后次序无关
*/
class Date
{
public:
	Date(int year)
		:_year(year)
		, _month(2)
	{}
	void Print()
	{
		cout << _year << "/" << _month << endl;
	}
private:
	//声明顺序决定了初始化时的顺序
	int _month;
	int _year;
};
int main()
{
	Date d1(1);
	d1.Print();
	return 0;
}

1.3:explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无缺省值其余均有缺省值的构造函数,还具有隐式类型转换的作用。但是我们可以通过explicit关键字来禁止构造函数的隐式类型转换.我们来看下面这段代码.

1.3.1:代码1(无explicit关键字)

#include <iostream>

using namespace std;

class Date
{
public:
	Date(int year = 2022, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year = 2022, int month = 1, int day = 1)" << endl;
	}

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

int main()
{
	Date d1 = 2023;
	return 0;
}

1.3.2:代码2(有explicit关键字)

#include <iostream>

using namespace std;

class Date
{
public:
	explicit Date(int year = 2022, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year = 2022, int month = 1, int day = 1)" << endl;
	}

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

int main()
{
	Date d1 = 2023;
	return 0;
}

通过观察上面的两段代码,我们可以清晰地观察到,当使用explicit关键字修饰构造函数,会禁止构造函数的隐式类型转换.

2:static成员

2.1:概念

使用static关键字修饰的类成员被称为类的静态成员,static关键字修饰的成员变量,称之为静态成员变量;用static关键字修饰成员函数,称之为静态成员函数.

PS:静态成员变量一定要在类外面进行初始化.

2.1.1:代码1(在类里面初始化)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
	Date()
		:_count(1)
		,_year(25)
	{
		cout << "Date()" << endl;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	static int _count;
	int _year;
};

int main()
{
	return 0;
}

2.1.2:代码2(在类外面初始化)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
	Date()
		:_year(25)
	{
		cout << "Date()" << endl;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	static int _count;
	int _year;
};

int Date::_count = 1;

int main()
{
	return 0;
}

2.2:特性

2.2.1:特性1:静态成员为被所有对象所共享,不属于某一个具体的对象,静态成员被存储在静态区.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		++_count;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}

	void Print()
	{
		cout <<"_count = "  << _count << endl;
	}
private:
	static int _count;
	int _year;
};
int Date::_count = 0;
int main()
{
	Date d1;
	Date d2;
	d1.Print();
	d2.Print();
	return 0;
}

2.2.2:特性2:静态成员变量必须在类外面定义,定义时不添加static关键字,类中只是声明

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
	Date()
		:_year(25)
	{
		cout << "Date()" << endl;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	static int _count;
	int _year;
};

int Date::_count = 1;

int main()
{
	return 0;
}

2.2.3:特性3:类的静态成员可以使用类名::静态成员 或者 对象.静态成员来进行访问.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		++_count;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}

public:
	static int _count;
	int _year;
};
int Date::_count = 0;
int main()
{
	Date d1;
	Date::_count;
	d1._count;
	return 0;
}

2.2.4:静态的成员函数没有隐藏的this指针,不能访问任何的非静态成员

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		++_count;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}

	static void Print()
	{
		cout << _year << endl;
	}
public:
	static int _count;
	int _year = 2008;
};
int Date::_count = 0;
int main()
{
	Date d1;
	return 0;
}

2.2.5:静态的成员函数不能够调用非静态的成员函数

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		++_count;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}

	static void Print()
	{
		Menu();
	}

    void Menu()
	{
		cout << "这是个菜单" << endl;
	}

public:
	static int _count;
	int _year = 2008;
};
int Date::_count = 0;
int main()
{
	Date d1;
	return 0;
}

2.2.6:非静态的成员函数可以调用类的静态成员函数

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		++_count;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}

	static void Print()
	{
		cout << "hello world" << endl;
	}

    void Menu()
	{
		Print();
	}

public:
	static int _count;
	int _year = 2008;
};
int Date::_count = 0;
int main()
{
	Date d1;
	d1.Menu();
	return 0;
}


2.2.7:静态成员也是类的成员,也受访问限定符的限制.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		++_count;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}

	static void Print()
	{
		cout << "hello world" << endl;
	}

    void Menu()
	{
		Print();
	}

private:
	static int _count;
	int _year = 2008;
};
int Date::_count = 0;
int main()
{
	Date d1;
	d1._count;
	return 0;
}

3:友元

友元提供了一种能够突破封装的方式,有时候会提供便利,但是友元会增加耦合度,破坏封装,因此友元函数不适合过多使用.

3.1:友元函数

在讲友元函数之前呢,我们首先来重载一下流插入(<<)运算符与流提取(>>)运算符.

3.1.1:代码1

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
public:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
};

void operator<<(ostream & _cout,const Date & d)
{
	_cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}
void operator>>(istream& _cin,Date & d)
{
	cout << "请分别输入年 月 日" << endl;
	_cin >> d._year >> d._month >> d._day;
}

int main()
{
	Date d1;
	Date d2;
	cout << d1;
	cin >> d2;
	cout << d2;
	return 0;
}

cout与cin是分别包含在ostream与istream这两个类里面的,因此在重载流插入操作符(<<)与流提取操作符(>>)时,第一个参数的类型必须得分别是ostream 与 istream.通过观察上面的代码,我们可以清晰发现,此时我们重载了<<流插入与>>流提取.但是,这算成功了吗,我们来观察下面这种情况

当我们连续输出时,此时编译以后会报错,这是因为,重载流插入操作符时,上面的代码的函数返回类型是void,如果要达到连续输出的目的的话,那么应该要返回ostream类的引用,>>也是同理的.

3.1.2:代码2(<<与>>的正确重载方式)
 

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
public:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
};

ostream & operator<<(ostream & _cout,const Date & d)
{
	_cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return _cout;
}
istream & operator>>(istream& _cin,Date & d)
{
	cout << "请分别输入年 月 日" << endl;
	_cin >> d._year >> d._month >> d._day;
	return _cin;
}

int main()
{
	Date d1;
	Date d2;
	cout << d1 << d2 << endl;
	cin >> d1 >> d2;
	cout << d1 << d2 << endl;
	return 0;
}

学习了<<与>>的重载方式后,那么很多uu就会有疑惑,<<与>>能不能重载成类的成员函数呢?我们来试试看.

3.1.2:代码3(<<与>>重载成类的成员函数)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
		return _cout;
	}
	istream& operator>>(istream& _cin, Date& d)
	{
		cout << "请分别输入年 月 日" << endl;
		_cin >> d._year >> d._month >> d._day;
		return _cin;
	}
private:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
};

int main()
{
	Date d1;
	Date d2;
	cout << d1 << d2 << endl;
	cin >> d1 >> d2;
	cout << d1 << d2 << endl;
	return 0;
}

通过观察我们可以清晰地发现,<<与>>无法重载成类的成员函数,只能重载成全局函数,但是重载成全局函数的话,就没办法访问类的私有成员变量了,因此这个时候就需要使用友元来帮助.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
	//进行友元声明
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
private:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
	cout << "请分别输入年 月 日" << endl;
	_cin >> d._year >> d._month >> d._day;
	return _cin;
}
int main()
{
	Date d1;
	Date d2;
	cout << d1 << d2 << endl;
	cin >> d1 >> d2;
	cout << d1 << d2 << endl;
	return 0;
}

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部进行声明,声明时需要加freind关键字.

  • 友元函数可以访问类的私有成员与保护成员,但不是类的成员函数.
  • 友元函数不能使用const关键字修饰.
  • 友元函数可以在类定义的任何地方声明,不受访问限定符的限制.
  • 一个函数可以是多个类的友元函数.
  • 友元函数的调用与普通函数的调用原理一致.

3.2:友元类

友元类的所有成员函数都可以是另外一个类的友元函数,都可以访问另外一个类中的非公有成员.

了解了,友元类的基本概念后,那么接下来我们来看几段代码.

3.2.1:代码1(未进行友元声明)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Time
{
	friend class Date;
public:
	Time(int hour,int minute,int second)
		//初始化列表
		:_hour(hour)
		,_minute(minute)
		,_second(second)
	{
	}
private:
	int _hour;
	int _minute;
	int _second;
};


class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
		, _t(12, 12, 12)
	{
		_t._hour = 13;
		_t._minute = 13;
		_t._second = 10;
	}
private:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
	Time _t;
};

int main()
{

	return 0;
}

通过观察,我们可以清晰地看到,当未进行友元声明时,此时无法在Date类里面访问Time类的私有成员变量.

3.2.2:代码2(进行友元声明)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Time
{
	friend class Date;
public:
	Time(int hour,int minute,int second)
		//初始化列表
		:_hour(hour)
		,_minute(minute)
		,_second(second)
	{
	}
private:
	int _hour;
	int _minute;
	int _second;
};


class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
		, _t(12, 12, 12)
	{
		_t._hour = 13;
		_t._minute = 13;
		_t._second = 10;
	}
private:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
	Time _t;
};

int main()
{
	Date d1(2024, 3, 5);
	return 0;
}

3.2.3:特性

3.2.3.1:特性1:友元关系是单向的,不具有交换性.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


class Time
{
	friend class Date;
public:
	Time(int hour, int minute, int second)
		//初始化列表
		:_hour(hour)
		, _minute(minute)
		, _second(second)
		, _d(2023, 2, 1)
	{
		_d._year = 2024;
	}
private:
	int _hour;
	int _minute;
	int _second;
	Date _d;
};

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
		, _t(12, 12, 12)
	{
		_t._hour = 13;
		_t._minute = 13;
		_t._second = 10;
	}
private:
	int _year = 2024;
	int _month = 5;
	int _day = 3;
	Time _t;
};



int main()
{
	Date d1(2024, 3, 5);
	return 0;
}
3.2.3.2:特性2:友元关系不能传递

若C是B的友元,B是A的友元,不能说明是C是A的友元.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


class A
{

	friend class B;
private:
	int A_value;

};

class B
{
	friend class C;
private:
	int B_value;
};

class C
{
	C()
	{
		_a.A_value;
	}

private:
	int C_value;
	A _a;
};



int main()
{

	return 0;
}

3.2.3.3:特性3:友元关系不能够继承

这一点等到后面讲到继承时再详细介绍,uu们先暂时作为了解就好啦.

4.内部类

4.1:概

若一个类定义在另外一个类的内部,那么这个类就被称为内部类.内部类是一个独立的类,它不属于外部类,更无法通过外部类的对象去访问内部类的成员.外部类对内部类没有任何优先的访问权限.

  • 内部类是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类的所有成员
  • 外部类不是内部类的成员.
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
	}
	//Time类是Date类的友元类,Time这个类受到Date类类域的限制
	class Time
	{
	public:
		Time(int hour, int minute, int second)
			:_hour(hour)
			, _minute(minute)
			, _second(second)
		{
		}
		//Time类是Date类的友元类
		void Function(Date& d)
		{
			d._year = 2022;
			d._month = 12;
			d._day = 1;
			//内部类可以直接访问外部类的static成员
			++_number;
		}
	private:
		int _hour;
		int _minute;
		int _second;
	};
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	//声明
	static int _number;
};

//静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
int Date::_number = 0;


int main()
{
	Date d1(2022, 1, 2);
	d1.Print();

	Date::Time t1(1, 2, 3);

	t1.Function(d1);
	d1.Print();
	return 0;
}

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
	}
	//Time类是Date类的友元类,Time这个类受到Date类类域的限制
	class Time
	{
	public:
		Time(int hour, int minute, int second)
			:_hour(hour)
			, _minute(minute)
			, _second(second)
		{
		}
		//Time类是Date类的友元类
		void Function(Date& d)
		{
			d._year = 2022;
			d._month = 12;
			d._day = 1;
			//内部类可以直接访问外部类的static成员
			++_number;
		}
	private:
		int _hour;
		int _minute;
		int _second;
	};
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	//声明
	static int _number;
};

//静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
int Date::_number = 0;


int main()
{
	Date d1(2022, 1, 2);
	Date::Time t1(1, 2, 3);
	t1.Function(d1);
	d1.Print();
	return 0;
}

4.2:特性

4.2.1:内部类可以定义在外部类的public,protected,private都是可以的.

4.2.2:内部类可以直接访问外部类的static成员,不需要外部类的对象/类名

4.2.3:sizeof(外部类) = 外部类,与内部类没有任何关系

4.2.3.1:代码1(无内部类)
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

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;
	//声明
	static int _number;
};

//静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
int Date::_number = 0;


int main()
{
	Date d1(2022, 1, 2);
	cout << sizeof(d1) << endl;
	return 0;
}

4.2.3.2:代码2(有内部类)
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
	}
	//Time类是Date类的友元类,Time这个类受到Date类类域的限制
	class Time
	{
	public:
		Time(int hour, int minute, int second)
			:_hour(hour)
			, _minute(minute)
			, _second(second)
		{
		}
		//Time类是Date类的友元类
		void Function(Date& d)
		{
			d._year = 2022;
			d._month = 12;
			d._day = 1;
			//内部类可以直接访问外部类的static成员
			++_number;
		}
	private:
		int _hour;
		int _minute;
		int _second;
	};
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	//声明
	static int _number;
};

//静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
int Date::_number = 0;


int main()
{
	Date d1(2022, 1, 2);
	cout << sizeof(d1) << endl;
	return 0;
}

4.2.4:内部类受到外部类类域的影响

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
	}
	//Time类是Date类的友元类,Time这个类受到Date类类域的限制
	class Time
	{
	public:
		Time(int hour, int minute, int second)
			:_hour(hour)
			, _minute(minute)
			, _second(second)
		{
		}
		//Time类是Date类的友元类
		void Function(Date& d)
		{
			d._year = 2022;
			d._month = 12;
			d._day = 1;
			//内部类可以直接访问外部类的static成员
			++_number;
		}
	private:
		int _hour;
		int _minute;
		int _second;
	};
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	//声明
	static int _number;
};

//静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
int Date::_number = 0;


int main()
{
	Date d1(2022, 1, 2);

	Date::Time t1(1, 2, 3);
	return 0;
}

5:匿名对象

在之前我们所使用的对象都是有名对象,这种对象有属于自己的名字,但除了有名对象外,还有一种对象,叫做匿名对象.匿名对象有一个特性,那就是它的生命周期只有这一行,在下一行它会自动调用析构函数.

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Date
{
public:
	//构造函数
	Date(int a = 0)
	{
		cout << "Date()" << endl;
	}
	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	//有名对象
	Date d1;

	//匿名对象
	Date();
	return 0;
}

6:拷贝对象时编译器的一些优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对对象的拷贝,提高效率

6.1:代码1

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
	//构造函数
	Date(int year = 1)
		:_year(year)
	{
		cout << "Date(int year = 1)" << endl;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		cout << "Date(const Date & d)" << endl;
	}

	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
		}
		return *this;
	}

private:
	int _year;
};
int main()
{
	//单参数构造函数支持隐式类型的转换,构造一个临时对象, 再进行拷贝构造
	Date d1 = 2022;//构造 + 拷贝构造---->会被优化成一次构造
	return 0;
}

6.2:代码2

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
	//构造函数
	Date(int year = 1)
		:_year(year)
	{
		cout << "Date(int year = 1)" << endl;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		cout << "Date(const Date & d)" << endl;
	}

	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
		}
		return *this;
	}

private:
	int _year;
};

void Func1(Date d)
{
	;
}


int main()
{
	//单参数构造函数支持隐式类型的转换,构造一个临时对象, 再进行拷贝构造
	Date d1 = 2022;//构造 + 拷贝构造---->会被优化成一次构造
	//自定义类型传值调用会调用拷贝构造
	Func1(d1);
	return 0;
}

6.3:代码3

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
	//构造函数
	Date(int year = 1)
		:_year(year)
	{
		cout << "Date(int year = 1)" << endl;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		cout << "Date(const Date & d)" << endl;
	}

	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
		}
		return *this;
	}

private:
	int _year;
};

void Func1(Date d)
{
	;
}


int main()
{
	//传匿名对象:构造 + 拷贝构造---->优化成一次构造
	Func1(Date(2));
	cout << endl;

	return 0;
}

6.4:代码4

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
	//构造函数
	Date(int year = 1)
		:_year(year)
	{
		cout << "Date(int year = 1)" << endl;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		cout << "Date(const Date & d)" << endl;
	}

	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
		}
		return *this;
	}

private:
	int _year;
};

Date Fun3()
{
	Date d;
	return d;
}


int main()
{
	//拷贝构造 + 拷贝构造 --->优化成了直接构造
	Date result = Fun3();
	return 0;
}

6.5:代码5

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date
{
public:
	//构造函数
	Date(int year = 1)
		:_year(year)
	{
		cout << "Date(int year = 1)" << endl;
	}
	//拷贝构造函数
	Date(const Date& d)
	{
		cout << "Date(const Date & d)" << endl;
	}

	//析构函数
	~Date()
	{
		cout << "~Date()" << endl;
	}
	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
		}
		return *this;
	}

private:
	int _year;
};

Date Fun4()
{
	return Date();
}

int main()
{
	//构造 + 拷贝构造 + 拷贝构造--->优化成了直接构造
	Date result1 = Fun4();


	return 0;
}

好啦,uu们,关于类和对象(下)这部分滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~

相关推荐

  1. 对象

    2024-07-10 16:46:02       40 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 16:46:02       99 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 16:46:02       107 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 16:46:02       90 阅读
  4. Python语言-面向对象

    2024-07-10 16:46:02       98 阅读

热门阅读

  1. MMSegmentation笔记

    2024-07-10 16:46:02       22 阅读
  2. 网络安全筑基篇——XSS、XML、XXE

    2024-07-10 16:46:02       27 阅读
  3. 语义熵:深度学习中的信息度量新指标

    2024-07-10 16:46:02       26 阅读
  4. RedisTemplate使用

    2024-07-10 16:46:02       19 阅读