类与对象(中)

news2025/1/19 23:01:31

类与对象

  • 类的6个默认成员函数
  • 构造函数
    • 概念
    • 特性
  • 析构函数
    • 概念
    • 特性
  • 拷贝构造函数
    • 概念
    • 特性
  • 赋值运算符重载
    • 运算符重载
    • 赋值运算符重载
    • 前置++ 后置++ ++重载
  • const成员函数
  • 取地址及const取地址操作符重载

类的6个默认成员函数

当类中没有任何成员时,称作空类
但是呢,编译器会自动生成6个默认成员函数,所以当一个类中没有任何成员时,还是存在6个默认函数的

默认成员函数:使用者没有实现,编译器自动生成;使用者自己实现,则使用以实现的

在这里插入图片描述

构造函数

概念

观察下面代码

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, 12, 4);
	d1.Print();
	return 0;
}

对于 Date类而言,难道只能通过共有函数 Init给对象进行初始化吗?而且如果每次创建对象都需要调用此方式初始化,是不是很麻烦呢?
由此,便引入构造函数的概念,可以在对象创建时就进行初始化

构造函数是一个特殊的成员变量,名字与类名相同,创建类类对象时由编译器自动调用,以确保,每个数据成员都有一个适当的初始值,并且在对象整个生命周期内只调用一次
功能类似于C语言中的Init

特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称为构成,但其功能不是开辟空间创建对象,而是初始化对象

  1. 函数名与类名相同
  2. 没有返回值
  3. 对象实例化(定义)时编译器会自动调用相应的构造函数
  4. 构造函数可以重载
class Date
{
public:
	//无参构造函数
	Date()
	{

	}

	//含参构造函数
	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;
};

void test()
{
	Date d1;//调用无参构造函数
	d1.Print();
	Date d2(2022, 12, 4);//调用含参构造函数
	d2.Print();
}

在这里插入图片描述

这里有一个点需要注意,Date d1调用无参构造函数时,不可以加上(),因为会造成函数声明

在这里插入图片描述

  1. 如果使用者没有在类中实现构造函数,编译器便会自动生成一个无参的默认构造函数;不过如果使用者已经实现,则编译器不会再生成
class Date
{
public:
	//没有构造函数
	void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
void test()
{
	Date d1;
	d1.Print();
}

在这里插入图片描述

  1. 在5中可以观察到,当使用者不在类中实现构造函数,编译器会自动生成默认构造函数,但是呢,运行的结果却是随机值,似乎没有任何用处。既然这样的话,还不如使用者自己在类中实现需要的构造函数,但真的是如此吗???
    原因是在C++中,把类型分为内置类型(基本类型)和自定义类型。内置类型:语言提供的数据类型,例如int/char/double等;自定义类型:使用者自己定义的类型,例如class/struct/union
    编译器生成的默认构造函数只会对自定义类型起作用
    之后为了解决默认构造函数不处理内置类型的问题,规定内置类型成员变量在类中声明时可以进行赋值

  2. 只有无参构造函数全缺省构造函数编译器默认生成的构造函数称为默认构造函数,并且默认构造函数只能存在一个
    不传参数就可以调用的构造函数,就称作默认构造

class Date
{
public:
	//无参构造函数
	Date()
	{
		_year = 2022;
		_month = 12;
		_day = 5;
	}
	
