【C++精华铺】5.C++类和对象(中)类的六个默认成员函数

news2024/11/22 16:52:38

目录

1. 六个默认成员函数

2. 构造函数

2.1 概念

2.2 默认构造

2.2.1 系统生成的默认构造

2.2.2 自定义默认构造函数

 2.3 构造函数的重载

3. 析构函数

3.1 概念

 3.2 系统生成的析构函数

 3.3 自定义析构函数

4. 拷贝构造

4.1 概念

 4.2 默认生成的拷贝构造(浅拷贝)

 4.3 自定义拷贝构造(深拷贝)

 5. 赋值运算符重载

5.1 运算符重载

5.2 赋值运算符重载

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

7. 附:完整日期类(文章中的代码不取自这里的代码,都是为了讲解知识点临时敲的,这里的代码是完整的日期类(取自比特科技),可以借鉴学习)


1. 六个默认成员函数

        当我们想到空类的时候肯定想到的是里面什么都没有的类称之为空类,但是事实却并非如此。当一个类里面什么都不写的时候编译器会默认生成六个默认成员函数来完成一个类的基本功能。

  1. 构造函数:对象初始化工作
  2. 析构函数:空间清理工作
  3. 拷贝构造和赋值运算符重载:对象的拷贝复制工作
  4. 取地址和const取地址重载:一般很少自己实现,除非需要给用户返回指定的特殊的地址。

2. 构造函数

2.1 概念

        构造函数是一个特殊的成员函数,他会在对象创建时由编译器自动调用完成对象的初始化工作,构造函数没有返回值,并且函数名与类名相同。

        构造函数特征如下:

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

};

2.2 默认构造

        什么是默认构造?无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。一般来说我们所实现的自定义类型都必须具有一个默认构造函数,原因会在下面进行叙述。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。

2.2.1 系统生成的默认构造

         当我们没有去手动定义构造函数的时候系统会默认生成一个无参的构造函数,这个无参的构造函数会对类里面的成员变量进行初始化,其中包括对自定义类型调用它的默认构造函数(这也是是为什么每个类必须要有一个默认构造的原因),对内置类型不做处理,对,你没听错,系统生成的默认构造不会对内置类型进行处理,这是一个非常bug的点。比如下面的案例:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;

		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
private:
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	cout << d._year << d._month << d._day << endl;
	return 0;
}

输出:

Time()
-858993460-858993460-858993460 

        由此可见,默认生成的构造函数不会对内置类型进行处理,对自定义类型会调用它的默认构造。 由于这一点,让系统生成的默认构造毫无用武之地,所以在C++11中给这个漏洞打了一个补丁:C++11 中针对内置类型成员不初始化的缺陷,打了一个补丁,即:内置类型成员变量在 类中声明时可以给默认值。(以给缺省值的方式给内置类型一个默认值)如下:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;

		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	// 基本类型(内置类型)
	int _year = 2023;
	int _month = 8;
	int _day = 9;
private:
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	cout << d._year << ' ' << d._month << ' '  << d._day << endl;
	return 0;
}

输出:

Time()
2023 8 9

2.2.2 自定义默认构造函数

        自定义的默认构造函数只有俩种,无参的和全缺省的构造函数,并且这俩种构造只能存在一个。但是有人可能就要问了,这俩种构造函数不是构成重载了吗,为什么不能同时存在呢?首先就是语法上规定了默认构造只能存在一个,另外就是这俩个函数虽然符合函数重载的语法,但是在调用的时候会出现歧义,要知道,任何语法上的规定都是有原因的,如下:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;

		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}

	Date(int a = 1, int b = 2)
	{
		cout << "Date(int a = 1, int b = 2)" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
	Time _t;
};
int main()
{
	Date d;  //这里出现了歧义,
			   //不知道调用的是全缺省的构造还是无参的构造
			   //因为这俩种函数都可以用 Date() 的形式来调用就出现了歧义
	return 0;
}

 错误:
C2668    “Date::Date”: 对重载函数的调用不明确。

