C++继承 - 下

news2024/12/23 13:16:40

目录

1. 继承和友元

2. 继承与静态成员

3. 菱形继承以及菱形虚拟继承

3.1. 单继承

3.2. 多继承

3.3. 菱形继承

3.4. 菱形虚拟继承

3.5. 菱形继承的底层细节

3.6. 菱形虚拟继承的底层细节

3.7. 虚拟继承

4. 继承的总结

5. 相关继承练习题

5.1. 如何定义一个无法被继承的类?

5.2. 关于切片的问题

5.3. 选择正确选项


1. 继承和友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。

测试 demo 如下: 

namespace Xq
{
	class worker;
    // 基类
	class person
	{
	public:
		friend void display(const person& pobj, const worker& wobj);
	protected:
		std::string _name = "haha";
	};
    // 派生类
	class worker : public person
	{
	protected:
		std::string _job_number = "111";
	};

	void display(const person& pobj, const worker& wobj)
	{
		cout << pobj._name << endl;
		cout << wobj._job_number << endl;
	}
}
void Test1(void)
{
	Xq::person pobj;
	Xq::worker wobj;
	Xq::display(pobj, wobj);
}

现象如下: 

总而言之, 友元关系不可以从基类继承到派生类中,并且友元要慎用且不宜多用。

2. 继承与静态成员

基类定义了static 静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子

类,都只有一个 static 成员实例 。

测试 demo 如下:

namespace Xq
{
	class person
	{
	public:
		person(const char* name = "lisi")
		:_name(name)
		{}
	public:
		std::string _name;
        // 静态成员属性
		static int count;
	};
	class worker : public person
	{
	public:

	protected:
		std::string _job_number = "111";
	};
    // 静态成员属性需要在类外定义
	int person::count = 0;
}

void Test2(void)
{
	Xq::person pobj;
	Xq::worker wobj;

    pobj._name = "wangwu";
	cout << pobj._name << endl;
	cout << wobj._name << endl;

	cout << "wobj.count: " << wobj.count << endl;
	// 基类更改这个静态成员
	pobj.count = 5;
	cout << "wobj.count: " << wobj.count << endl;

	cout << "&pobj.count = " << &pobj.count << endl;
	cout << "&wobj.count = " << &wobj.count << endl;
}

现象如下:

上面的_name,基类对象和派生类对象各自私有一份,而对于静态成员变量 count,派生类继承的 count 和基类里面的 count 是同一份。

即基类里面的静态成员,无论有多少个派生类,它们都共享同一个静态成员。

如果我们想确定基类和派生类一共实例化了多少个对象,我们可以这样做:

person(const char* name = "lisi")
:_name(name)
{
	++count; 
}

当实例化对象时 (无论是基类对象还是派生类对象),都会调用基类的构造函数,就会++count,且由于这个静态成员 count 是所有 ( 基类/派生类 ) 对象共享的,因此可以得到实例化 ( 基类/派生类 ) 对象的总个数;

3. 菱形继承以及菱形虚拟继承

3.1. 单继承

单继承:一个派生类只有一个直接基类,我们称这个继承关系为单继承。

比如有三个类,teacher 类继承 worker类,worker 类继承 person 类,如下所示:

class Person{};
// Worker 继承 Person 类
class Worker : public Person{};
// Teacher 公有继承 Worker 类
class Teacher : public Worker{};

如图所示:

3.2. 多继承

多继承:一个派生类有两个或以上直接基类时称这个继承关系为多继承。

比如 Assistant 类 继承 Student 类和 Teacher 类,如下:

class Student{};
class Teacher{};
// Assistant 类 公有继承 Student 和 Teacher
class Assistant : public Student, public Teacher {};

如图所示:

3.3. 菱形继承

菱形继承:菱形继承是多继承的一种特殊情况。 

