【C++】---类和对象(中)默认成员函数 和 操作符重载

news2024/11/26 13:41:25

前言:

假如一个类中既没有成员变量也没有成员函数,那么这个类就是空类,空类并不是什么都没有,因为所有类都会生成如下6个默认成员函数:
在这里插入图片描述

一、构造函数

1、构造函数的定义及其特性

对于日期类对象,我们可能会忘记调用Init函数进行初始化,C++为了解决这个问题,引入构造函数进行初始化。

#include<iostream>
using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};

int main()
{
	Date d1;
	d1.Init(2024, 2, 11);
	d1.Print();
	return 0;
}

构造函数一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次
特性:
1.构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
2.其特征如下:

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义了编译器将不再生成!

3.默认构造函数是我们不传参就可以调用的函数

  • 我们什么都没写,编译器自动生成的
  • 我们自己写的:无参的构造函数
  • 我们自己写的:全缺省构造函数
    这三类只能存在一个,注意后两个:不能同时存在的原因:当定义一个不带参数的类对象时,编译器不能确定到底要调用我们写的无参默认构造函数还是要调用我们写的带参全缺省默认构造函数,会报“对重载函数的调用不明确错误”。
    (1) 我们什么都没写,编译器自动生成的
#include<iostream>
using namespace std;
 
class Date
{
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
	Date d1;//调用编译器自动生成的默认构造函数
 
	return 0;
}

(2)我们自己写的:无参的构造函数

#include<iostream>
using namespace std;
 
class Date
{
public:
    
	//1.无参默认构造函数:初始化对象
	Date()
	{
		_year = 2024;
		_month = 2;
		_day = 12;
	}
	
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
	Date d1;
 
	return 0;
}

(3)我们自己写的:全缺省构造函数

#include<iostream>
using namespace std;
 
class Date
{
public:
    //2.带参全缺省默认构造函数:初始化对象
	Date(int year = 2024, int month = 2, int day= 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
	Date d1;//调用带参默认构造函数
 
	return 0;
}

2、编译器自动生成的默认构造函数

关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,
编译器生成默认的构造函数对内置类型不做处理
而对自定义类型成员会调用的它的默认成员函数

看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数:

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

二、析构函数

1、析构函数的定义及其特性

析构函数用来完成类的资源清理工作,编译器在销毁对象时,会自动调用析构函数。
特性:

(1)析构函数名是在类名前加上字符 ~。

(2)无参数无返回值。(析构函数不能重载,一个类有且仅有一个析构函数)

(3)一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数

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

2、多对象的析构顺序

假如这个类有多个对象,那么析构的先后顺序是什么?
多对象的析构顺序 :局部对象(后定义先析构) -> 局部的静态(后定义先析构) ->全局对象(后定义先析构)

// 局部对象(后定义先析构) -> 局部的静态(后定义先析构) ->全局对象(后定义先析构)
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year)
	{
		_year = year;
	}
	~Date()
	{
		// 调用一次 析构函数 我就打印一次
		cout << "~Date()->" << _year << endl;
	}
};

void func()
{
	// 局部域
	Date d4(4);
	static Date d5(5);
	static Date d10(10);
}

// 全局域
Date d6(6);
static Date d7(7);
Date d8(8);
static Date d9(9);

int main()
{
	// 局部域
	Date d1(1);
	Date d2(2);
	static Date d3(3);
	func();
	return 0;
}

3、编译器自动生成的默认析构函数

当不写析构函数时,编译器会自动生成默认的析构函数,不过这个默认的析构函数什么也不做,不需要清理资源。那么编译器自动生成的默认析构函数到底有什么用呢?

同析构函数

(1)对于内置类型,不会处理

(2)对于自定义类型,会调用它的析构函数

三、拷贝构造函数

1、拷贝构造函数定义及特性

1.拷贝构造函数只有单个形参,该形参是对同类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
2.特征:
拷贝构造函数也是特殊的成员函数,其特征如下:

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错因为会引发无穷递归调用

思考:为什么使用传值方式会引发无穷递归调用?
C++规定:对自定义类型的函数传值传参时,都会调用拷贝构造函数!!!
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <iostream>

using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	// 构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	//Date(Date d)// 错误写法 使用传值方式编译器直接报错,因为会引发无穷递归调用
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	// 析构函数
	~Date()
	{

	}
};