	//全缺省构造函数
	Date(int year = 2022, int month = 12, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

void test()
{
	Date d;
}

因为默认构造函数只能存在一个,所以程序会崩溃

在这里插入图片描述

析构函数

概念

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

特性

  1. 析构函数名是在类名前面加上~
  2. 无参数无返回值类型
  3. 一个类只能有一个析构函数,若使用者没有在类中实现,系统便会自动生成默认的析构函数。析构函数不能重载
  4. 对象生命周期结束时,编译器才会调用析构函数。这里的生命周期包括生:局部域,全局域,malloc申请的空间
class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(int x)
	{
		//...
		_a[_top++] = x;
	}

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

int main()
{
	Stack sk;
	sk.Push(1);
	sk.Push(2);
	sk.Push(3);
	sk.Push(4);
	return 0;
}

在创建对象的同时构造函数进行初始化,结果如下

在这里插入图片描述

将四个数值全部插入栈之后,结果如下

在这里插入图片描述

此时_top的值为4,表示此时栈中已经存在四个数值

当程序跑到return 0时,主函数生命周期结束(全局域)调用析构函数,此时监视结果如下

在这里插入图片描述

  1. 默认构造函数只处理自定义类型成员变量,所以类似的,编译器生成的默认析构函数,对自定义类型成员变量才会调用它的析构函数
class M
{
public:
	~M()
	{
		cout << "~M()析构函数" << endl;
		_m = 0;
	}
private:
	int _m;
};
class Date
{
public:
	void Init(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;
	//自定义类型
	M _m;
};
int main()
{
	Date d1;
	return 0;
}

在这里插入图片描述

对象 d1的成员变量,包括内置类型和自定义类型。其中内置类型在 d1销毁时不需要资源清理,也就是不需要调用析构函数;自定义类型在 d1销毁时需要调用其本身的析构函数,也就是调用 M类的析构函数,从而销毁 _m。虽然 Date中没有显示析构函数,但编译器会默认生成一个析构函数,其目的是在内部调用 M的析构函数,也就解释了运行结果为什么会打印 ~M()析构函数

  1. 判断析构函数是否需要使用者实现的方法是,如果类中没有申请资源,析构函数可以不写,直接使用编译器默认生成的析构函数;如果有资源申请,一定要写,否则会造成资源泄漏

总结
面对需求:编译器默认生成的就可以,就不要自己写,不满足就自己写
Stack的析构函数需要自己写
Date的不需要自己写,默认生成的就可以

拷贝构造函数

概念

拷贝构造函数也称拷贝初始化,只有一个形参,且整个形参是对相同类类型对象的引用(一般由const修饰),在用已存在的类类型对象创建对象时由编译器自动调用

特性

  1. 拷贝构造函数是构造函数的一个重载形式
  2. 拷贝构造函数的参数只有一个且必须是相同类类型对象的引用;如果使用传值方式,将会引发无穷递归,编译器会报错
class Date
{
public:
	Date(int year = 2022, int month = 12, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

void test()
{
	Date d1;
	Date d2(d1);
}

在这里插入图片描述

传值调用的本质就是拷贝一份数据传递给相应的函数

在这里插入图片描述

  1. 若使用者没有在类中实现拷贝构造函数,编译器会生成。拷贝对象时按照内存存储字节序完成拷贝,称为浅拷贝
class Date
{
public:
	Date(int year = 2022, int month = 12, int day = 5)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
    //内置类型
	int _year;
	int _month;
	int _day;
};

void test()
{
	Date d1;
	Date d2(d1);
}
int main()
{
	test();
	return 0;
}

监视结果如下

在这里插入图片描述

在编译器生成的默认拷贝构造函数中,内置类型按照字节方式直接拷贝,自定义类型是调用其拷贝构造函数完成拷贝

  1. 既然编译器生成的默认构造函数已经可以完成字节序的值拷贝,那么还有自己在类中实现的必要吗???

观察下面的代码

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(int x)
	{
		//...
		_a[_top++] = x;
	}

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

int main()
{
	Stack sk1;
	sk1.Push(1);
	sk1.Push(2);
	sk1.Push(3);
	sk1.Push(4);
	Stack sk2(sk1);
	return 0;
}

运行结果如下

在这里插入图片描述

程序直接崩溃,为什么呢,上面也是没有自己写拷贝构造函数,程序正常运行,在这里为什么就不行呢

接下来用一张图来进行解释

在这里插入图片描述

如果类中没有涉及资源申请,拷贝构造函数便不需要自己实现;如果涉及到资源申请,拷贝构造函数必须要自己实现,否则就是浅拷贝,程序便会崩溃

改进如下

Stack(const Stack& sk)
	{
		cout << "Stack(const Stack& sk)" << endl;
		_a = (int*)malloc(sizeof(int) *sk._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, sk._a, sizeof(int) * sk._top);
		_top = sk._top;
		_capacity = sk._capacity;
	}

在这里插入图片描述

sk1,sk2中_a所指的不是同一块空间,便完成了深拷贝

需要写析构函数的类,都需要写深拷贝的拷贝构造
不需要写析构函数的类,默认生成的浅拷贝的拷贝构造就可以满足

  1. 拷贝构造函数使用场景

使用已存在对象创建新对象
函数参数类型是类类型对象
函数返回值类型是类类型对象

赋值运算符重载

运算符重载

为了增加代码的可读性引入运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名以及参数列表,其返回值类型与参数列表与普通的函数类似

函数名:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型operator操作符(参数列表)

  1. 不能通过连接其他符号来创建新的操作符:例如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .* / :: / sizeof / ?: / . 这五个运算符不能重载

重载 ==
operator==

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date& d2)
	{
		return _year == d2._year
			&& _month == d2._month
			&& _day == d2._day;
	}
	
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2022, 12, 5);
	Date d2(2022, 12, 25);
	cout << (d1 == d2) << endl;
	return 0;
}

在这里插入图片描述

重载>

bool operator>(const Date& d2)
	{
		if (_year > d2._year)
		{
			return true;
		}
		else if (_year == d2._year && _month > d2._month)
		{
			return true;
		}
		else if (_year == d2._year && _month == d2._month && _day > d2._day)
		{
			return true;
		}
		return false;
	}

重载>= 只需要赋用上面两种运算符即可

bool operator>=(const Date& d2)
	{
		return *this > d2 || *this == d2;
	}

重载+=重载+

class Date
{
public:
	//判断日期的有效性
	int Getmonthday(int year, int month)
	{
		int monthdayarray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}
		else
		{
			return monthdayarray[month];
		}
	}

	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > Getmonthday(_year, _month))
		{
			_day -= Getmonthday(_year, _month);
			_month++;
			if (_month == 13)
			{
				++_year;
				_month = 1;
			}
		}
		return *this;
	}