比如 Assistant 类继承 Student 类和 Teacher 类,同时 Student 类继承 Person 类,Teacher 继承 Person类,就形成了一个菱形继承,如下:

具体如下:

namespace Xq
{
	class Person
	{
	public:
		std::string _name;  //姓名
	};
	class Teacher : public Person
	{
	protected:
		std::string _id_card;  //职工编号
	};
	class Student : public Person
	{
	protected:
		std::string _num;  // 学号
	};
	class Assistant : public Teacher, public Student
	{
	protected:
		std::string _subject;  //学科
	};
}

void Test(void)
{
    // 实例化一个 Assistant 对象
	Xq::Assistant obj;
}

菱形继承的问题:从下面的 Assistant类的对象成员模型,可以看出菱形继承有数据冗余和二义性的问题。 

可以看到,Assistant 的对象中 person 成员 (_name) 会有两份 (一份是 Teache r类中的,另一份是 Student 类中的),并且访问时会存在二义性问题,如下 demo: 

void Test(void)
{
	Xq::Assistant obj;
	obj._name = "haha";
}

现象如下: 

二义性问题即调用不明确,编译器在这里不知道这个_name 是哪一个类中的_name, 也许是 Student 这个类中的,也许是 Teacher 这个类中的,这就是菱形继承的二义性问题。

当然我们也可以解决这种问题,方案是:指明类域,如下 demo:

void Test(void)
{
	Xq::Assistant obj;
	obj.Teacher::_name = "haha";
	obj.Student::_name = "hehe";
}

此时就可以成功编译,现象如下:

虽然可以通过指明类域解决菱形继承的二义性问题,但是菱形继承的数据冗余的问题没有解决。 

因此,人们在此基础上引入了菱形虚拟继承,虚拟继承可以解决菱形继承的二义性问题和数据冗余的问题。

如上面的继承关系,在 Student 和 Teacher 继承 Person 时使用虚拟继承,即可解决二义性问题和数据冗余问题,需要注意的是,虚拟继承不要在其他地方去使用。

3.4. 菱形虚拟继承

菱形虚拟继承:

比如 Assistant 类继承 Student 类和 Teacher 类,同时 Student 类虚拟继承 Person 类,Teacher 虚拟继承 Person类,就形成了一个菱形虚拟继承,如下图所示:

上图对应的 code 如下:

namespace Xq
{
	class Person
	{
	public:
		std::string _name;  //姓名
	};
	class Teacher : virtual public Person  //虚拟继承
	{
	protected:
		std::string _id_card;  //职工编号
	};
	class Student : virtual public Person  //虚拟继承
	{
	protected:
		std::string _num;  // 学号
	};
	class Assistant : public Teacher, public Student
	{
	protected:
		std::string _subject;  //学科
	};
}

void Test()
{
	Xq::Assistant obj;
	obj._name = "haha";
}

此时还会有数据冗余问题和二义性问题吗? 结果如下所示:

通过现象,我们发现:

  • 首先,此时不指明类域,也可以成功编译,即菱形虚拟继承可以解决二义性问题;
  • 其次,我们发现,在 obj 这个对象中,它们的 _name 是同一个成员,因此,这也就解决了数据冗余的问题。 

3.5. 菱形继承的底层细节

为了探究菱形继承的底层细节,我们所用的 demo 具体如下:

根据上面的继承关系,写出如下 code,并通过 Test1 来进行测试:

namespace Xq
{
	class A
	{
	public:
		int _a;
	};

	class B : public A
	{
	public:
		int _b;
	};

	class C : public A
	{
	public:
		int _c;
	};

	class D : public B, public C
	{
	public:
		int _d;
	};
}


void Test1(void)
{
	Xq::D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
}

运行进程,通过监视窗口,得到下面的结果:

我们也可以观察此时的内存窗口,如下:

 

因此,结合上面的两个窗口,我们得到此时的菱形继承的d对象模型,具体如下:

可以看到,此时基类A中的成员 _a 在菱形继承的D类模型中出现了两次,即数据冗余问题,也正因为这个原因,当D类实例化的对象访问这个_a成员时,会存在着二义性的问题。

3.6. 菱形虚拟继承的底层细节

为了探究菱形虚拟继承的底层细节,我们所用的 demo 具体如下:

根据上面的继承关系,写出如下 code,并通过 Test2 来进行测试:

namespace Xq
{
	class A
	{
	public:
		int _a;
	};
	class B : virtual public A
	{
	public:
		int _b;
	};

	class C : virtual public A
	{
	public:
		int _c;
	};

	class D : public B, public C
	{
	public:
		int _d;
	};
}

void Test2(void)
{
	Xq::D d;
	d._a = 0;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
}

运行进程,通过监视窗口,得到下面的结果:

我们也可以观察此时的内存窗口,如下:

因此,结合上面的两个窗口,我们得到此时的菱形虚拟继承的d对象模型,具体如下: 

首先,上面的代码可以成功编译,因此,菱形继承解决了二义性问题;

其次, 在菱形虚拟继承的D类对象模型中,只有一份 _a (A类中的),因此也解决了数据冗余的问题 (在这里将_a放在了一个公共的区域)。

但是,我们发现一个事实,与菱形继承相比,多了两个地址,那有人说,这不是空间浪费了吗?

实际上,并没有,如果此时的A很大 (有很多成员),如果没有虚拟继承,那么就会造成大量的重复数据 (数据冗余),进而导致浪费空间,因此,事实上,虚拟继承会将冗余数据变成一份数据 (共享),因此会节省空间。

那现在的问题就是,这两个地址是什么呢?

通过内存窗口查看这个两个地址指向的内容,具体如下:

B类对象中的地址 0x0058dd1c 的内容如下: 

C类对象中的地址 0x0058dd38 的内容如下: 

我们发现这个地址里面存的是一个数字,B类对象中的地址存储的 0x00000014,即20;C类对象中的地址存储的 0x0000000c,即 12。

这两个数字我们称之为相对距离,有的也叫偏移量。

刚刚我们说了虚拟继承会把这里的 _a 放在一个公共的区域里面,那么对于B和C这两个类它是如何知道这个_a 所在的位置呢?

因此,B和C类就需要借助这个偏移量。

同时,我们发现,

为什么要设计这个偏移量呢? 原因是因为在某些情况下,我们必须借助偏移量找到这个公共的位置。

例如当发生切片的时候,具体如下:

void Test3(void)
{
	Xq::D d;
	// 发生切片的时候:
	// 比如 D 类对象经过切片得到B类(对象/引用/指针)
	Xq::B b1 = d;
	Xq::B& b2 = d;
	Xq::B* b3 = &d;

	// 也比如 D 类对象经过切片得到C类(对象/引用/指针)
	Xq::C c1 = d;
	Xq::C& c2 = d;
	Xq::C* c3 = &d;
}

因为此时这里的 _a 成员 (A类中的_a) 在菱形虚拟继承中的D类对象只有一份,这一份 _a 被放在了D对象模型中的公共位置。

在非虚拟继承中,_a 会在B类对象中有一份,在C类对象中也有一份,当发生切片时,B和C类很容易找到这个 _a;

但如果是虚拟继承,这个_a就在公共位置,而并没有在B和C类对象中,因此,当发生切片时,B和C类就需要借助偏移量找到这个_a。

我们用下图总结下菱形虚拟继承:

3.7. 虚拟继承

接下来,我们来探讨一下虚拟继承的细节问题,我们所用的 demo 具体如下:

根据上面的继承关系,写出如下 code,并通过 Test4 来进行测试: 

class A
{
public:
	int _a;
};
class B : virtual public A
{
public:
	int _b;
};

void Test4(void)
{
	Xq::B b;
    std::cout << "Xq::B b size: " << sizeof(b) << std::endl;
}