E0339    类 "Date" 包含多个默认构造函数。

        但是我们如果自己传实参给构造函数我们发现就可以调用了,所以语法上规定只能存在一个默认构造本质就是因为上述情况会出现歧义,而在我们平时实例化的时候传个实参就可以避免这种歧义,这个时候即使定义了俩个默认构造也依然不会报错,但是我们不建议这样去写,因为这样在项目中的风险是巨大的,比如下面:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;

		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}

	Date(int a = 1, int b = 2)
	{
		cout << "Date(int a = 1, int b = 2)" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
	Time _t;
};
int main()
{
	Date d(1,2);  //不建议,不能因为避免报错就去特殊处理初始化方式。
                  //不能同时定义俩个默认构造,即使编译器没有报错
	return 0;
}

 2.3 构造函数的重载

        构造函数是支持重载的,可以让我们应对不同的初始化场景(这里在强调一遍:无参构造和全缺省构造不能同时定义,会出现歧义)。

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

	Date(int a, int b = 2)
	{
		cout << "Date(int a = 1, int b = 2)" << endl;
	}
	Date(int year, int month, int day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
	Time _t;
};

3. 析构函数

3.1 概念

        析构函数的性质与构造函数十分的类似,但是与构造函数功能相反,析构函数用于清理对象销毁后的空间,但它不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

        析构函数和构造函数一样无参数无返回值,命名有所不同,并且析构函数是在对象销毁时由编译器自动调用的,这一点和构造函数类似。特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3.  一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
class Date
{
public:
	~Date()
	{
		//析构函数
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
};

 3.2 系统生成的析构函数

        如果一个类里面没有显式的定义析构函数,系统会自动生成一个默认的析构函数,这个析构函数会在对象生命周期结束自动调用,默认生成的析构函数对内置类型不做处理,对自定义类型会去调用它的析构函数。如下:

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

输出:

~Date()
~Time()

 3.3 自定义析构函数

        上面说过,析构函数对内置类型是不做处理的,但是我们在日常的使用过程中常常会涉及到内存申请(比如malloc)然后用指针类型去存储地址,诸如此类的空间我们就不能依赖系统生成的默认析构以免导致内存泄漏,需要自己定义析构函数去手动释放。 

class Date
{
public:
	Date()
	{
		p = (int*)malloc(10 * sizeof(int));
	}
	~Date()
	{
		free(p);
		cout << "~Date()" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
	int* p = nullptr;
};

4. 拷贝构造

4.1 概念

        拷贝构造是构造函数的一种的重载形式,拷贝构造只有一个形参,一般是这个类型的const引用(只能传引用,不能传值,传值会造成无穷递归),在用已存在的类型对象创建新对象时由编译器自动调用。

        传引用(正确):

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)  //拷贝构造函数
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 9;
	int* p = nullptr;
};

        传值(无穷递归):

 4.2 默认生成的拷贝构造(浅拷贝)

        在我们没有显式的定义拷贝构造的时候会自动生成一个默认的拷贝构造,默认的拷贝构造会对我们的成员变量按字节拷贝,也成为值拷贝或者浅拷贝。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		p = (int*)malloc(10 * sizeof(int));
	}
    ~Date()
	{
		free(p);
		cout << "~Date()" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
	int* p = nullptr;
};

int main()
{
	Date d1(12, 12, 12);
	Date d2(d1);   //引发异常
}

 4.3 自定义拷贝构造(深拷贝)

        当涉及到内存管理的时候编译器默认生成的拷贝构造就不够用了,这个时候就需要我们对其进行深拷贝。什么是深拷贝呢:就是创建一个新的对象,将原对象的各项属性的“值”(数组的所有元素)拷贝过来。如果是上面的情况我们就需要重新申请一块空间去保存d.p指向空间里面的数据。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		p = (int*)malloc(10 * sizeof(int));
		if (p == NULL)
		{
			perror("malloc fail");
			exit(-1);
		}
		for (int i = 0; i < 10; i++)
		{
			p[i] = 10;
		}
	}
	~Date()
	{
		free(p);
		cout << "~Date()" << endl;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		p = (int*)malloc(10 * sizeof(int));
		if (p == NULL)
		{
			perror("malloc fail");
			exit(-1);
		}
		for (int i = 0; i < 10; i++)
		{
			p[i] = d.p[i];
		}
	}
