C++ 类和对象(中) 构造、析构、(运算符重载)赋值运算符、const成员函数等;有c语言基础更好

news2024/9/20 3:46:34

 在最开始如果有些,看不懂可以去看上一篇 -->类和对象 上

阅读时要结合代码一起思考

 学习完日期类可以看看以下oj题

KY111 日期差值

计算一年的第几天

1. 类的默认成员函数

  1. 默认成员函数,编译器会自动生成的成员函数被成为默认成员函数,一个类默认不写的情况下编译器会默认生成一下6个默认成员函数,最重要的是前4个,取地址重载不是很重要,了解即可。默认成员函数比较复杂,但是很重要;在开始之前需要提出两个问题
    1. 不写时,编译器默认生成的函数行为时什么,是否满足我们的需求?
    2. 编译器不满足我们的需求,那么要怎么实现?

2. 构造函数

构造函数是特殊的成员函数,构造函数的任务不是开辟空间创建对象(经常使用的局部对象是函数栈帧创建的,空间就开辟好了),构造函数的作用是在对象实例化的时初始化对象用的,构造函数的本质和Init函数功能一样,构造函数自动调用的特点就完美的替代了Init(初始化函数)

  1. 构造函数的特点:
    1. 函数名与类名相同;对象实例化后就会自动调用构造函数
    2. 无返回值;在类里写构造函数时,不用写返回值(也不用写void,C++规定的)
    3. 对象实例化的时候系统会自动调用对应的构造函数;可以通过函数重载找到对应函数
    4. 函数可以重载;但是要注意的是无参和全缺省函数不可以同时出现,语法上没问题,但是调用会出问题
    5. 如果类中没有显示定义构造函数,则C++编译器会自动生成一个无参的构造函数(大部分情况下编译器生成的构造函数,是达不到我们想要的效果的),如果自己写了编译器就不会自己生成
    6. 无参构造函数、全缺省构造函数、不写构造函数时编译器默认生成的构造函数,这些都是默认构造函数;但是这三个函数有且只有一个存在,不能同时存在;总的来说就是不传实参就可以调用的构造就是默认构造
    7. 不写构造函数,编译器默认生成的构造,对内置类型成员变量的初始化没有要求(有可能不会处理),初不初始化看编译器;自定义类型成员变量要调用成员变量的默认构造函数初始化才行,没有默认构造函数就会报错,要初始化成员变量就需要用到初始化列表才行 初始化列表比较复杂,后面再说
#include<iostream>
using namespace std;
class Date
{
public:
	// 1.无参构造函数
	//Date()
	//{
	//	_year = 1;
	//	_month = 1;
	//	_day = 1;
	//}
	 2.带参构造函数
	//Date(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	 //3.全缺省构造函数
	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();//调用无参的后面不要接括号,因为可能会被误以为是函数声明
	Date d1;
	d1.Print();
	
	//Date d2(2000,3,1);//即代表对象实例化,也会去调用默认构造函数,如果要传参直接有对应值
	//d2.Print();

	Date d3(2222);
	d3.Print();

	return 0;
}
  1. 全缺省默认构造函数,既满足无参的,也满足带参的
  2. 无参和全缺省的函数不能同时出现,函数重载没问题,但是调用函数传递无参的时候,系统调用那个呢?全缺省的也可以不传参调用
  3. 注意:写全缺省参数,只需要写在声明的地方即可

  1. 这种情况有资源申请的需要自己写构造,什么情况不需要自己写呢?
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};
int main()
{
	//有了默认构造函数
	Stack s1;
	return 0;
}
  1. 编译器自动生成的构造,两个栈模拟队列,这种就不需要自己写
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};
class Myqueue
{
public:
	//
private:
	Stack s1;
	Stack s2;
	int size;//编译器可能没有初始化,并没有处理内置类型
};
int main()
{
	//有了默认构造函数
	//Stack s1;

	//编译器自动生成的构造
	Myqueue m;
	return 0;
}
  1. 总结:大多数情况下都是需要自己写构造函数的,只有少数情况像Myqueue、Stack有默认构造才不需要写;这里的Myqueue自动生成就可用了
  2. 所以大多数的情况:应该写就都要写