首先,有一个问题:我们单看B类,它实例化的对象是多大呢?

单看B类,它是一个单继承,因此里面一定会有_a (继承A类中的),那实例化的大小是8吗?这个虚拟继承是否会有影响?我们看看结果:

结果并不是8,那么肯定就是虚拟继承带来的变化了,我们通过内存窗口,查看一下,为什么是12呢?

不过在这之前,为了查看的更明显一点,我们更改一下测试代码,如下:

void Test5(void)
{
	Xq::B b;
	b._a = 1;
	b._b = 2;
	std::cout << "Xq::B b size: " << sizeof(b) << std::endl;
}

运行进程,调出内存窗口,现象如下:

我们发现菱形虚拟继承B类对象模型中有一个地址,而运行起来的进程是32位的,故地址是4字节,因此,B类的对象是12个字节。

既然它是一个地址,那么它的内容是什么呢?如下:

结合上面的分析,我们可以得出B对象的模型如下:

事实上,虚拟继承B类中的对象模型中会存储一个指针,这个指针只想一张表,这张表会存储偏移量的值,在这里因为是单虚拟继承,因此只有一个偏移量。

这里的指针 (偏移量地址) 我们称之为虚基表指针,这张表我们称之为虚基表。

4. 继承的总结

  • 很多人说 C++ 语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,也不建议设计出菱形继承,否则在代码维护以及性能上都有问题;
  • 多继承可以认为是 C++ 的缺陷之一,很多后来的oo(object--oriented) 语言都没有多继承,如 Jave;
  • 继承和组合:
    • public 继承是一种 is-a 的关系。也就是说每个派生类对象都是一个基类对象;
    • 组合是一种 has-a 的关系。假设 B 组合了 A ,每个 B 对象中都有一个 A 对象;
    • 优先使用对象组合,而不是类继承。
    • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用 (white-box reuse) ;
    • 术语 “ 白箱 ” 是相对可视性而言:在继承方式中,基类的内部细节对子类可见,继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响,派生类和基类间的依赖关系很强,耦合度高;
    • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用 (black-box reuse) ,因为对象的内部细节是不可见的。对象只以 “ 黑箱 ” 的形式出现。组合类之间没有很强的依赖关系,耦合度低;
    • 优先使用对象组合有助于保持每个类的封装特性;
    • 实际中尽量多去使用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。

下面用实例说明什么是 is-a (继承),什么是has-a (组合)?demo 具体如下:

is-a关系:

class A                                                            
{                                                                  
public: 
  void func() { cout << _a << endl;}
protected:
  int _a;                                                         
};   
                                                              
// (公有)继承 --- is-a关系
// 由于A中成员的改动可能会影响B类,体现了耦合度高的特点
                                                              
class B : public A                                                 
{                                                                  
public:     
  //...                                                     
};

has-a关系:

class C                                                            
{                                                                  
public: 
  void func() {cout << _c << endl;}
protected:
  int _c;                                                          
};  
                                                               
// 组合 --- has-a关系
// 而对于组合而言,C的保护改动基本不影响D类,体现了耦合度低的特点     
                                                            
class D                                                            
{                                                                  
public:   
  // ...                                                                                                                       
protected:      
    class C _c;  // (组合)    
};

在未来代码设计中,遵循的设计原则是:低耦合,高内聚。

5. 相关继承练习题

5.1. 如何定义一个无法被继承的类?

第一种方式,将基类的构造私有化,派生类继承这个基类,在实例化对象时,需要调用基类的构造,但由于基类的构造已经私有化,故会编译报错。

namespace Xq
{
	class A
	{
	public:
    
    //将基类的构造函数私有化
	private:   
		A(int a = int())
			:_a(a)
		{
			cout << "A()" << endl;
		};
	protected:
		int _a;
	};

	class B : public A
	{
	protected:
		int _b;
	};
}
 