int main()
{
	Date d1(2024,2,12);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

拷贝构造函数也是构造函数,函数名和类型名相同,参数是同类型对象的引用,由编译器自动调用。
因此,对于自定义类型的对象,一般推荐使用传引用传参,虽然传值传参也可以,但是要调用拷贝构造

(1)对其拷贝构造函数的理解

#include <iostream>
using namespace std;

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

	Date(int year = 2024, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 析构函数
	~Date()
	{
		cout << "~Date()" << endl;//在析构函数内打印,调用一次就打印一次
	}
	//拷贝构造函数
	Date(const Date& d)  // Date (Date* this ,Date &d)
		// 因为d2调用拷贝构造函数,所以&d2=this, (d是d1的别名)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}


};

int main()
{
	Date d1(2024,2,16);// 调用构造函数

	Date d2(d1);// 调用拷贝构造函数,就是在定义对象的时候直接用拷贝构造函数
	// Date(&d2,Date& d1)

	d2.Print();

	return 0;
}

在这里插入图片描述

2、深浅拷贝

1.传值传参:是浅拷贝
浅拷贝的缺点:
两个数组的指针指向同一块空间,当进行free时只能 free一个,那么另外一个就会变成“野指针”!
动态开辟资源的,浅拷贝都不行,因为浅拷贝只会仅仅copy数据。
在这里插入图片描述
2.传引用传参:是深拷贝:深拷贝还需要我们自己去写,编译器不会自动生成。
在这里插入图片描述
在这里插入图片描述
在判断一个类里面需不要写拷贝构造函数,根据具体情况而定。如果没有动态开辟的程序,就不需要写深拷贝。

3、编译器自动生成的拷贝构造函数

若未显式定义,系统会生成默认拷贝构造函数。 同构造函数和析构函数不同:

(1)拷贝构造函数对内置类型依次按照字节序完成拷贝,即浅拷贝或值拷贝。

假如不写拷贝构造函数,照样正常打印:

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <iostream>

using namespace std;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	// 构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	拷贝构造函数
	Date(Date d)// 错误写法 使用传值方式编译器直接报错,因为会引发无穷递归调用
	//Date(const Date& d)
	//{
	//	_year = d._year;
	//	_month = d._month;
	//	_day = d._day;
	//}
	// 析构函数
	~Date()
	{

	}
};

int main()
{
	Date d1(2024,2,12);
	Date d2(d1);
	d1.Print();
	d2.Print();
	return 0;
}

在这里插入图片描述

四、赋值运算符重载函数

1、运算符重载

1.定义:对于内置类型来说,语言层面本身就支持已经定义好的运算符,但对于自定义类型来说不行, C++中规定运算符重载的原因是:让自定义类型可以像内置类型一样使用运算符,想重载哪个运算符就重载哪个运算符。
运算符重载的语法:
函数原型:返回值类型 operator操作符(参数列表)
2.特性:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针
  5. (.*) (::) (sizeof) (?;) (.) 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

运算符重载实例:

(1)==运算符重载

#include<iostream>
using namespace std;
 
class Date
{
public:
	//构造函数
	Date(int year = 2024, int month = 2, int day = 16)
	{
		_year = year;
		_month = month;
		_day = day;
	}
 
//成员变量公有
public:
	int _year;
	int _month;
	int _day;
};
 
//operator==运算符重载
bool operator==(Date x1, Date x2)
{
	return x1._year == x2._year
		&& x1._month == x2._month
		&& x1._day == x2._day;
}
 
int main()
{
	Date d1(2024, 2, 15);
	Date d2(2024, 2, 16);
 
    //两种调用方式:
	//1.可读性不强
    operator==(d1, d2);
 
    //2.当编译器看到==自定义类型,会去检查日期类有没有==的重载运算符,如果有重载会转换成operator==(d1, d2)去调用operator==函数
	d1 == d2;
 
	return 0;
}

2、运算符重载和函数重载的区别:

运算符重载和函数重载,虽然都使用了重载,但是两者之间没有关联:

(1)函数重载时支持定义同名函数

(2)运算符重载是为了让自定义类型可以像内置类型一样去使用运算符。

3、赋值运算符重载

1.赋值运算符重载语法格式:

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

赋值运算符重载和拷贝构造的区别:

(1)拷贝构造函数:对即将要创建的新对象进行初始化(不过初始化的内容是将一个已经存在的对象拷贝给他)

(2)赋值运算符重载:两个已经存在的对象进行赋值