	Date operator+(int day)
	{
		Date ret(*this);
		ret += day;
		return ret;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d(2022, 12, 5);
	d += 50;
	return 0;
}

在这里插入图片描述

赋值运算符重载

  1. 赋值运算符重载格式

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

Date类,没有资源的申请赋值重载较为简单

class Date
{
public:
	//判断日期的有效性
	int Getmonthday(int year, int month)
	{
		int monthdayarray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}
		else
		{
			return monthdayarray[month];
		}
	}

	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	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;
};
int main()
{
	Date d1(2022, 12, 5);
	Date d2;
	d2 = d1;
	return 0;
}

在这里插入图片描述

Stack类,有资源的申请,赋值重载较为复杂

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}

	Stack& operator=(const Stack& sk)
	{
		if (this != &sk)
		{
			free(_a);
			_a = (int*)malloc(sizeof(int) * sk._capacity);
			if (_a == nullptr)
			{
				perror("malloc fail");
				exit(-1);
			}
			memcpy(_a, sk._a, sizeof(int) * sk._top);
			_top = sk._top;
			_capacity = sk._capacity;
		}
		return *this;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(int x)
	{
		//...
		_a[_top++] = x;
	}

private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack sk1;
	sk1.Push(1);
	sk1.Push(2);
	sk1.Push(3);
	Stack sk2;
	sk2.Push(10);
	sk2.Push(20);
	sk2.Push(30);
	sk1 = sk2;
	return 0;
}

存在三种情况,
sk1中的_a申请的空间比sk2中的_a申请的空间大,相等,小,为了简便处理,在赋值时,先将sk1中_a申请的空间释放,接着将sk2整体拷贝给sk1即可

  1. 赋值运算符只能重载成类的成员函数不能重载成全局函数
    因为类中的成员变量是私有的,在类外不能进行访问的

赋值运算符如果使用者没有在类中实现,编译器会生成一个默认的

  1. 使用者没有在类中实现赋值运算符重载时,编译器会生成一个默认的,且以值的方式逐字节拷贝(浅拷贝)。内置类型成员变量是直接赋值,而自定义类型成员变量需要调用相应类的赋值运算符重载完成赋值

既然编译器生成的默认构造函数已经可以完成字节序的值拷贝,那么还有自己在类中实现的必要吗???

这里与上面拷贝构造的思想类似,就不加赘述

如果类中没有涉及资源管理,赋值运算符不需要写;如果涉及到资源管理使用者必须在类中实现

前置++ 后置++ ++重载

先区分,前置与后置的区别,主要区别就是,返回的结果不同,前置返回的结果是++后的数值;后置返回的结果是++之前的数值,也就是本身;对于- -也是同样的道理

//前置++
Date& operator++()
{
	*this += 1;
	return *this;
}
//后置++,多一个参数,为了与前置区分
Date operator++(int)
{
	Date ret(*this);
	*this += 1;
	return ret;
}

const成员函数

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

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, 12, 5);
	d1.Print();
	const Date d2;
	d2.Print();
	return 0;
}

在这里插入图片描述