void Test3(void)
{
	Xq::B b;
}

现象如下: 

上面的做法是 C++98 的做法,而对于 C++11 的做法是:通过关键字 final,被 final 修饰的类,无法被继承,编译器会强制检查。

namespace Xq
{
	// 用 final 修饰 A类, 此时A类无法被继承
	class A final
	{
	public:
		A(int a = int()) 
			:_a(a)
		{
			std::cout << "A()" << std::endl;
		};
	protected:
		int _a;
	};

	class B : public A
	{
	protected:
		int _b;
	};
}

void Test4(void)
{
	Xq::B b;
}

现象如下: 

5.2. 关于切片的问题

class Base1 { public: int _b1;};
class Base2 { public: int _b2;};
class Derive : public Base1, public Base2 {public: int _d; };
 
int main()
{
    Derive d;
    Base1* p1 = &d;
    Base2* p2 = &d;
    Derive* p3 = &d;
    return 0;
}
 
// A:p1 = p2 = p3   
// B: p1 < p2 < p3
// C: p1 == p3 != p2  
// D: p1 != p2 != p3

做这种问题,我建议先把 Derive 的对象模型画出来,如下:

此时,我们再代入问题,如下: 

 

现在,我们就得出答案了, p1 == p3 != p2。

不过我需要补充几点:

在多继承场景下,派生类继承多个基类时,是先继承的谁呢?

当派生类继承多个基类时,写在前面的就先继承谁。

诸如上面的例子:

class Derive : public Base1, public Base2 {public: int _d; };

此时就是先继承 Base1,在继承 Base2,Derive 对象模型如下:

 如果是下面这样呢?

class Derive : public Base2, public Base1 {public: int _d; };

此时就是先继承 Base2,在继承 Base1,Derive 对象模型如下:

我们发现这个问题的结果是:p1 == p3 != p2,但请记住,虽然 p1 和 p3 指向了同一个位置,但意义不同,因为,指针的类型决定了它能访问内容的大小,如下所示:

同时,我们再看一看对象里面的成员在内存中如何存储的,我们通过下面的 demo 测试:

void Test4()
{
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;

	p1->_b1 = 1;
	p2->_b2 = 2;
	p3->_d = 3;
}

通过内存窗口得到如下现象:

换言之,这三者的地址关系应该是: p1 == p3 < p2。

5.3. 选择正确选项

如下所示:

class A{
public:
	A(char *s) { std::cout << s << std::endl; }
	~A(){}
};
class B :virtual public A
{
public:
	B(char *s1, char*s2) :A(s1) { std::cout << s2 << std::endl; }
};
class C :virtual public A
{
public:
	C(char *s1, char*s2) :A(s1) { std::cout << s2 << std::endl; }
};
class D :public B, public C
{
public:
	D(char *s1, char *s2, char *s3, char *s4) :C(s1, s3), B(s1, s2), A(s1)
	{
		std::cout << s4 << std::endl;
	}
};
int main() {
	D *p = new D("class A", "class B", "class C", "class D");
	delete p;
	return 0;
}

// A:class A class B class C class D 
// B:class D class B class C class A
// C:class D class C class B class A 
// D:class A class C class B class D

首先我们要知道,初始化列表的初始化顺序是由成员声明顺序决定的,而在继承中,谁先被继承,谁就先初始化。

在这里,A类最先被D类继承,然后D类继承B,在继承C,因此A类最先被初始化,下来是B,下来是C,因此最后的答案是A。

结果如下:

继承的宏观内容到此结束。 

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

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

相关文章

Android:弹出对话框方式梳理一览(一)

Android&#xff1a;弹出对话框方式梳理一览&#xff08;一&#xff09; Guide&#xff5c;导言 在Android开发中&#xff0c;对话框可能是我们与用户交互的非常常用的方式&#xff0c;包括弹出一个小界面&#xff0c;可能在实际场景中都非常实用。本篇文章主要就是对Android弹…