有资源申请的一般都要写,构造/析构/复制拷贝/拷贝

3. 析构函数

析构函数与构造函数相反,C++规定对象在销毁的时候会自动调用析构函数,完成对象中的资源释放,比如局部的对象是存放在栈帧里面的,函数结束局部对象就会跟着销毁,所以我们不用管;他和Stack的Destroy函数的功能类似,也是用来销毁空间的;如果没有malloc开辟的空间是不需要资源释放的,所以按理来说上面写的Date是不需要析构函数的

  1. 析构函数的特点:
    1. 要使用析构函数,在类名字的前面加上 ~ 这个符号,就是析构函数了
    2. 单个局部域有多个对象,C++规定后定义的先析构
    3. 和构造函数类似,如果不写析构,编译器自动生成的析构函数对内置类型的成员不做处理;自定义类型会调用自己的析构函数
    4. 一个类只能有一个析构函数,如果没有显示写出析构函数,那么系统会自动生成默认的析构函
    5. 对象生命周期结束前会自动调用析构函数
    6. 无参数无返回值。(也和构造函数一样不用加void)
    7. 如果没有资源申请(例:malloc动态开辟的),析构函数可以不写,直接使用编译器自动生成的析构函数即可,如果有资源申请,就一定要写析构函数,否则会导致资源泄漏;
    8. 我们显示写析构函数,对于自定义类型成员还是会调用自己的析构函数,最后自定义类型成员无论什么情况都会自动调用析构函数(可以自行观察~Myqueue()析构函数的变化)
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
    // Stack的析构,如果不写就会导致内存泄漏
	~Stack()
	{
		free(_a);
		_capacity = _top = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};
class Myqueue
{
public:
	//自己写了析构函数,但是没有做处理;此时为了防止内存泄漏编译器还是会去调用自动生成的析构函数
	~Myqueue()
	{
		cout << "^Myqueue" << endl;
	}

private:
	Stack s1;
	Stack s2;
	//int size;//编译器可能没有初始化,并没有处理内置类型
};
int main()
{
	//有了默认构造函数
	//Stack s1;

	//编译器自动生成的构造
	Myqueue m;
	return 0;
}

4. 拷贝构造函数

  1. 一个构造函数如果第一个参数是自身类 类型的引用,此时就是拷贝构造函数,除此以外,后面也可以接其他的参数,这个也是拷贝构造函数;拷贝构造是一个特殊的构造函数
  2. 个人理解:拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化,本质上还是构造函数
  3. 拷贝构造的特点:
    1. 拷贝构造函数是构造函数的一个重载
    2. 拷贝构造函数只有一个并且必须是类 类型对象的引用;如果使用传值传参会在语法上引发无穷递归,编译器会直接报错

C++规定传值传参会调用拷贝构造

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
    //拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << this->_year << '/' << this->_month << '/' << this->_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024,7,31);
	d1.Print();
    //拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化
	Date dd(d1);

    //也是拷贝构造
    Date dd2 = d1;
	dd.Print();
    return 0;
}

  1. C++规定自定义的类型对象进行拷贝必须调用拷贝构造,自定义类型传值传参和传值返回都会调用拷贝构造完成(下一章会详细说明)
  2. 如果没有显示定义拷贝构造,编译器会自动生成一个拷贝构造函数自动生成的拷贝构造对内置类型会完成值拷贝或者浅拷贝(一个一个字节拷贝、和memcpy类似)对于自定义类型的成员变量会调用它的拷贝构造 主要就是看类的成员变量 是否是 内置还是自定义
  3. Date这样的类成员变量都是内置类型没有指向资源,编译器自动生成的拷贝构造就可以完成拷贝的需要,所以不需要自己显示写拷贝构造。Stack有资源申请,编译自动生成的拷贝构造(值拷贝或者浅拷贝)不够用了,所以此时需要显示写拷贝构造而且得是深拷贝构造;当然也有需要不要的,比如例子中的myqueue有着天然的优势,自动生成的拷贝构造就够用了,里面的stack会自动调用自己的拷贝构造
    1. 指向同一块空间,你改变了也会影响我
    2. 对于有资源申请的,浅拷贝会析构两次