改善之后
const修饰成员函数,实际是修饰该成员变量隐含的this指针

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

在这里插入图片描述

总结:
凡是内部不改变成员变量的,也就是*this对象数据的,此类成员函数都应该加上const进行修饰

取地址及const取地址操作符重载

这里两类默认成员函数一般不需要重新定义,编译器会默认生成

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

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

相关文章

Docker使用(容器、镜像相关命令)

虚拟化 在计算机中&#xff0c;虚拟化&#xff08;英语&#xff1a;Virtualization&#xff09;是一种资源管理技术&#xff0c;是将计算机的各种实体资源&#xff0c;如服务器、网络、内存及存储等&#xff0c;予以抽象、转换后呈现出来&#xff0c;打破实体结构间的不可切割…

虹科方案|将以太网连接添加到Dell EMC PowerVault™ ML3 SAS库

一、Dell EMC 和 ATTO 磁带解决方案 Dell EMC 和 ATTO 提供了业界唯一的商用解决方案&#xff0c;可将高速以太网连接添加 到标准 SAS LTO 磁带驱动器。ATTO XstreamCORE ET 8200 智能网桥允许您使用 iSCSI 和 iSER 协议通过标准以太网远程连接到 SAS 磁带驱动器。当与采用最新…

花 2 个月备战字节跳动Java岗,3 轮面试拿下 60W Offer

最近收到一位刚入职字节的 Java 工程师朋友投稿——以下内容来自其亲身经历&#xff0c;某双非硕士拿到 字节 60W offer &#xff0c;感谢他的走心分享&#xff08;文末附硬货笔记&#xff09; PART1&#xff1a;个人情况简介 菜 J 一枚&#xff0c;本硕都是计算机&#xff08…

[论文阅读] SqueezeSeg V1

文章目录1. 主要思想2. 具体方法2.1 数据处理方式2.2 网络架构3. 实验支撑4. 总结启示5. 相关文献paper 原论文的链接 code: 源代码链接 paper全称&#xff1a;SqueezeSeg: Convolutional Neural Nets with Recurrent CRF for Real-Time Road-Object Segmentation from 3D LiDA…

【02】FreeRTOS获取10.4.6源码+移植到STM32F407步骤

目录 1.获取FreeRTOS源码 1.1 FreeRTOS官网下载步骤 1.2FreeRTOS源码内容 1.3FreeRTOS内核文件 1.3.1Demo文件夹 1.3.2Source文件夹 2.FreeRTOS移植 2.1添加FreeRTOS源码 2.1.1复制FreeRTOS源码 2.1.2将文件添加到工程 2.1.3添加头文件路径 2.2添加FreeRTOS.h 2.3修改SYS…

vpp process类型节点调度过程

vpp节点类型 VLIB_NODE_TYPE_PROCESS&#xff1a;process类型节点可以被挂起也可以被恢复&#xff0c;main线程上调度 &#xff08;免费订阅,永久学习&#xff09;学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂 process节点注册 pro…

【MC】新加载器 Quilt 好用吗?和 Fabric 相比好在哪?

在今年四月 (2022/4/20) &#xff0c;一个船新加载器 Quilt 发布了第一个测试版。 Quilt officially entered its first beta today, attracting an influx of new users and an amazing amount of support and positive feedback. By the end of the day, Quilt was happily l…

Go语言设计与实现 -- 字符串

Go语言的字符串与Java和python是一样的。具有不可变性。是一个只读的字节数组&#xff0c;如图所示。 因为Go的字符串具有不可变性&#xff0c;所以我们只能通过string和[]byte类型之间反复转换实现修改。 将这一段内存复制到栈上将变量的类型转换成[]byte后并修改字节数据将修…

功能上新|使用 Excel 低门槛进行指标分析!

Kyligence Zen 功能上新啦&#xff01;用户不仅可以在 Kyligence Zen 中定义、分析和管理指标&#xff0c;还可直接使用 Excel 插件来分析 Kyligence Zen 中已经定义好的指标&#xff0c;学习无门槛&#xff0c;上手更轻松&#xff01;欢迎访问 http://zen.kyligence.io 申请免…

实验二A 图像的空域(源代码一站式复制粘贴)

实验二A 图像的空域一、实验目的二、实验原理三、实验内容与要求四、实验的具体实现一、实验目的 1.掌握图像滤波的基本定义及目的。 2.理解空间域滤波的基本原理及方法。 3.掌握进行图像的空域滤波的方法。 二、实验原理 1.空域增强 空域滤波是在图像空间中借助模板对图像进…

