C++类和对象篇

news2024/10/2 8:29:23

1.类的定义


在C语言结构体中,只能定义变量,C++扩展了类的概念,能够在类定义函数;同时,struct仍然可以使用,但更常用class来表示类

1.1类中函数的两种定义方式

  • 函数的声明和定义都在类中

    class Date
    {
    public:
    	void Init()
    	{
    		cout << _year << "-" << _month << "-" << _day << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
  • 函数的声明在.h文件中,定义在.cpp文件中,此时定义函数时必须指定类域

    //Date.h
    class Date
    {
    public:
    	void Init();
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    //Date.cpp
    void Date::Init()
    {
    	cout << _year << "-" << _month << "-" << _day << endl;
    }
    

2.访问限定符和封装


C++有三大访问限定符,分别是public、protected、private

  • 被public修饰的成员可以在类外直接被访问
  • 被protected和private修饰的成员只能在类中访问
  • struct的默认访问限定符是public,而class是private

通过访问限定符对成员进行修饰,想让外部访问的成员和不想让外部访问的成员,达到封装的效果

3. 类的作用域和实例化


3.1 作用域

在上篇文章中说到过,C++中有4种域:

  • 局部域
  • 全局域
  • 类域
  • 命名空间域

我们定义出来的类也是一种域,外部想要使用类中的成员,必须指定域

3.2 实例化

用类创建变量,叫做类的实例化;类的定义不会占用空间,只有实例化才会占用内存

3.3 类大小的计算

一个类中既有函数,又有变量,怎么计算一个类的大小呢?

//有成员函数和成员变量
class C1
{
	void Fun1() {}
	int _year;
};

//只有成员函数
class C2
{
	void Fun2() {};
};

//空类
class C3
{};

int main()
{
	cout << sizeof(C1) << endl;// 4
	cout << sizeof(C2) << endl;// 1
	cout << sizeof(C3) << endl;// 1

	return 0;
}

实际上,如果想调用类中的函数,没有必要再复制函数的内容,因为每次调用的函数都是同一份;因此,类中函数的地址存放在公共代码区;计算类大小时,只计算成员变量所占的大小,并且要符合结构体内存对齐规则

注意:空类占1byte,很多人好奇为什么不是0byte,因为得知道这个类是存在的,只是什么都没有,如果是0byte,连内存都不占,我怎么知道它存在

说到结构体内存对齐,这里再提一个小问题:为什么要内存对齐?

  1. 效率方面,由于硬件在设计时,一次只能读取4byte或8byte,内存对齐更有利于拿取内存中的数据

  2. 更主要的原因,是因为硬件在读取时,只能在对应类型的整数倍处读取,不能在任意地方读取

    在这里插入图片描述

class V
{
public:
	void Fun()
	{
		cout << "Fun()" << endl;
	}
};

int main()
{
	V v1;
	V* p1 = &v1;
	p1->Fun();//打印Fun()

	V* p2 = nullptr;
	p2->Fun();//打印Fun()

	return 0;
}

上面的代码中,p1访问类中的Fun()函数我们不难理解,但为什么p2明明是空指针,仍然能访问Fun()函数?

其实这个问题前面已经说过,是因为Fun函数在公共代码区;编译器首先会查找Fun()函数,既然它不在类中,那就没必要解引用了

4. this指针


4.1 this指针的引出

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;
	Date d2;
	d1.Init(2024, 1, 27);
	d2.Init(2024, 2, 27);
	d1.Print();// 打印2024-1-27
	d2.Print();// 打印2024-2-27

	return 0;
}

上面的代码中,调用Init()函数时,编译器怎么知道应该是去初始化d1,而不是d2呢?

实际上,编译器默认给每个成员函数增加了一个隐式指针参数,在调用时也会默认增加一个指针,该指针指向目标的地址,这就是this指针

4.2 this指针的特性

  • 我们不能在形参或实参中显示this指针

  • 我们可以在成员函数内部使用this指针,如果不对成员变量加上this指针,编译器会默认帮我们加上

    void Print()
    {
    	cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
    }
    
  • 不能对this指针进行修改,因为this指针被const修饰

    Date* const this
    
  • this指针在哪?

    this指针在栈上,因为它实际上是一个形参,只不过传参和使用由编译器自动帮我们完成

class A1
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}

private:
	int _a;
};