#include <iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	// st2(st1)
	Stack(const Stack& st)
	{
		cout << "Stack(const Stack& st)" << endl;

		// 需要对_a指向资源创建同样大的资源再拷贝值
		_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		memcpy(_a, st._a, sizeof(STDataType) * st._top);
		_top = st._top;
		_capacity = st._capacity;
	}

	void Push(STDataType x)
	{
		if (_top == _capacity)
		{
			int newcapacity = _capacity * 2;
			STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
				sizeof(STDataType));
			if (tmp == NULL)
			{
				perror("realloc fail");
				return;
			}
			_a = tmp;
			_capacity = newcapacity;
		}
		_a[_top++] = x;

	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

class myqueue
{
	Stack t1;
	Stack t2;
};
int main()
{
	Stack s1;
	s1.Push(20);
	s1.Push(10);

	//假设此时还没有写赋值拷贝构造,此时调用默认的拷贝构造,是浅拷贝,把地址也拷贝过来了就会导致两个对象指向同一块空,而且还会出现两次析构,会导致程序崩溃
	Stack s2(s1);

    s2 = s1;// 这个也是赋值拷贝构造

	//myqueue有着天然优势,不需要自己写拷贝构造,编译器自己生成的就能用
	myqueue my;
	myqueue my2(my);
	return 0;
}
  • 传值返回会产生临时对象并调用拷贝构造,传值引用返回:不会产生拷贝,是返回一个对象的别名(引用);如果此时返回的对象是一个存在局部域内的对象,函数结束返回时会调用析构函数空间会被回收,此时返回去的就是一个野引用和C语言的野指针差不多,如果要使用引用返回应该避免这种问题;只有在返回对象时出函数保证对象不被销毁,此时才可以使用引用返回

//一般情况下参数传参都用引用,除非有特殊需要
//传值返回的时候会调用拷贝构造
Stack fun2(Stack& s)
{
	s.Push(10);
	s.Push(20);
	return s;
}

//引用返回不会去调用拷贝构造,相当于返回别名了
//Stack& fun2(Stack& s)
//{
//	s.Push(10);
//	s.Push(20);
//	return s;
//}


// 这个对象在局部域,会导致返回对象别名消失,返回了一个野引用
Stack& fun3()
{
	Stack ten1;
	return ten1;
}

int main()
{
	Stack s1;
	Stack ret = fun2(s1);

	Stack ret2 = fun3();
	return 0;
}

5.赋值运算符重载

5.1. 运算符重载

  1. 可以发现除了编译器自带的加、减、乘、除、大于、小于、等于,这些常用的运算符编译器等等,都可完成运算;但是比较年月日的呢?或者说这个食品还有多久过期这些;编译器自带的就不够用了,所以有了运算符重载
  2. 运算符被用于类 类型的对象时,C++语言让我们通过运算符重载的形式来给定义新的含义(就是这个运算符不再是原本的意思)。C++规定类 类型对象使用运算符时,必须将d1 == d2,转换成调用对应的运算符重载 operator==(d1,d2),如果没有对应的运算符重载会编译报
    1. 注:d1和d2,都是下方例子中的对象
  3. 运算符重载是具有特殊名字的函数,它的名字由operator和后面要定义的运算符共同构成;和其他函数一样,它具有返回类型参数列表以及函数体
  4. 重载运算符函数的参数个数和这个运算符作用的运算对象个数一样多,一元运算符( -- 、++ 等等)有一个参数二元运算符有两个参数,二元运算符左边计算对象传递给第一个参数,右边计算对象传递给第二个参数
  5. 如果重载运算符函数是一个成员函数,则它是一个运算对象默认传递隐式的this指针,所以参数比运算对象少一个