阳哥JUC并发编程之AQS后篇全网最详细源码笔记

文章目录AQS后序课程笔记AQS源码ReentryLock锁的原理分析公平锁以及非公平锁源码详解Aquire方法调用原码流程分析第一步、tryAquire第二步、addwrite第三步&#xff1a;aquireQueuedAQS释放锁的过程第一步、释放锁第二步进入aquireQueueAQS异常情况下走Cancel流程分析第一种队尾…

ECharts项目实战:全球GDP数据可视化

【课程简介】 可视化是前端里一个几乎可以不用写网页&#xff0c;但又发展得非常好的方向。在互联网产品里&#xff0c;无论是C端中常见的双十一购物节可视化大屏&#xff0c;还是B端的企业中后台管理系统都离不开可视化。国家大力推动的智慧城市、智慧社区中也有很多可视化的…

对于DDoS攻击防御有哪些误区?

​  DDoS攻击是属于常见网络攻击之一&#xff0c;也是一种较难防御的网络攻击。它的特点就是易于启动、难防御、有害、难跟踪等。因此DDoS攻击也是众多站长最怕遇见的网络攻击。那么大家在使用海外服务器时&#xff0c;会有哪些DDoS攻击防御的误区呢? 1、防御全部DDoS攻击 防…

CSS -- 09. 移动WEB开发之flex布局

文章目录移动WEB开发之flex布局1 flex布局原理2 常见的父项属性2.1 设置主轴方向 flex-direction2.2 设置主轴上的子元素排列方式 justify-content2.3 设置元素是否换行 flex-wrap2.4 设置侧轴上的子元素的排列方式&#xff08;单行&#xff09; align-items2.5 设置侧轴上的子…

【矩阵论】6.范数理论——基本概念——矩阵范数生成向量范数谱范不等式

6.1.3 矩阵范数产生向量范数 CnnC^{n\times n}Cnn 上任一矩阵范数 ∥∙∥\Vert \bullet\Vert∥∙∥ 都产生一个向量范数 φ(X)∥X∥V\varphi(X)\Vert X\Vert_Vφ(X)∥X∥V​ 矩阵范数与向量范数的相容性&#xff1a;φ(Ax)≤∥A∥φ(x)\varphi(Ax)\le \Vert A\Vert\varphi(x)φ…

MySQL SSL安全解读

安全一直是不可不重视的问题。目前MySQL这方面应大方向上技术手段都具备。如&#xff1a;网络链接&#xff0c;权限控制&#xff0c;key秘钥认证&#xff0c;数据加密脱敏 等方式。综合考虑&#xff0c;虽然很多环境无法所有这些安全策略全部应用上&#xff0c;但在可控范围内尽…

【C语言数据结构(基础版)】第五站:树和二叉树

目录 一、树的概念及结构 1.树的概念 2.树的表示 3.树在实际中的应用 二、二叉树概念及结构 1.概念 2.特殊的二叉树 3.二叉树的性质 4.二叉树的存储结构 &#xff08;1&#xff09;顺序存储 &#xff08;2&#xff09;链式存储 三、二叉树链式结构的实现 1.二叉树的…

Mysql的MVCC知多少(隐藏字段,undolog版本链和ReadView)

文章目录前言一、什么是MVCC二、快照读与当前读三、4种隔离级别与MVCC四、MVCC实现4.1 隐藏字段和undolog版本链4.2 ReadView五、说明前言 其实自己之前对MVCC知之甚少&#xff0c;总觉得是一块很难啃的骨头&#xff0c;有点内惧&#xff0c;但当你真的掌握之后&#xff0c;就…

Fastadmin ThinkPHP5 宝塔环境 下Redis的使用教程

本篇为学习参考&#xff0c;不喜勿喷 此为宝塔windows环境下 1.首先在宝塔的安装面板&#xff0c;找到redis&#xff0c;点击安装。 2.安装完redis后&#xff0c;点击设置&#xff0c; 设置个密码(ps:留空代表没有设置密码) 3.php环境安装redis扩展&#xff08;ps:安装redis扩…

Linux学习笔记——Linux用户和权限管控

03、Linux用户和权限管控 3.1、认知root 用户 3.1.1、root用户&#xff08;超级管理员&#xff09; 1、无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。 在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root&#xff08;超级管理员&#x…