陪诊系统|陪诊小程序成品|陪诊系统功能

随着人们对健康的日益关注以及医疗技术的不断进步&#xff0c;陪诊小程序应运而生&#xff0c;通过提供陪同就医、医疗服务和健康管理等功能为患者和家庭成员提供了更多的便利和选择。本文将分析陪诊小程序的关键功能&#xff0c;以便更好地理解其在医疗领域的作用。 在陪诊小程…

分布式任务调度工具 XXL-JOB

默认的账号密码是&#xff1a;admin/123456 一&#xff0c;部署docker容器 docker run \ -e PARAMS"--spring.datasource.urljdbc:mysql://192.168.150.101:3306/xxl_job?Unicodetrue&characterEncodingUTF-8 \ --spring.datasource.usernameroot \ --spring.dataso…

GT资源-Clock资源

一、Transmitter 时钟分布 XCLK&#xff1a;在使用TX buffer的模式下&#xff0c;XCLK来源于TXOUTCLK。在使用TX bypassing的模式下XCLK来源于TXUSERCLK。TXUSRCLK是GTX/GTH中PCS的内部逻辑时钟。TXUSRCLK2是GT Transceiver 用户侧逻辑时钟。 TXUSRCLK与TXUSRCLK2的关系 FPGA …

阿赵UE引擎C++编程学习笔记——信息打印输出

大家好&#xff0c;我是阿赵。   在之前介绍HelloWorld的时候&#xff0c;使用了一个打印的命令&#xff0c;把HelloWorld输出到输出日志里面。   对于我们编写程序代码来说&#xff0c;有2个手段是对程序差错非常重要的&#xff0c;一个是断点&#xff0c;另外一个是输出日…

很快就可以试用Domino 15了

大家好&#xff0c;才是真的好。 前几天在比利时的安普卫特举办的Engage2024大会已经结束&#xff0c;流出的现场照片很多&#xff0c;主要是会议场地照片很多&#xff0c;说是令人震撼&#xff1b;可惜这次一手的PPT和会议内容不多.是的&#xff0c;本来我也是在等与会者写的…

安装Jupyter notebook NbExtensions遇到的问题

在安装 Jupyter notebook NbExtensions 时&#xff0c;使用下列代码&#xff1a; pip install jupyter_nbextensions_configurator jupyter_contrib_nbextensions jupyter contrib nbextension install --user jupyter nbextensions_configurator enable --user ——————…

获取京东商品详情,API返回值说明全攻略

京东商品详情API是开发者获取京东平台上商品详细信息的重要工具。通过调用API并解析返回的响应数据&#xff0c;您可以快速获取商品的各项属性&#xff0c;如商品ID、标题、价格、图片等。下面&#xff0c;我们将为您详细介绍京东商品详情API的返回值说明&#xff0c;帮助您更好…

(五)JVM实战——JVM性能调优与监控

JVM调优案例的场景 为什么要调优&#xff1a;防止或者解决jvm虚拟机中的OOM问题&#xff1b;减少FullGC出现的频率&#xff0c;解决系统运行卡、慢问题JVM调优案例的四个方面 OOM(堆溢出)&#xff1a;java heap spaceOOM(元空间溢出)&#xff1a;MetaspaceOOM(GC overhead lim…

【Android项目】“追茶到底”项目介绍

没有多的介绍&#xff0c;这里只是展示我的项目效果&#xff0c;后面会给出具体的代码实现。 一、用户模块 1、注册&#xff08;第一次登陆的话需要先注册账号&#xff09; 2、登陆&#xff08;具有记住最近登录用户功能&#xff09; 二、点单模块 1、展示饮品列表 2、双向联动…

k8s 资源文件参数介绍