#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year = 2, int month = 2, int day = 2)//构造函数和类同名
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << '/' << _month << '/' << _day <<endl;
	}
	//方法2:间接访问
	int GetYear()
	{
		return _year;
	}
	int GetMonth()
	{
		return _month;
	}
	int GetDay()
	{
		return _day;
	}
	//方法4,这样就只需要传递一个参数,有隐含的this指针
	bool operator==(Date d2)
	{
		return _year == d2._year
			&& _month == d2._month
			&& _day == d2._day;
	}
//方法1,放开权限 //private:
private:
	int _year;
	int _month;
	int _day;
};

//3.一般情况下,成员变量是不希望直接被访问的,所以有两个方法,第一:间接访问、第二:运算符重载放到类里面
//bool operator==(Date d1, Date d2)
//{
//	return d1.GetYear() == d2.GetYear()
//		&& d1.GetMonth() == d2.GetMonth()
//		&& d1.GetDay() == d2.GetDay();
//}

int main()
{
	//实例化类两个对象,还调用了构造函数
	Date d1(2024,7,29);
	Date d2(2024,7,29);

    //重载为全局时,要考虑如何访问类的私有成员
    //1.放开类的私有限定
    //2.在类里面提供一个get函数获取私有对象
    //3.友元函数,后面再说
    //4.重载为成员函数,推荐第4种方法
    
	// 方法1
	//operator==(d1,d2);
	//d1 == d2;


	//方法4,这里d1表示d1里面的运算符重载,而调用类里面的函数时默认传递一个this指针,第二个参数是d2
	d1.operator==(d2);
	//运算符重载
	if (d1 == d2)
	{
		cout << "Yes" << endl;
	}
	else
	{
		cout << "No" << endl;
	}
	return 0;
}

需要注意的是使用第4种方法只要,传递一个参数,因为有一个隐含的this指针

如果同时出现两个运算符重载,一个在类里面另外一个是全局,会优先调用类里面

  • 运算符重载后,优先级和结合性还是和内置类型运算符保持一致
    1. 优先级:运算的先后顺序
    2. 结合性:当运算符两侧的优先级一样时,结合性决定了运算对象与运算符的结合顺序;意思就是优先级相同是从左向右开始算,还是从右向左开始算
    3. 假设: a + b - c 此时是从右向左,那么就会先算 b - c然后再加上a
  • 不能使用语法中没有的符号来创建新的操作符:比如operator@。
  • ( .*    ::    sizeof   ?: (三目)    . ) 注意这5个运算符不能重载 ;看下面举例 .* 符号的使用方法
#include <iostream>
using namespace std;
class V
{
public:
	void fun()
	{
		cout << "V::fun()" << endl;
	}
private:

};
void print()
{
	cout << "print()" << endl;
}
int main()
{
	// V::fun ,类域访问限定符,访问类域里面的函数;V.fun 是调用这个函数
	typedef void (V::*vf)();//成员函数指针,重命名为vf ,意思是将这个函数类型重命名
	typedef void (*vp)();

	vf vget = nullptr;//函数指针

	vget = &V::fun;//C++规定需要用取地址符才能拿到函数地址

	
	V va;
	// 拿到这个类域里面函数的地址,可是在类域里面所以用到了(va.*vget)();这个方法也是回调函数的用法
	(va.*vget)();                 

	//如果是全局通过函数指针的方式调用是不用 va. 的
	vp vprint = &print;
	(*vprint)();
	return 0;
}
  1. 重载操作符最少有一个类型是类 类型参数,不可以通过运算符重载来改变内置类型的原本意思,例如:operator+(int n,int m);就是错的,两个参数都是内置类型了
  2. 一个类需要重载哪些运算符,要看你重载的运算符有没有意义,比如Date类的运算符operator+,可以计算多少天后的哪一个,perator* 对于日期类就没意义了