class A2
{
public:
	void Print()
	{
		cout << _a << endl;
	}

private:
	int _a;
};

int main()
{
	A1* p1 = nullptr;
	A2* p2 = nullptr;
	p1->Print();// 正常打印
	p2->Print();// 程序崩溃
	return 0;
}

出现两种情况,第一种前面已经解释过;第二种,因为调用Print()函数时,编译器默认帮我们传了p2,在Print()函数中,_a实际上是this->_a,因为this位nullptr,所以发生了空指针的解引用,程序崩溃

5. 默认成员函数


一个类中,其实默认含有一系列函数,这些函数如果程序员自己不定义,编译器会自动帮我们定义;有了这些函数,我们可以更加方便写代码,这就叫默认成员函数,主要有六个成员函数

在这里插入图片描述

5.1 构造函数

相信大家都犯过这样的错误,用C语言实现栈时,忘记初始化栈导致程序运行错误;为了防止未初始化函数就直接使用这种情况,祖师爷引入了构造函数的概念

5.1.2 概念

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;
	Date d2;
	d1.Init(2024, 1, 27);
	d2.Init(2024, 2, 27);
	d1.Print();
	d2.Print();

	return 0;
}

拿日期函数举例,我们发现每次调用Init()函数都要自己手动初始化,显得比较麻烦,能不能在类对象实例化时就直接给我们初始化了呢?

构造函数是一个特殊的成员函数,名字和类相同,创建类对象时由编译器自动调用,确保每一个成员变量都有一个初始值

