类和对象(中)(构造函数、析构函数和拷贝构造函数)

1.类的六个默认成员函数

任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

//空类
class Date{};

默认成员函数:用户没有显示实现,编译器会自动生成的成员函数称为默认成员函数

2.构造函数

构造函数 是一个 特殊的成员函数,名字与类名相同 , 创建类类型对象时由编译器自动调用 ,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次
构造函数主要任务不是开空间创建对象,而是初始化对象。
其特征如下:
        ① 函数名与类名相同。
        ② 无返回值。//不需要写void
        ③ 对象实例化时编译器 自动调用 对应的构造函数。
        ④ 构造函数可以重载。
多个构造函数,有多种初始化方式,一般情况,建议每个类,都可以写一个全缺省的构造(好用)
class Date
 {
  public:
      //他们俩构成函数重载,但是无参调用时会存在歧义
      // 1.无参构造函数
      Date()
     {
        _year = 1;
		_month = 1;
		_day = 1;
     }
  
      // 2.带参构造函数
      // 一般情况,建议每个类,都可以写一个全缺省的构造(好用)
      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();  //d1后面不能带括号否则定义对象无法跟函数声明区分开
//Date func();//这就是d1为什么不能带括号
	Date d1;
	d1.Print();
//类型 对象(2024,4,2)
	Date d2(2024, 4, 2);//这里调用构造函数是对象名加参数列表  
//这里可以和函数声明进行区分,如下一行的函数声明所示
 //Date func(int x, int y, int z);
	d2.Print();

	Date d3(2024);
	d3.Print();

	Date d4(2024, 4);
	d4.Print();

	return 0;
}

        ⑤如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦            用户显式定义编译器将不再生成。