5.2. 赋值运算符重载

赋值运算符重载是一个默认成员函数,用来完成两个已经存在的对象直接的拷贝赋值;这里需要区分拷贝构造,拷贝构造:一个同类型的(旧)对象初始化给另一个要创建的对象

  1. 赋值运算符重载是一个运算符重载,规定必须重载为成员函数,建议写成const 当前类 类型的引用,不然会调用拷贝构造
  2. 也有返回值,不过更加推荐写成类 类型的引用,可以减少拷贝,提高效率;有返回值的目的为了支持连续赋值

  1. 没有显示写的时候,编译器会自动生成一个赋值运算符重载,和默认拷贝构造函数类似,对内置成员类型进行值拷贝(浅拷贝),对自定义类型调用自己对应的赋值重载函数
  2. 前面说过对域stack这样的类,虽然也是内置类型,但是有了资源申请,编译器自动生成的浅拷贝达不到要求,所以也需要自己实现深拷贝;像之前的Myqueue这样的类型,编译器自动生成的就够用;
  3. 如果显示写了析构函数,那么就需要写赋值运算符重载,反之不需要
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	//拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	// dd2 = d2 赋值重载拷贝
	void operator=(Date d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
    //建议写成 带引用的方式,可以减少拷贝
	void operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << this->_year << '/' << this->_month << '/' << this->_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024, 7, 31);
	Date d2(1999, 12, 21);
	d1.Print();
	//拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化
	Date dd(d1);

	//也是拷贝构造
	Date dd2 = d1;

	// 赋值重载
	dd2 = d2;
	return 0;
}

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	//拷贝构造
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	// dd2 = d2 赋值重载拷贝
	//void operator=(Date d) 拷贝构造一份给了d,然后在赋值拷贝给d1
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;//解引用返回,对this一级指针;返回dd2的别名
	}
	void Print()
	{
		cout << this->_year << '/' << this->_month << '/' << this->_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024, 7, 31);
	Date d2(1999, 12, 21);
	d1.Print();
	//拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化
	Date dd(d1);

	//也是拷贝构造
	Date dd2 = d1;


	// 赋值重载
	dd2.operator=(d2);
	dd2 = d1 = d2;


	d1 = d2 ;
	return 0;
}

5.3. 日期类实现

此篇内容较多,所以单独分离一篇  ---->日期类

6. 取地址运算符重载

6.1. const成员函数

  1. const修饰的成员函数称为const成员函数,放到成员函数参数列表后面
  2. const是用来修饰该成员函数的this指针的,在函数里面不能修改类里的任何成员

const如果修饰指针本身是不存在权限放大缩小的问题的

const在 * 号左边,表示指向的内容不能修改,权限缩小,如果此时传递过来的this指针去修改就会导致权限放大的问题

传递实参的类型需要和形参一一对应的

  1. Print隐含的this指针从 Date* const this --> const Date* const this
class A
{
public:
	A(int a)
	{
		_a = a;
	}
	void Print() const
	{
		cout << _a << endl;
	}
private:
	int _a;
	//int _b;
};
int main()
{
	const A aa2(10);
	aa2.Print();

    //权限可以缩小 但是不能放大
	A aa3(30);
	aa3.Print();
	return 0;
}

6.2.取地址运算符重载

  1. 取地址运算重载,一般分为普通取地址运算符重载和const取地址运算符重载,一般编译器自动生成的就够用了,不需要自己写;除非有特殊情况
class Date
{
public:
	Date* operator&()
	{
		return this;
		// return nullptr;
	}
	const Date* operator&()const   // const Date* const this
	{
		return this;
		// return nullptr;

		//可以使坏,返回一个 (Date*)0x03aff48
	}
private:
	int _year; // 年
	int _month; // ⽉
	int _day; // ⽇
};

加油啊!!! 兄弟们  为了美好的明天而战

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

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