5.1.3 特性

  • 函数名与类名相同

  • 无返回值

  • 对象实例化时编译器自动调用

  • 可以重载

    class Date
    {
    public:
        //无参构造函数
    	Date(){}
    	
        //带参构造函数
    	Date(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1;// 调用无参构造函数
    	Date d2(2024, 1, 27);// 调用带参构造函数
    
    	return 0;
    }
    //注意:如果是调用无参构造函数,不能写成【Date d1()】,因为这就和函数声明冲突了,编译器无法识别这到底是函数声明,还是调用无参构造函数
    
  • 如果类中没有显式定义构造函数,那么编译器会自动生成一个无参构造函数;而如果类中有显式构造函数,编译器就不会生成默认构造函数

  • 默认构造函数不会对内置类型进行初始化;而如果有自定义类型,会去调用自定义类型的构造函数

    class Date
    {
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1;
        //实例化之后,发现d1中的成员变量是随机值,也就是说默认构造函数没有做任何事情
    
    	return 0;
    }
    
    class Week
    {
    public:
    	Week()
    	{
    		_week = 0;
    	}
    private:
    	int _week;
    };
    
    class Date
    {
    private:
    	int _year;
    	int _month;
    	int _day;
    
    	Week _date;
    };
    
    int main()
    {
    	Date d1;
    
    	return 0;
    }
    //对于_date,编译器会去调用类Week中的构造函数Week()
    

    不管怎么看,构造函数的这个特性都不太对劲,我本来想让你帮我完成初始化的工作,结果你什么都没做,有点不太合理;因此C++11增加了新的规则,可以在成员变量声明时给默认值

    class Date
    {
    private:
    	int _year = 1;
    	int _month = 1;
    	int _day = 1;
    };
    
  • 无参构造函数、全缺省构造函数、编译器生成的构造参数,都可以叫做默认构造参数,且三个只能存在一个

    class Date
    {
    public:
    	Date() 
    	{
    		_year = 2024;
    		_month = 1;
    		_day = 27;
    	}
          
    	Date(int year = 2024, int month = 1, int day = 27)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
          
    private:
    	int _year;
    	int _month;
    	int _day;
    };
          
    int main()
    {
    	Date d1;
          
    	return 0;
    }
    //编译器会报错,因为实例化d1时,没有指定数据,表明是调用默认构造函数,而类中有两个默认构造函数,编译器无法区分,因此会报错
    

5.2 析构函数

5.2.1 概念

析构函数是类中特殊的函数,当对象销毁时,他会自动帮我们完成内存清理的工作

5.2.2 特性

  • 析构函数的函数名是在类名前加上~

  • 无参数无返回值

  • 一个类只能有一个析构函数,若没有显式定义,则调用默认析构函数

  • 析构函数不能重载

  • 对象生命周期结束时,自动调用析构函数

    class Stack
    {
    public:
    	Stack(int capacity = 4)
    	{
    		_arr = (int*)malloc(sizeof(int) * capacity);
    		if (_arr == NULL)
    		{
    			perror("mallco fail");
    			return;
    		}
    		_size = 0;
    		_capacity = capacity;
    	}
    
    	void Push(int x)
    	{
    		//CheckCapacity();
    		_arr[_size++] = x;
    	}
    
    	void Pop()
    	{
    		assert(_size > 0);
    		_size--;
    	}
    
    	void Print()
    	{
    		for (int i = 0; i < _size; i++)
    		{
    			printf("%d\n", _arr[i]);
    		}
    	}
    
    	~Stack()
    	{
    		free(_arr);
    		_arr = NULL;
    		_size = _capacity = 0;
    	}
    
    private:
    	int* _arr;
    	int _size;
    	int _capacity;
    };
    
    int main()
    {
    	Stack s;
    	s.Push(1);
    	s.Push(2);
    	s.Print();
    	s.Pop();
    	s.Print();
    
    	return 0;
    }
    
  • 如果没有显式析构函数,编译器调用默认析构函数;默认析构函数会调用自定义类型成员中的析构函数

    class Time
    {
    public:
    	~Time()
    	{
    		cout << "Time()" << endl;
    	}
    
    private:
    	int _value = 1;
    };
    
    class Date
    {
    private:
    	int _year = 2024;
    	int _month = 1;
    	int _day = 27;
    	Time _t;
    };
    
    int main()
    {
    	Date d1;
    
    	return 0;
    }
    //Date类中的内置类型_year、_month、_day,在d1销毁时自己释放,不需要析构函数对其进行资源释放
    //对于自定义类型_t,d1在销毁前需要对其进行检查,是否进行资源释放,但main函数中不能直接访问Time类
    //因此Date类中编译器生成的默认析构函数会去调用Time类中的析构函数
    
  • 如果类中没有申请资源时,析构函数可以不写,由编译器自动生成;但如果有申请资源,需要程序员自己写好对应的析构函数

  • 程序结束时,析构函数的调用规则满足后定义的先析构:局部变量–>局部静态变量–>全局或全局静态变量

    class Date
    {
    public:
    	Date(int year = 1)
    	{
    		_year = year;
    	}
    
    	~Date()
    	{
    		cout << "Date()->" << _year << endl;
    	}
    
    private:
    	int _year;
    	int _month = 1;
    	int _day = 1;
    };
    
    Date d4(4);
    Date d5(5);
    static Date d6(6);
    
    void fun()
    {
    	Date d7(7);
    	static Date d9(9);
    	Date d8(8);
    }
    
    int main()
    {
    	fun();
    	Date d1(1);
    	Date d2(2);
    	static Date d3(3);
    
    	return 0;
    }
    //正确的析构顺序:8->7->2->1->3->9->6->5->4
    

5.3 拷贝构造函数

5.3.1 概念

拷贝构造函数是一种成员函数,完成一个对象的拷贝工作

5.3.2 特性

  • 拷贝构造函数是构造函数的一个重载形式

  • 拷贝构造函数只能有一个形参(用const修饰),且类型必须是类名的引用,如果类型是类名,会引发无穷递归的问题

    C++中规定,对类进行传值传参,首先得调用该类的拷贝构造函数在这里插入图片描述

由此会一直调用下去,所以拷贝构造函数的参数类型必须是类名引用类型;同时,为了防止修改掉原来的数据,最好加上const修饰

  • 如果拷贝构造函数没有显示定义,则编译器会默认生成拷贝构造函数;与构造函数不同,默认生成的拷贝构造函数对于内置类型会按字节顺序拷贝数据,对于自定义类型,会去调用它的拷贝构造函数;按字节顺序拷贝数据叫做浅拷贝

    class Time
    {
    public:
    	Time() = default;
    
    	// Date d2(d1)
    	Time(const Time& t)
    	{
    		_hour = t._hour;
    		_minute = t._minute;
    		_second = t._second;
    	}
    
    private:
    	int _hour = 1;
    	int _minute = 1;
    	int _second = 1;
    };
    
    class Date
    {
    private:
    	int _year = 2024;
    	int _month = 2;
    	int _day = 26;
    
    	Time _t;
    };
    
    int main()
    {
    	Date d1;
    	Date d2(d1);
    
    	return 0;
    }
    

拷贝构造函数与之前的成员函数不同,它会帮我们完成拷贝,那么是不是说就不需要我们写拷贝构造函数了呢?对于上面的日期类,好像是这样的,但如果换成栈类呢?

typedef int DataType;

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

	void Push(const DataType& data)
	{
		//CheckCapacity();
		_array[_top] = data;
		_top++;
	}

	~Stack()
	{
		free(_array);
		_array = nullptr;
		_top = _capacity = 0;
	}

private:
	DataType* _array;
	int _top;
	int _capacity;
};