Kubernetes资源文件yaml参数介绍 yaml 介绍 yaml 是一个类似 XML、JSON 的标记性语言。它强调以数据为中心&#xff0c;并不是以标识语言为重点例如 SpringBoot 的配置文件 application.yml 也是一个 yaml 格式的文件 语法格式 通过缩进表示层级关系不能使用tab进行缩进&am…

软考是否存在包过班?

国家考试&#xff0c;虽然有人喊着包过&#xff0c;但你也别轻易相信啊&#xff0c;姐妹&#xff0c;要好好保护好你的钱包啊&#xff01;这种考试是没有所谓的包过的。 给你一些学习软考需要注意的要点&#xff1a; 1、深入理解考试内容&#xff1a; 在准备软考之前&#xf…

前端传递list(数组)类型参数,后端接收失败

一顿报错,我之前遇到的list都是Long类型 貌似用GET也是可以的,但是很奇怪一直报错 就是不可以 后来去百度 查询到可以用两种方法解决这个问题 1、拆开 传 以GET方式&#xff0c;后端GetMappingRequestParam接收。 2、以Post方式传&#xff0c;后端创建dto PostMappingReques…

自动化测试再升级,大模型与软件测试相结合

近年来&#xff0c;软件行业一直在迅速发展&#xff0c;为了保证软件质量和提高效率&#xff0c;软件测试领域也在不断演进。如今&#xff0c;大模型技术的崛起为软件测试带来了前所未有的智能化浪潮。 软件测试一直是确保软件质量的关键环节&#xff0c;但传统的手动测试方法存…

linux系统-部署YUM仓库及NFS共享服务

目录 一、YUM概述 二、准备安装源 软件仓库的提供方式 RPM软件包的来源 构建Centos7软件仓库 在软件仓库中加入非官方RPM包组 三、搭建yum软件仓库 四、NFS文件共享服务 NFS简介 使用NFS发布共享资源&#xff08;安装nfs-utils、rpcbird软件包&#xff09; 设置共享目…

记一次DNS故障导致用户无法充值的问题(上)

背景&#xff1a; 刚刚过去了五一劳动节&#xff0c;回来后一上班接到客服运营团队反馈的节日期间的问题&#xff0c;反馈有部分用户无法充值。拿到的反馈资料有&#xff1a; 无法充值操作视频、问题时间、手机机型、手机网络情况。 1、从视频中看到用户点击支付后没有任何反…

jenkins部署服务到windows系统服务器

1、安装openSSH windows默认不支持ssh协议&#xff0c;需要下载安装&#xff0c;主要适用于jenkins传输文件已经执行命令使用 点击查看下载openSSH 2、项目配置 这里简单说说怎么配置&#xff0c;主要解决点就是ssh执行cmd或shell命令时不能开启新窗口导致应用部署失败或者断…

vue使用pdfjs-dist在电脑上展示PDF文件

安装 安装的时候一定要带上版本号,这里采用的是2.0.943(因为这个版本对于我目前的项目比较合适可以正常使用,其他版本大概率会报错),当前项目使用的是vue2,vue的版本是2.5.10 npm install pdfjs-dist@2.0.943 查看版本发现这玩意版本非常之多 使用 在使用pdfjs-dist库…

Python实现打砖块游戏

提供学习或者毕业设计使用&#xff0c;功能基本都有&#xff0c;不能和市场上正式游戏相提比论&#xff0c;请理性对待&#xff01; 在本文中&#xff0c;我们将使用 Pygame 和 Tkinter 创建一个简单的打砖块游戏。游戏的目标是通过控制挡板来击碎屏幕上的砖块&#xff0c;同时…

GDPU 天码行空11

&#xff08;一&#xff09;实验目的 1、掌握JAVA中IO中各种类及其构造方法&#xff1b; 2、重点掌握IO中类所具有的IO操作方法&#xff1b; 3、熟悉软件中登录模块的开发方法&#xff1b; 4、掌握IO中读写常用方法。 5、进一步熟悉正则规则的使用方法。 &#xff08;二&…