相关文章

Excel导入数据

"update user set long1 "&C2&",long2 "&D2&" where code "&A2&";" excel怎么按逗号把数据分列? excel一列数字逗号隔开分列数据技巧_excel_办公软件_软件教程_脚本之家 excel怎么按逗号把数据分列? ex…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第四集:制作更多的敌人

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作敌人僵尸虫Zombie 1.公式化导入制作僵尸虫Zombie素材2.制作僵尸虫Zombie的Walker.cs状态机3.制作敌人僵尸虫的playmaker状态机二、制作敌人爬虫Climber…

C语言程序设计(进阶)

肆意张扬的我们都不会是烂尾的诗集。 2.整型在内存中的存储 我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同类型而决定的。 2.1原码、反码、补码 数值有不同的表现形式&#xff1a;2进制、8进制、10进制、16进制 其中整数的2进制表示也有三种形式&…

毕业论文写作会用到的AI软件!一定不能错过的18个网站!(务必收藏)

AI毕业论文写作它可以提供论文摘要、大纲、选题确立等多种写作辅助&#xff0c;还能帮助我们完成开题报告、实验报告、辩论灵感等内容。无论是文章纠正、批改&#xff0c;还是改写降重&#xff0c;它都能轻松搞定。甚至连论文致谢、创新创业计划书等都能为我们提供帮助。 以下…

缓存穿透 问题(缓存空对象)