int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	Stack s2(s1);

	return 0;
}
// 执行这段代码,我们发现程序崩了

经过调试,我们发现s2确实完成了s1的拷贝在这里插入图片描述

但仔细分析一下,s1中的array是我们自己申请的空间,程序在结束前,会去调用析构函数,释放掉s1中array的空间,而此时s2仍指向被释放掉的空间,这就出现了野指针的问题;下次释放s2时,对同一块空间释放了两次,因此程序会崩掉

由此总结,对于不需要申请空间的类,拷贝构造函数确实可以不写;而如果有申请空间,需要我们自己写好拷贝构造函数;而对于申请空间的拷贝我们叫做深拷贝

//需要我们自行完成拷贝构造函数
Stack(const Stack& s)
{
	_array = (DataType*)malloc(sizeof(DataType) * s._capacity);
	if (_array == nullptr)
	{
		perror("malloc fail");
		exit(-1);
	}
	memcpy(_array, s._array, sizeof(DataType) * s._top);
	_top = s._top;
	_capacity = s._capacity;
}

6. 运算符重载


为了代码的可读性,C++提供了对运算符赋予新的涵义的操作,也叫运算符重载

对于日期的比较,按照之前的思路写代码:

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

	bool CompareEqual(const Date& d2)
	{
		return _year == d2._year
			&& _month == d2._month
			&& _day == d2._day;
	}

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

		return false;
	}

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

int main()
{
	Date d1(2024, 2, 28);
	Date d2(2024, 2, 26);
	cout << d1.CompareEqual(d2) << endl;
	cout << d1.CompareMore(d2) << endl;

	return 0;
}

这样写能完成我们想要的结果,但在函数的命名上有些问题;有时我们并不能通过函数名就知道该函数是做什么的;我希望通过函数名就能知道该函数比较的是什么;C++能够使用operator+运算符对函数命名

class Date
{
public:
	Date(int year = 1900, 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;
	}

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

		return false;
	}

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

int main()
{
	Date d1(2024, 2, 28);
	Date d2(2024, 2, 26);

	//cout << d1.operator==(d2) << endl;
	//cout << d1.operator>(d2) << endl;

	cout << (d1 == d2) << endl;
	cout << (d1 > d2) << endl;

	return 0;
}
函数比较的是什么;C++能够使用operator+运算符对函数命名