private:
	int _year;
	int _month;
	int _day;
	int* p = nullptr;
};
int main()
{
	Date d1(12, 12, 12);
	Date d2(d1);
}

 输出:

~Date()
~Date()

 5. 赋值运算符重载

5.1 运算符重载

        C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 函数名字为:关键字operator后面接需要重载的运算符符号。 函数原型:返回值类型 operator操作符(参数列表)。

注意:

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

        运算符重载我们有俩种定义方式,可以定义成全局的,也可以直接定义成函数成员,但是定义成函数成员我们所看到的形式参数就会少一个(由于this指针隐含参数) ,下面以==为例:

//全局
bool operator==(const Date& d1, const Date& d2)
{
    return d1._year == d2._year
   && d1._month == d2._month
        && d1._day == d2._day;
}

//函数成员

class Date
{
public:
	bool operator==(const Date& d)
	{
		return (_year == d._year)
			&& (_month == d._month)
			&& (_day == d._day);
	}
private:
	int _year;
	int _month;
	int _day;
};

        在运算符重载里面需要注意的就是“++”和“--”了,因为这俩个运算符有前置和后置的区别,所以在前置和后置上也作了区分如下(以++为例):

	Date& operator++();//前置
	Date operator++(int);//后置 多了一个int形参,仅作标记,没有实际含义

5.2 赋值运算符重载

        赋值运算符重载函数如果没有被显示定义,编译器会自动生成一个默认的赋值运算符重载,拷贝的方式是浅拷贝。与其他运算符重载函数不同的地方是赋值运算符重载必须定义成成员函数,不能定义成全局,如果定义成全局,类体里没有赋值运算符重载就会自动生成,这会与我们在全局定义的发生冲突。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	  
	Date& operator=(const Date& d) //引用传参避免拷贝提高效率								
	{                              //引用返回因为赋值运算符支持连续赋值 d1 = d2 = d3;
		if (this != &d)
		{
			_year= d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

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

        如果类型涉及到内存管理就需要深拷贝,这里就与拷贝构造完全相同,不理解的话可以往前看。

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

        这俩种运算符重载编译器也会默认生成,绝大部分情况下都不需要我们自己去定义它,除非想让别人获取到指定的内容!

class Date
{
public:
	Date* operator&()
	{
		return this;
	}
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

7. 附:完整日期类(文章中的代码不取自这里的代码,都是为了讲解知识点临时敲的,这里的代码是完整的日期类(取自比特科技),可以借鉴学习)

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	Date(int year = 2023, int month = 8, int day = 10);
	void Print() const;
	int GetMonthDay(int year, int month) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;
	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	Date& operator+=(int day);
	Date operator+(int day) const;
	Date& operator-=(int day);
	Date operator-(int day) const;
	int operator-(const Date& d) const;
	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
private:
	int _year;
	int _month;
	int _day;
};
inline ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}
inline istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
#include"Date.h"
int Date::GetMonthDay(int year, int month) const
{
	assert(month > 0 && month < 13);

	int monthArray[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 monthArray[month];
	}
}
Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13
		&& (day > 0 && day <= GetMonthDay(year, month)))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法,初始化失败" << endl;
	}
}
void Date::Print() const
{
	cout << _year << " " << _month << " " << _day << endl;
}
bool Date::operator==(const Date& d) const
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
bool Date::operator<(const Date& d) const
{
	return _year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day);
}
bool Date::operator<=(const Date& d) const
{
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
	return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
	return !(*this < d);
}
bool Date::operator!=(const Date& d) const
{
	return !(*this == d);
}
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		*this -= -day;
		return *this;
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day) const
{
	Date tmp(*this);

	tmp += day;

	return tmp;
}
Date& Date::operator-=(int day) 
{
	if (day < 0)
	{
		*this += -day;
		return *this;
	}
	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}