文章目录 1、缓存穿透2、缓存空对象3、AlbumInfoApiController --》getAlbumInfo()4、AlbumInfoServiceImpl --》getAlbumInfo()5、RedisConstant6、请求缓存不存在的数据 1、缓存穿透 2、缓存空对象 3、AlbumInfoApiController --》getAlbumInfo() GetMapping("getAlbumI…

基于RK3588,AI边缘模块,单片6TOPS,可集群堆叠,Mixtile Blade 3

Mixtile Blade 3 是一款经济实惠、节能的 SBC&#xff0c;围绕下一代 8 纳米瑞芯微 RK3588 处理器构建。它非常适合快速开发、AI 应用程序原型设计和边缘计算&#xff0c;允许您集群多个 Mixtile Blade 3 SBC 以扩展您的部署。 硬件布局正反面 开箱即用的 Mixtile Blade 3 是一…

Three.js学习笔记

Three.js是一款基于原生WebGL封装的Web 3D库,向外提供了许多的接口。 它可以运用在在小游戏、产品展示、物联网、数字孪生、智慧城市园区、机械、建筑、全景看房、GIS等各个领域。 npm install three https://threejs.org/docs/index.html#manual/en/introduction/Installatio…

java序列化对象后读取数据错误的问题

今天学到了对象的序列化&#xff0c;就是将对象写入到文件中去&#xff0c;大家要直到我们普通的输入输出文件只是把数据的值写入了文件&#xff0c;而没有把数据的类型与之绑定&#xff0c;比如我向文件中写入100&#xff0c;那么这是字符串”100“还是整数100还是高精度浮点数…

CSS入门笔记

目录 概述 组成 CSS 语法 常见的使用方式 CSS 优先级 CSS 选择器 1. 基本选择器 2. 属性选择器 3. 伪类选择器 4. 组合选择器 示例 优先级 边框样式与盒子模型 单个边框 边框轮廓&#xff08;Outline&#xff09; 盒子模型 模型介绍 边距设置 布局示例 文…

支付宝开放平台-开发者社区——AI 日报「9 月 19 日」

1️⃣ 保守派中间派原生派&#xff0c;谁将主导搜索引擎未来三十年&#xff1f; 机器之心&#xff5c;阅读原文 拥有智能索引库、专属知识库、混合大模型调度系统的 AI 原生搜索&#xff0c;能否成为正统&#xff0c;引领搜索引擎的下一个三十年&#xff1f;AI 正成为「端掉」…

数据处理与统计分析篇-day05-Pandas详解

创建DaraFrame对象 概述 DataFrame是一个表格型的结构化数据结构&#xff0c;它含有一组或多组有序的列&#xff08;Series&#xff09;&#xff0c;每列可以是不同的值类型&#xff08;数值、字符串、布尔值等&#xff09;。 DataFrame是Pandas中的最基本的数据结构对象&…

51单片机-DA(数字转模拟)

作者&#xff1a;Whappy 个人理解&#xff1a;将电压或电流信号进行等分或不等分&#xff08;高电平的电压范围和低电平的范围&#xff0c;如0-5v&#xff0c;0-1.8位低电平&#xff0c;3.8-5v为高电平&#xff09;&#xff0c;同样也是通过采样&#xff0c;量化等操作将不连续…

工业仪器仪表指针数据集

工业仪器仪表指针数据集: 可用于仪表指针识别、分割项目 分别含有1000张原图和标签 图片中有各类工业仪器表盘&#xff0c;指针。 工业仪器仪表指针数据集介绍 数据集名称 工业仪器仪表指针数据集&#xff08;Industrial Instrument Needle Dataset&#xff09; 数据集概述 …

Introduction to LLMs in Python

1、The Large Language Models (LLMs) Landscape 1.1、Introducing large language models 1.1.1、LLMs development lifecycle 1.1.2、Pre-training and fine-tuning We can often use a pre-trained LLM as a foundation and fine-tune it with our specific data, saving …

【笔记】2.1 半导体三极管(BJT,Bipolar Junction Transistor)

一、结构和符号 1. 三极管结构 常用的三极管的结构有硅平面管和锗合金管两种类型。各有PNP型和NPN型两种结构。 左图是NPN型硅平面三极管,右图是PNP型锗合金三极管。 从图中可见平面型三极管是先在一块大的金属板上注入杂质使之变成N型,然后再在中间注入杂质使之变成P型,…

从零开始讲DDR(2)——DDR的核心技术

我们知道DDR区分于之前的SDRAM最大的点就在于它可以做到“在时钟周期的上升沿和下降沿都能传输数据&#xff0c;这样它的传输速率就比SDRAM快了一倍”。其实要做到这点&#xff0c;背后需要的技术突破体现在很多层面&#xff1a; 一、双边沿触发技术&#xff08;Double Data Ra…

DPDK 简易应用开发之路 2:UDP数据包发送及实现

本机环境为 Ubuntu20.04 &#xff0c;dpdk-stable-20.11.10 发送数据包的通用步骤 初始化DPDK环境&#xff1a; 调用 rte_eal_init() 来初始化DPDK的EAL&#xff08;环境抽象层&#xff09;&#xff0c;这是所有DPDK应用程序的第一步&#xff0c;用于初始化硬件、内存和逻辑核…

希尔排序(ShellSort)详解

希尔排序的底层是插入排序&#xff0c; 不了解插入排序的友友可以先看我下面这篇文章&#xff1a; 插入排序详解-CSDN博客 思路 就整体而言&#xff0c;希尔排序不过是多用了几次插入排序&#xff0c; 预排序→插入排序&#xff0c; 预排序的作用是让数组元素更接近于有序…

FreeRtos中Task函数详解

前言&#xff1a;本篇笔记参考韦东山老师&#xff0c;B站视频链接放在最后。 Task任务基本概念 在使用FreeRtos的程序中&#xff0c;可以创建多个Task来完成程序功能&#xff0c;Task是轻量级的独立执行单元&#xff0c;被FreeRtos的调度器管理&#xff0c;每个任务有着自己的…

从零开始手搓Transformer#Datawhale组队学习Task1#

从零开始手搓Transformer 目录 缩放点积注意力DotProductAttention 多头注意力Multi-Head Attention 位置编码Position Encoder 前馈神经网络FFN 残差连接和层归一化&#xff08;Add&Norm&#xff09; 编码器Encoder 解码器Decoder 编码器-解码器Encoder-Decoder …