```C++
class Date
{
public:
	Date(int year = 1900, 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;
	}

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

		return false;
	}

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

int main()
{
	Date d1(2024, 2, 28);
	Date d2(2024, 2, 26);

	//cout << d1.operator==(d2) << endl;
	//cout << d1.operator>(d2) << endl;

	cout << (d1 == d2) << endl;
	cout << (d1 > d2) << endl;

	return 0;
}

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

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

相关文章

【前端素材】推荐优质后台管理系统Space平台模板(附源码)

一、需求分析 综上所述&#xff0c;后台管理系统在多个层次上提供了丰富的功能和细致的管理手段&#xff0c;帮助管理员轻松管理和控制系统的各个方面。其灵活性和可扩展性使得后台管理系统成为各种网站、应用程序和系统不可或缺的管理工具。 当我们从多个层次来详细分析后台…

【c语言】字符函数和字符串函数(上)

前言 在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了⽅便操作字符和字符串&#xff0c;C语⾔标准库中提供了⼀系列库函数~ 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 前言 1. 字符分…

代码随想录刷题第43天

第一题是最后一块石头的重量IIhttps://leetcode.cn/problems/last-stone-weight-ii/&#xff0c;没啥思路&#xff0c;直接上题解了。本题可以看作将一堆石头尽可能分成两份重量相似的石头&#xff0c;于是问题转化为如何合理取石头&#xff0c;使其装满容量为石头总重量一半的…

DataSpell 2023:专注于数据,加速您的数据科学之旅 mac/win版

JetBrains DataSpell 2023是一款专为数据科学家和数据分析师设计的集成开发环境&#xff08;IDE&#xff09;。这款IDE提供了强大的数据分析和可视化工具&#xff0c;旨在帮助用户更快速、更高效地进行数据科学工作。 DataSpell 2023软件获取 DataSpell 2023在保持其一贯的数…

【零代码研发】OpenCV实验大师工作流引擎C++ SDK演示

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; OpenCV开发痛点 传统图像算法开发最好的开源解决方案是OpenCV视觉库&#xff0c;但是OpenCV中收录了2000的传统算法&#xf…

SpringMVC 学习(七)之报文信息转换器 HttpMessageConverter

目录 1 HttpMessageConverter 介绍 2 RequestBody 注解 3 ResponseBody 注解 4 RequestEntity 5 ResponseEntity 6 RestController 注解 1 HttpMessageConverter 介绍 HttpMessageConverter 报文信息转换器&#xff0c;将请求报文&#xff08;如JSON、XML、HTML等&#x…

【Java EE初阶二十三】servlet的简单理解

1. 初识servlet Servlet 是一个比较古老的编写网站的方式&#xff0c;早起Java 编写网站,主要使用 Servlet 的方式&#xff0c;后来 Java 中产生了一个Spring(一套框架)&#xff0c;Spring 又是针对 Servlet 进行了进一步封装,从而让我们编写网站变的更简单了&#xff1b;Sprin…

打造自己的AIGC应用(一)入门篇

其实细数AI的发展历程非常之久&#xff0c;而让AI的应用一下子出现在人们眼前的其实就是ChatGPT的出现&#xff0c;这意味着AIGC应用已经从概念为王变的非常实用了。伴随着ChatGPT的出现&#xff0c;大量的开源大模型也如雨后春笋一样出现。就现在而言&#xff0c;打造一个自己…

49 kmalloc 的实现

前言 这里说的是 内核中分配小对象的一种内存分配方式 kmalloc 呵呵 经典程度不必多说了, 内核使用的大多数 不经常使用的小对象基本上是基于 kmalloc 这里 我们来看一下 kmalloc, kmalloc 基于 slab, 因此 这里不会赘述太多 如何分配对象 kmalloc 是系统中分配小空间…

面试题JS篇

目录 Js 基本数据类型有哪些Ajax 如何使用如何判断一个数据是 NaN&#xff1f;Js 中 null 与 undefined 区别闭包是什么&#xff1f;有什么特性&#xff1f;对页面会有什么影响JS中模块化的方法Js 中常见的内存泄漏什么是事件冒泡&#xff1f;事件委托是什么&#xff1f;如何确…

Arduino中安装ESP32网络抽风无法下载 暴力解决办法 python

不知道什么仙人设计的arduino连接网络部分&#xff0c;死活下不下来。&#xff08;真的沙口&#xff0c;第一次看到这么抽风的下载口&#xff09; 操作 给爷惹火了我踏马解析json选zip直接全部下下来 把这个大家的开发板管理地址下下来跟后面python放在同一目录下&#xff0c…

redis——客户端

Redis是一个典型一对多服务器程序&#xff0c;一个服务器可以与多个客户端进行网络连接&#xff0c;每隔客户端可以向服务器发送命令请求&#xff0c;而服务器则接收并处理客户端发送的命令请求&#xff0c;并向客户端返回命令请求。 通过是一个I/O多路复用技术实现的文件事件处…

09 Redis之分布式系统(数据分区算法 + 系统搭建与集群操作)

6 分布式系统 Redis 分布式系统&#xff0c;官方称为 Redis Cluster&#xff0c;Redis 集群&#xff0c;其是 Redis 3.0 开始推出的分布式解决方案。其可以很好地解决不同 Redis 节点存放不同数据&#xff0c;并将用户请求方便地路由到不同 Redis 的问题。 什么是分布式系统?…

blender bvh显示关节名称

导入bvh&#xff0c;菜单选择布局&#xff0c;右边出现属性窗口&#xff0c; 在下图红色框依次点击选中&#xff0c;就可以查看bvh关节名称了。

如何保证档案室符合建设标准要求

保证档案室符合建设标准要求需要考虑以下几个方面&#xff1a; 1. 总体规划&#xff1a;合理规划档案室的布局和大小&#xff0c;确保能够满足现有和未来的档案存储需求。考虑档案室的空间利用率、通风、照明、安全出口等因素。 2. 档案室环境&#xff1a;档案室的环境应具备稳…

SQL Server 开发环境配置教程(SSMS+SQL Prompt)

背景 记录一下 SQL Server 常用开发软件 体验了各种数据库IDE(DBeaver、Navicat、DataGrip)之后综合下来还是感觉 SSMSSQL Prompt 对于 SQL Server 最好用&#xff0c;所以在此记录一下配置过程 数据库可视化管理工具SSMS 官方下载地址&#xff1a; https://learn.microsoft…

【Java程序设计】【C00317】基于Springboot的智慧社区居家养老健康管理系统(有论文)

基于Springboot的智慧社区居家养老健康管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧社区居家养老健康管理系统设计与实现&#xff0c;本系统有管理员、社区工作人员、医生以及家属四种角色权限 管…

01|Mysql底层存储引擎

1. 聚集索引&#xff08;聚簇&#xff09;与非聚集索引 1.1 聚集索引 索引和数据存储在一起。叶子节点存储了完整的数据记录&#xff1b; 1.2 非聚集索引 MyISAM存储引擎就是非聚集索引&#xff0c;索引和数据文件是分开存储的。索引在MYI文件中&#xff0c;数据在MYD文件中…

Zoho ToDo 满足您的需求:任务管理满足隐私和安全要求

任务管理工具已经成为我们日常生活中不可或缺的一部分&#xff0c;它们帮助我们处理各种事务&#xff0c;从杂项和愿望清单到管理截止日期和资源。这些工具不仅仅是简单的任务列表&#xff0c;它们掌握了项目的蓝图、雄心勃勃的目标和完成的最后期限。然而随着这些工具的使用越…

装配制造业的MES系统种的物料齐套技术

装配是制造企业涉及产品生产加工最为普遍的一种模式&#xff0c;包括汽车、电子、电器、电气等行业。经研究表明&#xff0c;装配在整个产品制造总成本中所占比例超过了50%&#xff0c;所占用的总生产时间比例在40%-60%&#xff0c;直接影响着产品质量和成本。装配制造非常强调…