Date Date::operator-(int day) const
{
	Date tmp(*this);
	tmp -= day;
	return tmp;
}
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
Date Date::operator++(int)
{
	Date tmp(*this);

	*this += 1;

	return tmp;
}
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;

	return tmp;
}
int Date::operator-(const Date& d) const
{
	Date max = *this;
	Date min = d;
	int flag = 1;

	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}
	return n*flag;
}

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

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

相关文章

解锁滴滴ES的性能潜力:JDK 17和ZGC的升级之路

前文介绍了滴滴自研的ES强一致性多活是如何实现的&#xff0c;其中也提到为了提升查询性能和解决查询毛刺问题&#xff0c;滴滴ES原地升级JDK17和ZGC&#xff0c;在这个过程中我们遇到了哪些问题&#xff0c;怎样解决的&#xff0c;以及最终上线效果如何&#xff0c;这篇文章就…

云计算技术——多GPU渲染的云渲染服务

多GPU渲染的云渲染服务&#xff0c;是一种利用云计算技术&#xff0c;将多个图形处理器&#xff08;GPU&#xff09;集成在一起&#xff0c;为用户提供高效、便捷、低成本的渲染解决方案的服务。本文将从多GPU渲染的概念、优势、应用场景&#xff0c;云渲染服务的特点、优势&am…

【C++】初阶 --- 内联函数(inline)

文章目录 &#x1f95e;内联函数&#x1f35f;1、C语言实现"宏函数"&#x1f35f;2、内联函数的概念&#x1f35f;3、内联函数的特性&#x1f35f;4、总结 &#x1f95e;内联函数 &#x1f35f;1、C语言实现"宏函数" &#x1f970;用C语言先来实现普通的…

并发——volatile 关键字

文章目录 1. CPU 缓存模型2. 讲一下 JMM(Java 内存模型)3. 并发编程的三个重要特性4. 说说 synchronized 关键字和 volatile 关键字的区别 我们先要从 CPU 缓存模型 说起&#xff01; 1. CPU 缓存模型 为什么要弄一个 CPU 高速缓存呢&#xff1f; 类比我们开发网站后台系统使…

分布式 - 服务器Nginx:一小时入门系列之静态网页配置

文章目录 1. 静态文件配置2. nginx listen 命令解析3. nginx server_name 命令解析4. nginx server 端口重复5. nginx location 命令 1. 静态文件配置 在 /home 文件下配置一个静态的AdminLTE后台管理系统&#xff1a; [rootnginx-dev conf.d]# cd /home [rootnginx-dev home…

flutter项目给安卓打正式包 和升级版本号

1.首先把flutter项目里的android目录整个拖进android studo里,运行一下看看是否能运行 2.Build->Generate Signed Bundle/APK… 点击APK->Next 如果没有key酒店及Create new…就生成新的key 点击Key store path:右边的图标选择保存路径 选择保存的路径,并且在Save…

【雕爷学编程】Arduino动手做(01)---干簧管传感器模块4

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

logo一键生成的操作,没有设计经验也无妨

您可能会担心&#xff0c;没有设计经验如何设计一个独特而专业的标志&#xff08;Logo&#xff09;。但是&#xff0c;随着技术的进步&#xff0c;现在有许多一键生成操作的设计工具可供使用&#xff0c;使人们更容易创建自己的Logo。 什么是一键生成操作&#xff1f; 一键生成…

若依vue -【 100 ~ 更 】

100 主子表代码生成详解 1 新建数据库表结构&#xff08;主子表&#xff09; -- ---------------------------- -- 客户表 -- ---------------------------- drop table if exists sys_customer; create table sys_customer (customer_id bigint(20) not null…

《大型网站技术架构》第二篇 架构-高可用