2.赋值运算符重载代码 以及如何调用!

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

	Date(int year = 2024, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 析构函数
	~Date()
	{
		cout << "~Date()" << endl;//在析构函数内打印,调用一次就打印一次
	}
	//拷贝构造函数
	Date(const Date& d)  // Date (Date* this ,Date &d)
		// 因为d2调用拷贝构造函数,所以&d2=this, (d是d1的别名)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	 //赋值运算符重载
	// d1=d2   //d1.operator=(&d1,d2)
	Date& operator=(const Date& d) // void Date& operator=(&d1,const Date& d)
	{
		if (this != &d) // 对d取地址,判断this的值和d的地址是否相同,如果不是自己给自己赋值,才需要拷
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

};

int main()
{
	//Date d1(2024,2,16);// 调用构造函数

	//Date d2(d1);// 调用拷贝构造函数,就是在定义对象的时候直接用拷贝构造函数
	 Date(&d2,Date& d1)

	//d2.Print();

	Date d1(2024, 2, 16);
	d1.Print();
	Date d2;
	d2.Print();

	d2 = d1;

	d2.Print();
	return 0;
}

在这里插入图片描述

4、const修饰类的成员函数

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

比如有如下场景:假如把Date类的operator==运算符重载函数写错了

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

将其中的一个"==“错写成”=" :

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

虽然编译没有问题,但是这会导致this的值被修改了,并且执行结果也错误:

int main()
{
	Date d1(2024, 2, 16);
	Date d2(2024, 3, 16);
 
	cout << (d1 == d2) << endl;
	d1.Print();
	d2.Print();
 
	return 0;
}

这不符合要求,仅仅是比较而已,但是被比较对象的值却被修改了。const最大的作用是保护对象和变量,d2传给了d,d是d2的别名,const已经保护了d,那d1如何保护呢?由于this是隐含的,那么const为了保护this,应该如何加?把const加在成员函数的后面,叫做const修饰成员函数

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

总结:
1.const引用:我是你的别名,但我不能修改你(是之前:权限的缩小)
比如:const int& c = a; // c是a的别名,但由于const修饰c所以说不能通过c来修a的值。
2.如果有const的修饰,那么说明这个变量是只能读的!如果没有const修饰,普通的变量它是可读可写的!
在这里插入图片描述

五、总结:

1.构造函数和析构函数
如果我们不写,编译器对内置类型不做处理,自定义类型会调用它的构造函数和析构函数进行处理

2.拷贝构造和赋值运算符重载
如果我们不写,内置类型会完成浅拷贝,自定义类型会调用它的拷贝构造函数和赋值运算符重载函数。

3.取地址操作符重载和const取地址操作符重载
一般不需要重载,编译器默认生成的已经够用,重载没有价值。


好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
在这里插入图片描述

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

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

相关文章

C语言---指针进阶

1.字符指针 int main() {char str1[] "hello world";char str2[] "hello world";const char* str3 "hello world.";const char* str4 "hello world.";if (str3 str4){//常量字符串在内存里面是无法修改的&#xff0c;所以没必要…

数据检索:倒排索引加速、top-k和k最邻近

之前在https://www.yuque.com/treblez/qksu6c/wbaggl2t24wxwqb8?singleDoc# 《Elasticsearch: 非结构化的数据搜索》我们看了ES的设计&#xff0c;主要侧重于它分布式的设计以及LSM-Tree&#xff0c;今天我们来关注算法部分&#xff1a;如何进行检索算法的设计以及如何加速倒排…

RapidMiner数据挖掘2 —— 初识RapidMiner

本节由一系列练习与问题组成&#xff0c;这些练习与问题有助于理解多个基本概念。它侧重于各种特定步骤&#xff0c;以进行直接的探索性数据分析。因此&#xff0c;其主要目标是测试一些检查初步数据特征的方法。大多数练习都是关于图表技术&#xff0c;通常用于数据挖掘。 为此…

嵌入式系统中常见传感器介绍

&#xff08;本文为简单介绍&#xff0c;内容取材网络&#xff09; 传感器是嵌入式系统接入外部环境信息的重要接口,根据测量物理量的不同,传感器可以分为温度传感器、湿度传感器、压力传感器、加速度传感器等多种类型。选择合适的传感器,对于实现嵌入式系统的控制和互动功能至…

Java微服务架构的选择:Spring Cloud、Kubernetes还是Kubernetes + Istio?

微服务架构已经成为现代软件开发的趋势&#xff0c;其可以带来高度可伸缩性、松耦合性和团队自治性等优势。 在Java开发领域中&#xff0c;选择适合的微服务架构是非常关键的决策&#xff0c;本文将探讨Spring Cloud、Kubernetes和KubernetesIstio这三个架构选择的优势和劣势。…

MIT-BEVFusion系列八--onnx导出1 综述及相机网络导出

目录 综述export-camera.py加载模型加载数据生成需要导出成 onnx 的模块Backbone 模块VTransform 模块 生成 onnx使用 pytorch 原生的伪量化计算方法导出 camera.backbone.onnx导出 camera.vtransform.onnx 综述 bevfusion的各个部分的实现有着鲜明的特点&#xff0c;并且相互…

BUGKU-WEB 本地管理员

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 解题思路 老规矩&#xff0c;先看源码发现了&#xff0c;那就要base一下了测试账号密码绕过IP限制 相关工具 F12插件modHeader 或者 直接使用Burp Suit对请求进行修改 解题步骤 先输入admin测试一下 显示…

vue3 中使用pinia 数据状态管理(在Taro 京东移动端框架中的使用)

1.pinia 介绍 pinia 是 Vue 的存储库&#xff0c;它允许您跨组件/页面共享状态。就是和vuex一样的实现数据共享。 依据Pinia官方文档&#xff0c;Pinia是2019年由vue.js官方成员重新设计的新一代状态管理器&#xff0c;更替Vuex4成为Vuex5。 Pinia 目前也已经是 vue 官方正式的…

蓝桥杯2023年第十四届省赛真题

题目 题目描述&#xff1a; 程序猿圈子里正在流行一种很新的简写方法&#xff1a;对于一个字符串&#xff0c;只保留首尾字符&#xff0c;将首尾字符之间的所有字符用这部分的长度代替。例如 internation-alization 简写成 i18n&#xff0c;Kubernetes &#xff08;注意连字符不…

Javaweb之SpringBootWeb案例之AOP核心概念的详细解析

2.3 AOP核心概念 通过SpringAOP的快速入门&#xff0c;感受了一下AOP面向切面编程的开发方式。下面我们再来学习AOP当中涉及到的一些核心概念。 1. 连接点&#xff1a;JoinPoint&#xff0c;可以被AOP控制的方法&#xff08;暗含方法执行时的相关信息&#xff09; 连接点指的…

2.8学习总结

2.8 1.二叉树的前序遍历 2.二叉树的中序遍历 3.二叉树的后序遍历 4.⼆叉树的层序遍历 5.⼆叉树的层序遍历2 6.二叉树的右视图 7.二叉树的层平均值 8.N叉树的层序遍历 9.每个树行中找最大值 10.填充每个节点的下一个右侧节点指针 11.填充每个节点的下一个右侧节点指针2 12.生命之…

C/C++中的max函数如何使用?哪个头文件?多个数字可以用max吗?

在C中&#xff0c;max函数是一个非常实用的函数&#xff0c;它用于比较两个或更多数值并返回其中的最大值。这个函数在头文件中定义。 下面是如何在C中使用max函数的一些示例&#xff1a; #include <iostream> #include <algorithm> // 引入algorithm头文件以使…

【从Python基础到深度学习】 8. VIM两种状态

一、安装 sudo apt install vim 二、VIM两种模式 - 命令状态/编辑状态 1.1 进入/退出VIM 进入VIM vim 退出vim :q <enter> 2.2 根目录下添加配置文件 window下创建vimrc类型文件内容如下&#xff1a; set nu set cursorline set hlsearch set tabstop4 使用Wins…

HTTP协议-响应报文详解(Respond)

目录 前言&#xff1a; 1.Respond报文 1.1报文格式 1.2格式图解 2.状态行&#xff08;首行&#xff09; 2.1状态码/状态码解释 &#xff08;1&#xff09;200 OK &#xff08;2&#xff09;404 Not Found &#xff08;3&#xff09;403 Forbidden &#xff08;4&#…

【LeetCode: 107. 二叉树的层序遍历 II + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

红队笔记Day4 -->多层代理(模拟企业拓扑)

声明&#xff1a;本机文章只用于教育用途&#xff0c;无不良引导&#xff0c;禁止用于从事任何违法活动 前几天的红队笔记的网络拓扑都比较简单&#xff0c;今天就来模拟一下企业的真实网络拓扑&#xff0c;以及攻击方法 一般的大企业的网络拓扑如下&#xff1a;&#xff1a;…

【二叉树层序遍历】【队列】Leetcode 102 107 199 637 429 515 116 117 104 111

【二叉树层序遍历】【队列】Leetcode 102 107 199 637 429 515 116 117 102. 二叉树的层序遍历解法 用队列实现107. 二叉树的层序遍历 II解法199. 二叉树的右视图 解法637. 二叉树的层平均值 解法429. N叉树的层序遍历515. 在每个树行中找最大值116. 填充每个节点的下一个右侧节…

vue3-组合式 API

什么是组合式 API&#xff1f; 组合式 API (Composition API) 是一系列 API 的集合&#xff0c;使我们可以使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语&#xff0c;涵盖了以下方面的 API&#xff1a; 响应式 API&#xff1a;例如 ref() 和 reactive()&a…

TCP_IP(6)

网络层 在复杂的网络环境中确定一个合适的路径. IP协议 与TCP协议并列,都是网络体系中最核心的协议. 基本概念 主机:配有IP地址,但是不进行路由控制的设备; 路由器:即配有IP地址,又能进行路由控制; 节点:主机和路由器的统称; 协议头格式 4位版本号(version):指定IP协议的版…

python 人脸检测器

import cv2# 加载人脸检测器 关键文件 haarcascade_frontalface_default.xml face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# 读取图像 分析图片 ren4.png image cv2.imread(ren4.png) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 进行人脸…