#include<iostream>
using namespace std;
class Date
{
public:
	/*
	// 如果用户显式定义了构造函数,编译器将不再生成
	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;
};

int main()
{
	// 将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
	// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
	// 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
	Date d1;//对象实例化的时候自动调用对应的构造函数
	return 0;
}

如果用户显示定义了构造函数,编译器将不会生成无参的默认构造函数,这时候如果定义一个无参的类如Date d1,会编译失败。

⑥C++ 把类型分成内置类型 ( 基本类型 ) 和自定义类型。内置类型就是语言提供的数据类型,如:int/char.../任意类型指针 ,自定义类型就是我们使用 class/struct/union 等自己定义的类型。
如果我们没写构造函数,编译器自动生成构造函数,对于编译器自动生成的构造函数
对于内置类型的成员变量,编译器没有规定要不要做处理!(有些编译器会处理成0,但是C++标准并没有规定)
对于自定义类型的成员变量,才会调用他的默认成员函数即无参构造,如果没有无参构造会报错。(不传参就可以调用的那个构造,全缺省构造)
#include<iostream>
using namespace std;
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;
}

这里我们发现编译器自动生成的构造函数对于自定义类型调用了它的默认构造函数。

注意: C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即: 内置类型成员变量在
类中声明时可以给默认值
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 = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
     Date d;
     return 0;
}

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

总结:不传参数就可以调用的函数就是默认构造

Ⅰ一般情况构造函数都需要我们自己显式去实现

Ⅱ只有少数情况下可以让编译器自动生成构造函数类似MyQueue,成员全是自定义类型

3.析构函数

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

析构函数是特殊的成员函数,其特征如下:

①析构函数名是在类名前加上字符 ~
②无参数无返回值类型。
③一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载
④对象生命周期结束时,C++ 编译系统系统自动调用析构函数

⑤对于编译器自动生成的默认析构函数,对于自定义类型成员会调用它的析构函数。

跟构造函数类似:

a、内置类型不做处理       

b、自定义类型去调用他的析构

class Time
{
public:
 ~Time()
 {
 cout << "~Time()" << endl;
 }
private:
 int _hour;
 int _minute;
 int _second;
};
class Date
{
private:
 // 基本类型(内置类型)
 int _year = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d;
 return 0;
}//对象在这里被销毁后,这里会自动调用析构函数

程序运行结束后输出: ~Time() ,在main 方法中根本没有直接创建 Time 类的对象,为什么最后会调用 Time 类的析构函数?
因为: main 方法中创建了 Date 对象 d ,而 d 中包含 4 个成员变量,其中 _year, _month,
_day 三个是 内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t Time 类对 象,所以在d销毁时,要将其内部包含的 Time 类的 _t 对象销毁,所以要调用 Time 类的析构函数。但是:main函数 中不能直接调用Time 类的析构函数,实际要释放的是 Date 类对象,所以编译器会调用 Date 类的析构函数,而Date 没有显式提供,则编译器会给 Date 类生成一个默认的析构函数,目的是在其内部调用Time 类的析构函数,即当Date 对象销毁时,要保证其内部每个自定义对象都可以正确销毁
 main函数中并没有直接调用 Time 类析构函数,而是显式调用编译器为 Date 类生成的默认析 构函数
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
Date 类;有资源申请时,一定要写,否则会造成资源泄漏,比如 Stack
自动生成的构造函数和析构意义何在?两个栈实现一个队列
Stack.h如下:
#pragma once
#include<stdlib.h>
#include<iostream>
using namespace std;

class Stack
{
public:
	Stack(int n = 4);
	~Stack();

	//void Init();
	//void Destroy();

	void Push(int x);
	bool Empty();
	void Pop();
	int Top();
private:
	// 成员变量
	int* _a;
	int _top;
	int _capacity;
};

class Queue
{
public:
	void Init();
	void Push(int x);

};

Stack.cpp如下:

#include"Stack.h"

Stack::Stack(int n)//缺省参数声明和定义不能同时给,规定了只在声明时候给,定义的时候不给
{
	_a = (int*)malloc(sizeof(int)*n);
	_top = 0;
	_capacity = n;
}

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

//void Stack::Init()//指明类的作用域就指明了类的出处
//{
//	_a = nullptr;
//	_top = 0;
//	_capacity = 0;
//}//任何一个变量都得先定义再使用,不符合语法报语法错误

//void Destroy()
//{
//	//...
//}

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

bool Stack::Empty()
{
	return _top == 0;
}

void Stack::Pop()
{
	--_top;
}

int Stack::Top()
{
	return _a[_top - 1];
}

//void Queue::Push(int x)
//{
//
//}

test.cpp
#icnlude<Stack.h>
class MyQueue
{
private:
	Stack _pushst;
	Stack _popst;
};

int main()
{
	MyQueue q;
	return 0;
}

这里创建MyQuque类q时,会自动调用调用Stack类的默认构造函数,q销毁时,也会自动调用Stack类的默认析构函数。

下面是一个括号匹配问题,利用c和c++实现的比较,可以发现,利用c++的构造和析构特性后,会方便很多

实践中总结:
1、有资源需要显示清理,就需要写析构。如:Stack List
2、有两种场景不需要显示写析构,默认生成就可以了
a、没有资源需要清理,如:Date
b、内置类型成员没有资源需要清理。剩下的都是自定义类型成员
4.拷贝构造函数
拷贝构造函数: 只有单个形参 ,该形参是对本 类类型对象的引用 ( 一般常用 const 修饰 ) ,在用 已存 在的类类型对象创建新对象时由编译器自动调用
特征:
①拷贝构造函数 是构造函数的一个重载形式
拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错 因为会引发无穷递归调用。
class Date
{
public:
	Date(int year = 1970, int month =1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//Date(Date d)错误写法
    //Date d2(d1);//d1传给了d,d2就是this
	Date(const Date& d)//加了const之后d的内容就无法修改
	{
		cout << "const Date& d" << endl;
        //this->_year = d._year;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
//	Date(Date* d)//规定这里不是拷贝构造,就是一个普通构造,编译器会自动生成一个默认的拷贝构造
//	{
//		_year = d->_year;
//		_month = d->_month;
//		_day = d->_day;
//	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	// 基本类型(内置类型)
	int _year ;
	int _month ;
	int _day ;

};
void func(Date d)
{
	d.Print();
}
int main()
{
	Date d1(2024,4,18);
	Date d2(d1);
	func(d2);
	return 0;
}

//Date(Date d)是错误写法,

原因:对于自定义类型传值传参要调用拷贝构造完成,这是一种规定,自定义类型的拷贝都要调用拷贝构造才能完成。这里用d1去构建d2,规定需要调用拷贝构造,调用拷贝构造得先传参,先传参形成了一个新的拷贝构造,新的拷贝构造假设去调用,去调用这个拷贝构造,又要先传参,从逻辑上来说就是一个无穷递归

对于
func(d2);
void func(Date d)
{
	d.Print();
}
Date(const Date& d)
{
	cout << "const Date& d" << endl;
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

与构造和析构函数一样,内置类型就直接传,自定义类型传值传参就要调用拷贝构造完成,这里调用func之前先传参,传参就会形成一个拷贝构造,d是d2的别名,函数结束回来,传参完成,参数的传递就是完成拷贝构造调用,最后调用调用func函数,结束

也可以从建立函数栈帧的方式去看函数func(d2)的调用

当然也可以采用指针或者引用的方式,这种方式不会调用拷贝构造。

	func(d2);
void func(Date& d)
{
    d.Print();
}
//这种方式也不会调用拷贝构造,d是d2的别名

    func(&d2);
void func(Date* d)
{
	d->Print();
}
//这种情况下没有拷贝构造,因为传的是内置类型,把d2地址进行传递,用指针进行接收

③若未显示定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d1;

	// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
	// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
	Date d2(d1);
	return 0;
}

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
④深拷贝
编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,这是浅拷贝或者值拷贝,对于日期类不需要自己显示实现,值拷贝就是将一块空间里面的值按照字节一个一个的拷贝过来,有点像memcpy拷贝—样,memcpy就是按字节拷贝
但是如果内部有指针或者一些值指向资源,需要显示写析构释放,通常就需要显示写构造完成深拷贝,如:Stack Queue List等
//这里如何去掉Stack的拷贝构造会发现下面的程序会崩溃掉,这里就需要深拷贝去解决。
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	Stack(const Stack& st)
	{
		_array = (DataType*)malloc(st._capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		memcpy(_array, st._array, st._size*sizeof(DataType));
		_size = st._size;
		_capacity = st._capacity;
	}
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	bool Empty()
	{
		return _size == 0;
	}

	DataType Top()
	{
		return _array[_size - 1];
	}

	void Pop()
	{
		--_size;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
class MyQueue
{
private:
	Stack _st1;
	Stack _st2;
	int _size = 0;
};
int main()
{
	Stack st1(10);
	st1.Push(1);
	st1.Push(1);
	st1.Push(1);

	Stack st2 = st1;
	st2.Push(2);
	st2.Push(2);

	while (!st2.Empty())
	{
		cout << st2.Top() << " ";
		st2.Pop();
	}
	cout << endl;
    //输出1 1 1
	while (!st1.Empty())
	{
		cout << st1.Top() << " ";
		st1.Pop();
	}
	cout << endl;
    //输出2 2 1 1 1 
	MyQueue q1;
	MyQueue q2(q1);
 //这里自定义类型会去调用栈的拷贝构造(栈的拷贝构造是深拷贝),内置类型完成值拷贝
	return 0;
}

注意:这里_array按照字节拷贝,相当于然s2的_array指向的空间和s1的_array指向的空间一样,但是这样会导致两个问题:(只要指针指向资源的都会有问题)
1.s1 push后s2上也能看见,因为s1改变的是和s2同一块空间的值,同时s1的size会改变,但是s2的size不会改变
⒉析构的时候,s1会free一次,s2也会free—次,相当于对一块空间free两次,也即析构两次
这里可以用深拷贝来解决,深拷贝就是你的形状和你的空间是什么样子就去开和你—样的空间,放一样的值,复制和你一样的出来。
在类里面加上如下的拷贝构造即可
	Stack(const Stack& st)
	{
		_array = (DataType*)malloc(st._capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		memcpy(_array, st._array, st._size*sizeof(DataType));
		_size = st._size;
		_capacity = st._capacity;
	}

实践中总结:
1、如果没有管理资源,一般情况不需要写拷贝构造,默认生成的拷贝构造就可以。如:Date
2、如果都是自定义类型成员,内置类型成员没有指向资源,也类似默认生成的拷贝构造就可以。如:MyQueue3、一般情况下,不需要显示写析构函数,就不需要写拷贝构造
4、如果内部有指针或者一些值指向资源,需要显示写析构释放,通常就需要显示写构造完成深拷贝。如:Stack Queue List等

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

接口自动化入门: Http请求的域名与IP地址概念!

在进行接口自动化测试时&#xff0c;经常需要与服务器进行通信&#xff0c;这就涉及到了使用Http协议发送请求。在发送请求时&#xff0c;我们需要指定目标服务器的域名或者IP地址。下面将从0到1详细介绍域名与IP地址的概念及其在接口自动化测试中的应用。 本文从5个方面来书写…

3D可视化技术:研发基地的科技新篇章

在科技日新月异的今天&#xff0c;我们生活在一个充满无限可能性的时代。而在这个时代中&#xff0c;3D可视化技术正以其独特的魅力&#xff0c;引领着科技领域的新一轮变革。 3D可视化技术通过三维图像的方式&#xff0c;将现实世界或虚拟世界中的物体、场景等以立体、逼真的形…

改进下记录学习的小网站

Strong改进 结束&#xff1a;2024-4-14 打算投入&#xff1a;10h 实际消耗&#xff1a;12h 3m 学习总是不在状态。 我的时间花得很零散&#xff0c;也有点茫然。所以想尝试一下集中式地、一块一块地花&#xff0c;比如投入30个小时&#xff0c;去干一件事&#xff0c;这样就可…

npm怎么迁移到pnpm

下载的vue3模板用到了pnpm&#xff0c;就安装了一下 但是安装之后使用pnpm install 就发现包全被移动到ignored文件夹下面了,还报错 PS G:\Projects\gitProeject\TS_front> pnpm installWARN  Moving commitlint/config-conventional that was installed by a different …

继电器会不会被淘汰?

继电器作为一种电控制器件&#xff0c;其基本功能是在输入量达到一定条件时&#xff0c;使电气输出电路中的被控量发生预定的阶跃变化。 尽管现代电子技术发展迅速&#xff0c;新型产品不断涌现&#xff0c;但继电器因其独特的优势在许多应用领域仍然不可替代。 技术优势&#…

git 删除本地分支 删除远程仓库中的分支

语法&#xff1a; 删除本地分支 git branch -D <分支名>删除远程分支 git push <remote名称> <分支名> --delete 示例&#xff1a; 删除本地分支 git branch -D feature/test_listview删除远程分支 git push origin feature/test_listview --delete 两个…

FebHost:谁可以注册.CA加拿大域名?

在加拿大&#xff0c;互联网域名的注册管理遵循一套独特的规则。特别是对于代表加拿大身份的顶级域名“.ca”&#xff0c;其申请和注册过程涉及一些严格的条件。这些条件确保了只有符合特定标准的个人或实体才能获得这一具有国家象征意义的网络地址。 首先&#xff0c;想要注册…

实战1-批量爬取百度图片(上)

任务需求&#xff1a;输入关键字下载100个图片保存到本地&#xff0c;每个关键字单独存放一个文件夹&#xff08;GUI版&#xff09; 任务描述&#xff1a;当输入关键字时会爬取100个与关键词有关的图片到本地每个关键词单独保存到一个文件夹中&#xff0c;比如说我输入黑客下载…

Arduino源代码(ino)在Proteus中调试总结

一、前言 基于BluePill Plus开发板&#xff08;该板是毕设网红板&#xff09; BluePill Plus / WeAct Studio 微行工作室 出品 BluePill-Plus/README-zh.md at master WeActStudio/BluePill-Plus GitHub 首页-WeAct Studio-淘宝网 (taobao.com) 在Proteus中对应的例子是&…

windows下安装kibana

下载&#xff1a;https://www.elastic.co/cn/downloads/kibana 安装&#xff1a;https://www.elastic.co/guide/cn/kibana/current/install.html 安装好后&#xff0c;cd到kibana的bin目录&#xff0c;启动kibana.bat 然后访问localhost:5601

链表(C语言)

前言&#xff1a;前面几篇文章我们详细介绍了顺序表&#xff0c;以及基于顺序表来实现的通讯录。今天我们连介绍一下链表的下一个结构链表。那么链表和顺序表究竟有什么区别呢&#xff1f;他们两个的优缺点分别是什么。今天这篇文章就带大家了解一下链表。 目录 一.链表的概念…

前端三大件速成 01 HTML

文章目录 一、前端基础知识二、标签1、什么是标签2、标签的属性3、常用标签&#xff08;1&#xff09;声明&#xff08;2&#xff09;注释&#xff08;3&#xff09;html 根标签&#xff08;3&#xff09;head标签&#xff08;4&#xff09;body标签 三、特殊字符四、其他标签1…

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起, 是谁最早在此留下足印

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起&#xff0c; 是谁最早在此留下足印 《一世珍藏的诗歌200首》作者金宏宇&#xff0c;很多美好的诗&#xff0c;有徐志摩&#xff0c;戴望舒&#xff0c;林徽因&#xff0c;舒婷等的诗精选&#xff0c;很值得一读…

变配电场所智能综合监控系统无人化与自动化升级改造

一 项目背景 国家电力建设飞速发展,为了提高管理水平,智能化建设迫在眉睫。变配电场所作为电网中的核心单元,数量巨大,是智能化建设的中坚部分。但由于变配电场所分布的地理位置过于分散&#xff0c;且配电网的自动化水平有待提高,单纯依靠人力来对变配电场所进行巡视,不仅增加…

Leo赠书活动-24期 【三大层次学习企业架构框架TOGAF】文末送书

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

【网站项目】自习室预约系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

基于Springboot+Vue的Java项目-企业客户管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

【Python】OPC UA模拟服务器实现

目录 服务器模拟1. 环境准备2. 服务器设置3. 服务器初始化4. 节点操作5. 读取CSV文件6. 运行服务器 查看服务器客户端总结 在工业自动化和物联网&#xff08;IoT&#xff09;领域&#xff0c;OPC UA&#xff08;开放平台通信统一架构&#xff09;已经成为一种广泛采用的数据交换…

如何使用Docker部署WPS Office服务并实现无公网IP远程处理文档表格

文章目录 1. 拉取WPS Office镜像2. 运行WPS Office镜像容器3. 本地访问WPS Office4. 群晖安装Cpolar5. 配置WPS Office远程地址6. 远程访问WPS Office小结 7. 固定公网地址 wps-office是一个在Linux服务器上部署WPS Office的镜像。它基于WPS Office的Linux版本&#xff0c;通过…

【深入解析spring cloud gateway】13 Reactive Feign的使用

问题引入 在gateway中如果使用feignClient的话&#xff0c;会报如下错误 java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3at reactor.core.publisher.BlockingSingleSubscriber.bloc…