高可用在公司中的重要性 对公司而言&#xff0c;可用性关系网站的生死存亡。对个人而言&#xff0c;可用性关系到自己的绩效升迁。 工程师对架构做了许多优化、对代码做了很多重构&#xff0c;对性能、扩展性、伸缩性做了很多改善&#xff0c;但别人未必能直观地感受到&#…

字节编码学习

字节编码学习 文章目录 字节编码学习01_字节与ASCII码表02_每个国家都有独特的码表03_国际化UTF-804_编码本和解码本不一致&#xff0c;乱码 01_字节与ASCII码表 public class Demo01 {public static void main(String[] args) {// 计算机的底层全部都是字节 ---- ----// 一个…

Android Studio实现简单ListView

效果图 MainActivity package com.example.listviewtest;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.widget.ListView;import com.example.listviewtest.adapter.PartAdapter; import com.example.listviewtest.bean.PartB…

多目标优化之MOEA-D算法,MATLAB代码,免费获取

今天给大家分享一期MOEA/D算法。1 引言 基于分解的多目标进化算法(MOEA/D)的核心思想将多目标优化问题被转化为一系列单目标优化子问题或者是多个多目标的子问题&#xff0c;然后利用子问题之间的邻域关系&#xff0c;采用协作的方式对这些子问题同时进行优化。从而向整个Paret…

大麦订单截图 一键生成订单截图

新版付款图样式展示 这个样式图就是在大麦刚付款完的一个订单截图&#xff0c;它的状态是等待卖家发货 下滑下载源码 下载源码&#xff1a;https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3

LoRaWAN网关与网络服务器(NS)的通信LNS协议介绍

将 LoRaWAN网关连接到 LoRaWAN 网络服务器 (LNS) 是一个两步过程,该过程使用 WebSocket 作为传输协议来交换包含 JSON 编码对象的文本记录。 首先,网关向LNS查询LNS实际连接端点的URI。其次,它与该端点建立数据连接以接收设置信息。从那时起,LoRa 上行链路和下行链路帧通过…

golang专栏

GOLANG专栏订阅会员 Golang基础教程 Golang基础教程 Golang练手算法 Golang练手算法 Golang设计模式 Golang设计模式 Golang数据结构和算法 Golang数据结构和算法 Golang并发编程 Golang并发编程 ORM框架Gorm Golang ORM框架gorm Golang源码分析 Golang源码分析 MySQL教…

校园防欺凌防霸凌该怎么做

校园防欺凌防霸凌该怎么做 校园防欺凌和霸凌是一个长期而全面的过程&#xff0c;需要学生、教师和家长共同参与和努力。以下是一些可以采取的措施&#xff1a; 1. 教育和意识提高&#xff1a;通过教育和培训&#xff0c;提高学生、教师和家长对欺凌和霸凌的认识和了解。学校可…

机器人制作开源方案 | 超市服务机器人

作者&#xff1a;林江斌 岳沛霖 李锦扬 单位&#xff1a;北京理工大学 机械与车辆学院 指导老师&#xff1a;李忠新 朱杰 一、作品简介 1.设计背景 当前社会疫情防控已经成为常态化&#xff0c;超市是人员流动相对密集的公共场所&#xff0c;超市防疫也是社会防疫工作中重要…

linuxARM裸机学习笔记(4)----GPIO中断以及定时器中断实验

1.中断向量表 这个表里面存放的都是中断向量&#xff0c;中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量。中断向量表是一系列中断服务程序入口地址组成的表&#xff0c;当某个中断触发的时候会自动跳转到中断向量表对应的中断服务程序的入口。 2.NVIC(内嵌向…

vue3+ts+element-plus大屏看板---横向轮播(anime.js)

vue3ts大屏看板---横向轮播&#xff08;anime.js&#xff09; 1. 安装和引入anime.js1. 安装2. 引入* 引入报错&#xff1a;引入时候报错 2. 基于vue3tsanime.js实现一个大屏组件轮播效果&#xff0c;如下1. 写一个需要轮播的模块样式✏️ 代码&#xff08;有写注释&#xff09…