C++ [继承]

继承

本文已收录至《C++语言和高级数据结构》专栏!
作者:ARMCSKGT

CSDN


继承

  • 前言
  • 正文
    • 继承的概念及定义
      • 继承的概念
      • 继承的定义
      • 重定义
    • 基类和派生类对象赋值转换
    • 派生类中的默认成员函数
      • 隐式调用
      • 显示调用
    • 继承中的友元与静态成员
      • 友元
      • 静态成员
    • 菱形继承
      • 概念
    • 虚继承原理
    • 继承和组合
  • 最后


前言

面向对象的三大特性:封装,继承和多态,前面我们介绍了类和对象,如何对数据和数据操作方法进行封装,本章将为大家介绍另一大特性-继承,继承可以增强代码的复用性增强功能的可扩展性,我们将学习如何通过父类衍生出更多特性的子类!
水果类
这里,水果类作为父类,衍射出三种水果,苹果,西瓜和荔枝,这三种具体的水果都可以统称为水果,但是各自又有着自己的特性,例如苹果外皮是红色的,西瓜外皮是绿色的等等,这就是继承,继承将一个抽象的父类更加具体的实例表达!


正文


继承的概念及定义


继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。

其中:

  • 被继承的类是:父类/基类 (base)
  • 继承的类称为:子类/派生类 (derived)

继承的本质就是复用代码,我们通过一个校园信息管理类代码来展示和说明:
#include <iostream>
#include <string>
using namespace std;

>//基本人员信息类
class Person
{
public:
	string Name;         //姓名
	int Car_ID;          //身份证号
	string Date;         //生日
	long long telephone; //电话号码
};

//教师类
class Teacher : public Person
{
public:
	int Employee_ID; //教师员工号
	string Subjects; //教师所教科目
	double Wages;    //工资
	//...
};

//学生类
class Student : public Person
{
public:
	int Student_ID;     //学生学号
	string Faculties;   //学生所属院系
	string Specialized; //学生专业
	//...
};

我们在学校,最常见的两个身份就是老师和学生,老师和学生都有自己的姓名和身份证号等,但是老师有自己的员工号和任职科目,学生有自己的学号和专业信息,通过继承父类Person可以让老师和学生都拥有人的基本信息,然后在老师和学生类中增加不同身份所独有的特性,这样就不需要每个类都写上人员基本信息的变量条目了,实现了复用,当然函数后亦是如此!

继承的作用: 子类在继承父类后,会继承父类 公开(public)和保护(protected) 的成员,除了父类私有成员外,子类可以继承其余的所有成员!
结合访问限定符,可以让子类合理访问父类成员,互不干扰!

继承的定义

介绍完继承的定义相关知识,我们开始使用继承!
继承的格式如下:

class 子类 : 继承方式 父类 { /*子类成员*/ };

继承的使用

继承方式与权限
继承方式与权限

关于访问限定符有三种:

  • 公有(public):该成员可以被任意访问
  • 保护(protected):该成员只能被本类和子类中访问
  • 私有(private):该成员只能在本类中被访问

其中:保护在没有继承的情况下和private一样,外界无法访问,在有父子类的情况下才能有区别!
权限范围从大到小依次为: public > protected > private

访问限定符有三种,继承的方式则也有三种,继承方式与成员的访问限定符组合可以有多种情况:

访问/继承 权限公有public保护protected私有private
父类public成员仍然为public变为protected变为private
父类protected成员仍然为protected仍然为protected权限为private
父类private成员不可见不可见不可见

补充:

  • 父类(基类)的私有成员在子类中始终不可见,但并不代表不在子类中,其在子类中仍然被继承,但是不可见!
  • 父类(基类)的成员如果不想在类外被访问,但是可以被子类访问,则设置为protected,protected是专门为该场景设计的!
  • 在成员的继承上,权限只能被缩小,不能被放大,即成员最终的权限是在成员本身的访问权限和继承权限中取最小权限即可!
  • 在class继承时,如果我们不显示指定继承方式,则默认为private;在struct继承时,如果不显示指定继承方式,则默认为public;平时不建议省略继承方式!
  • 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强!

重定义

重定义也叫隐藏(覆盖),是指子类继承父类后,子类重新声明的成员名字与父类相同,此时访问子类成员时默认访问的是子类的同名成员而隐藏父类成员(局部优先原则),同时也证明了父子类中的作用域是独立存在的!

class Base
{
public:
	void func() { cout << "Base num:" << num << endl; } //同名方法
	void Bfunc() { cout << "Bfunc" << endl; } //非同名方法
	int num = 1; //同名变量
	char b = 'b'; //非同名变量
};

class Derived : public Base
{
public:
	void func() { cout << "Derived num:" << num << endl; }
	void Dfunc() { cout << "Dfunc" << endl; }
	int num = 2;
	char d = 'd';
};

int main()
{
	Derived der; //声明子类对象
	return 0;
}

执行同名函数func:
执行同名函数func
此时我们发现,对于同名函数和同名变量,执行时访问子类的同名函数和变量!

如果我们删除子类的同名变量再执行:

此时执行的是子类函数访问的是父类的num变量!

访问各自的非同名成员:
访问各自的非同名成员
对于非同名成员,访问是正常的!

如果我们想要访问父类的隐藏成员,可以使用 作用域限定符 :: 指定父类成员进行显示访问!

int main()
{
	Derived der;
	der.Base::func();
	cout << der.Base::b << endl;
	return 0;
}



总结:

  • 在继承体系中基类和派生类都有独立的作用域。
  • 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
    也叫重定义(在子类成员函数中,可以使用 基类::基类成员 显示访问)。
  • 如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  • 注意在实际中在继承体系里面最好不要定义同名的成员。

基类和派生类对象赋值转换


派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割
。寓意把派生类中父类那部分切来赋值过去。

基类对象不能赋值给派生类对象,子类是在基类的基础上进行拓展,将基类赋值给子类时需要编译器去推敲父类对象中需要增加什么,编译器不敢随便做这种事情(毕竟儿子不可能道反天罡)。

当基类的指针(引用)指向子类对象时,可以通过强制类型转换赋值给子类的指针(引用)。注意!这里必须是基类指针(引用)指向子类对象才是安全的!如果基类如果是多态类型,可以使用C++11中的安全的类型转换 dynamic_cast 进行赋值!
切片

总结:

  • 对于父类和子类之间的对象赋值会涉及类型转换,类似于double转int等。
  • 子类可以赋给父类对象,其中会产生类型转换,所以会有临时变量。
  • 但是父类不能赋值给子类(可以理解为子类的成员比父类多,父类无法判断),但是父类的指针可以指向子类。
  • 子类赋值给父类需要公开继承,是天然支持的,不存在类型转换(子类对象是一个特殊的父类对象)。
class Person
{
protected:
	string _name; // 姓名
	string _sex; // 性别
	int _age; // 年龄
};
class Student : public Person
{
public:
	int _No; // 学号
};

int main()
{
	Student sobj;
	// 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj;
	Person* pp = &sobj;
	Person& rp = sobj;

	//2.基类对象不能赋值给派生类对象 例如 sobj = pobj;
	
	// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
	pp = &sobj;
	Student * ps1 = (Student*)pp; // 这种情况转换时可以的。
	ps1->_No = 10;
	pp = &pobj;
	Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
	ps2->_No = 10;

	return 0;
}

派生类中的默认成员函数


前面我们介绍过,类的六大默认成员函数:
切片
同样的,子类在继承父类后也会有这六个默认成员函数;但是子类不写类的六大默认函数则调用时使用父类的默认函数,如果实现了这六个默认成员函数则按照隐藏的规则,在调用时默认调用子类的这六个默认成员函数,对于父类的则需要显示调用,这里我们对这种情况进行分析!

隐式调用

子类在实例化调用构造函数时,会先调用父类的构造函数先初始化父类再初始化子类本身;同样的,子类在析构时也会先析构自己再调用父类的析构函数析构父类空间!

class Base
{
public:
	Base() { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
};

class Derived : public Base
{
public:
	Derived() { cout << "Derived()" << endl; }
	~Derived() { cout << "~Derived()" << endl; }
};

int main()
{
	Derived der;
	return 0;
}


注意: 自动调用是由编译器完成的,如果不存在默认构造函数,那么需要我们手动在子类的构造函数中初始化父类对象,如果不处理则会报错!


显示调用

对于一些类,我们不妨会使用 赋值重载拷贝构造 这里如果我们不对相关函数进行特殊处理,在发生拷贝时,子类无法调用父类的赋值重载和拷贝构造函数,父类部分极易容易发生浅拷贝!

class Base
{
public:
	Base() {}
	~Base() {}
	Base(const Base& b) { cout << "Base(const Base& b)" << endl; }
	Base& operator=(const Base& b)
	{ 
		cout << "operator=(const Base& b)" << endl; 
		return *this;
	}
};

//子类中不对父类对象做特殊除了
class Derived : public Base
{
public:
	Derived() {}
	~Derived() {}
	Derived(const Derived& d) { cout << "Derived(const Derived& d)" << endl; }
	Derived& operator=(const Derived& d) 
	{ 
		cout << "operator=(const Derived& d)" << endl; 
		return *this; 
	}
};

//子类中对父类进行特殊处理
//class Derived : public Base
//{
//public:
//	Derived() {}
//	~Derived() {}
//	Derived(const Derived& d):Base(d) //切片 构造父类对象 初始化父类
//	{ 
//		cout << "Derived(const Derived& d)" << endl; 
//	}
//	Derived& operator=(const Derived& d) 
//	{ 
//		Base::operator=(d); //切片 构造父类对象 赋值给子类的父类部分
//		cout << "operator=(const Derived& d)" << endl; 
//		return *this; 
//	}
//};

int main()
{
	Derived der1;
	Derived der2(der1);
	cout << "-----------------------------" << endl;
	der1 = der2;

	return 0;
}

我们分别对特殊处理和不特殊处理的Derived类,在同一段main函数代码下测试,测试结果:


我们可以发现,在子类的成员函数中对父类隐藏的成员函数显示调用进行特殊处理,可以避免浅拷贝的问题,而且我们可以在子类中 通过 初始化列表:: 显示调用父类 构造函数 和 默认成员函数父类的拷贝问题进行处理!

总结:

  • 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认
    的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  • 派生类的拷贝构造函数必须调用基类的拷贝构造在初始化列表完成基类的拷贝初始化。
  • 派生类的operator=必须要调用基类的operator=完成基类的复制。
  • 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
    保证派生类对象先清理派生类成员再清理基类成员的顺序。
  • 派生类对象初始化先调用基类构造再调派生类构造,析构清理先调用派生类析构再调基类的析构(父类总是先构造最后析构)。
  • 对于析构函数,编译器会统一处理成 destructor 函数,对于父类的析构函数,我们不能显示调用,因为父类先构造,根据栈区规则必须先析构子类,此时因为类析构被统一处理为destructor,所以父类析构函数被隐藏,析构时只执行了子类析构造成父类内存空间不能被正常释放,此时我们需要对父类的析构函数进行重写(将父类析构使用virtual修饰成为虚函数)以满足析构要求,在多态中我们会重点介绍。


继承中的友元与静态成员


友元

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


静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。
静态变量为于静态区,不同于普通的堆栈区,静态变量的声明周期很长,通常是程序运行结束后才会被销毁,因此 假设父类中存在一个静态变量,那么子类在继承后,可以共享此变量!

class Base
{
public:
	void Bfunc() { cout << "Base a=" << a << endl; }
	static int a;
};

class Derived : public Base
{
public:
	void Dfunc() { cout << "Derived a=" << a << endl; }
};

int Base::a = 0;
//int Derived::a = 1; //继承中的静态成员只能被指定一次初始化

int main()
{
	Base b;
	Derived d;
	b.Bfunc();
	Base::a = 1;
	d.Dfunc();
	Derived::a = 2;
	b.Bfunc();
	return 0;
}


如预期所料,无论我们是使用子类访问静态成员a还是父类访问静态成员a都访问的是同一个静态成员a!


菱形继承


C++中,继承是可以单继承也可以多继承,单继承就是一个子类只继承一个父类,多继承就是一个子类继承多个父类!

关于多继承,即支持一个子类继承多个父类,使其基础信息更为丰富,但凡事都有双面性,多继承 在带来巨大便捷性的同时,也带来了个巨大的坑,即菱形继承问题!

因为多继承的弊端,其他大部分面向对象的语言都禁止多继承!

关于多继承,只需要使用 , 将继承的多个父类连接起来即可

class 子类 : 继承方式 父类1, 继承方式 父类2, .... { /*子类成员*/}

单继承与多继承

概念

菱形继承是多继承的一种特殊情况!
菱形继承
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余二义性的问题。
在Assistant的对象中Person成员会有两份。
菱形继承下的子类成员问题

class Person
{
public:
	string _name; // 姓名
};

class Student : public Person
{
protected:
	int _num; //学号
};

class Teacher : public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

int main()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter"; //此语句会报错

	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";

	return 0;
}


我们注释掉这句错误访问代码,编译运行进行调试发现:

a对象中出现了两个 _name 变量,如果我们直接访问,编译器会提示不明确,因为编译器不知道我们要访问哪一个 _name 。

此时我们仍然要坚持访问 _name变量 只能通过 :: 指定访问域进行访问:

这种方法解决了访问的问题,但是数据冗余和二义性的问题仍然没有被解决!

我们在实际使用中不需要两个 _name ,我们想 _name 是唯一的,此时我们就需要借助虚继承(与虚函数没有任何关系)来解决这个问题,换句话说,虚继承就是专门为解决菱形继承而产生的!

虚继承方式:

class 子类 : virtual 继承方式 父类 { /*子类成员*/ }

这就是对父类的虚继承!
需要注意的是,虚拟继承不要在其他地方去使用。


我们只需要在多继承的腰部,也就是父类在继承父父类时添加virtual即可!

class Person
{
public:
	string _name; // 姓名
};

class Student : virtual public Person
{
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

int main()
{
	Assistant a;
	a._name = "peter"; //此时可以正常赋值

	return 0;
}

虚继承
此时我们可以发现,我们可以通过a对象直接访问 _name ,且修改 _name 后 所有的_name成员都发生了变化,此时就解决了数据冗余和二义性的问题!


此时内存中的成员分布为:

为什么会是这样?接下来我们探究一下虚继承的原理!

虚继承原理

利用 虚基表 将冗余的数据存储起来,此时冗余的数据合并为一份,原来存储 冗余数据 的位置,现在用来存储 虚基表指针

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成
员的模型。

class A
{
public:
	int _a;
};

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

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

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

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

下图是菱形继承的内存对象成员模型:这里可以看到数据冗余

B和C类中都有各自的A类成员!

下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下
面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指
向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量
可以找到下面的A。


此时无论这个 冗余 的数据存储在何处,都能通过 基地址 + 偏移量 的方式进行访问!

补充:
当我们使用父类指针指向子类时,会发生父子类赋值兼容问题,也就是切片,切出父类的那一部分,从首地址开始,但我们访问公共成员时,仍然是通过 基地址+偏移量 实现的!

当然我们也可以实例化B对象,让ptrb指向b对象:

我们发现,当不存在继承关系时,只要我们对父类进行了虚继承,其父类对象的存储发生就是在 原数据位存放虚表地址来获取偏移量,再通过偏移量找到父类的成员!

之所以这么做,就是因为对象指针ptrb可能指向子类类型的对象,也可能指向本类类型对象,此时如果我们要访问父类成员 _a ,一条语句 ptrb->_a 对于指向D对象和指向B对象的地址不同场景需要有不同的处理方式,为了统一操作,只要是虚继承,其父类对象的都放在对象后面,采用偏移量的方式去访问,只要简化了处理过程!
当编译器识别到需要通过 基地址+偏移量 去访问时,会在汇编指令中进行特殊处理,而不是按照原方式直接去访问对象中的成员!

如果虚继承的父类中其成员是另一个对象存在多个成员,在访问时编译器先通过偏移量找到该成员对象的首地址,然后通过成员对象的声明顺序去依次访问,该场景例如:

class test
{
public:
	int a;
	int b;
	int c;
};
//此时被虚继承的父类A中有一个对象t !
class Base
{
public:
	test t;
};

class Derived : virtual public Base {};
//此时Derived实例的对象首地址仍然是存放Base对象中t对象所在偏移量
//访问时,通过int一个一个跳过即可(即使涉及内存对齐问题,编译器也会根据规则做出调整)

所以,无论最终位置在何处,最终汇编指令都一样(得益于偏移量的设计模式)!


总结:

  • 虚继承底层是如何解决菱形继承问题的?
    对于冗余的数据位,改存指针,该指针指向虚表,虚表中从首地址偏移4字节即为冗余的数据位偏移到数据地址的偏移量;对于冗余的成员,统一放置在后面,通过首地址和虚表中偏移量进行访问!
  • 为何在冗余处存指针?
    方便统一访问同一个变量,解决二义性和数据冗余问题;同时,偏移量存放在虚表首地址偏移4字节处(一般为第二个条目),而首地址的四个字节是将来存放多态的虚表地址的!
  • 虚基表指针 和 虚基表 是否会造成空间浪费?
    不会,指针大小固定为 4/8 字节;虚基表可以忽略不计,所有对象共享!

为了解决 菱形继承 问题,想出了 虚继承 这种绝妙设计,但在实际使用中,要尽量避免出现 菱形继承 问题!



说明:在多继承中,关于多个父类谁先初始化与其声明顺序有关,例如在上面的D类:

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

此时D类实例化会先初始化B再初始化C,最后初始化自己,所以初始化的顺序与继承时声明父类的顺序有关!


继承和组合


除了可以通过继承使用父类中的成员外,还可以通过 组合 的方式进行使用,前提是父类对象中的成员是public权限允许在类外访问或可以通过函数进行访问

关于继承和组合:

  • 公有继承:is-a —> 高耦合,可以直接使用父类成员
  • 组合:has-a —> 低耦合,可以间接使用父类成员
    实际项目中,更推荐使用 组合 的方式,这样可以做到 解耦,避免因父类的改动而直接影响到子类,不过,具体使用哪种方式还要取决于具体场景,具体问题具体分析!
//父类
class A {};

//继承-直接使用
class B : public A {};

//组合
class C
{
private:
   A _oa;  //创建 _oa 对象,使用成员及方法
}

继承的作用主要是为多态做准备,继承是多态不可或缺的一步!


最后

以上就是关于C++继承的内容,本节我们介绍了面向对象三大特性之一的继承,介绍了什么是继承,怎么用,有那些问题等等;最后介绍了C++多继承中的问题菱形继承,使用虚继承解决了这个问题,虚继承的原理等,这些知识将为后面的多态进行铺垫,大家在学习后一定要动手实践,多多琢磨!

本次 <C++ 继承> 就先介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!
C-PLUS-PLUS

🌟其他文章阅读推荐🌟
C++ <STL容器适配器> -CSDN博客
C++ <STL之list模拟实现> -CSDN博客
C++ <STL之list使用> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹

​​

​​


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

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

相关文章

讲座录播 | 邹磊教授:图数据库的概念和应用

2023年10月16日 由中国计算机学会主办的 “CCF Talk”直播间 进行了题目为 术语解读:“图计算”的内涵与应用 主题直播活动 讲座吸引7708人观看 图作为一种灵活表达复杂关联关系的数据结构&#xff0c;目前已广泛地应用于社会治理、医疗健康、电网分析、计算材料、计算育…

【MySQL】事务(中)

文章目录 事务异常与产出结论手动提交 和自动提交 对 回滚的区别 事务隔离性理论如何理解隔离性&#xff1f;MySQL的隔离级别事务隔离级别的查看设置隔离级别 事务异常与产出结论 在没有启动事务之前&#xff0c;account表中存在孙权和刘备的数据 在启动事务后&#xff0c; 向 …

【LIUNX】配置缓存DNS服务

配置缓存DNS服务 A.安装bind bind-utils1.尝试修改named.conf配置文件2.测试nslookup B.修改named.conf配置文件1.配置文件2.再次测试 缓存DNS服务器&#xff1a;只提供域名解析结果的缓存功能&#xff0c;目的在于提高数据查询速度和效率&#xff0c;但是没有自己控制的区域地…

洛谷P1923 【深基9.例4】求第 k 小的数(java)

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StreamTokenizer; import java.util.Arrays; import java.util.Scanner; //输入n个数字ai&#xff0c;输出这些数字的第k小的数。最小的数是第0小。 public cla…

高级数据分析方法与模型

前言 数据思维练习不仅要熟练地掌握了分析工具&#xff0c;还要掌握大量的数据分析方法和模型。 这样得出的结论不仅具备条理性和逻辑性&#xff0c;而且还更具备结构化和体系化&#xff0c;并保证分析结果的有效性和准确性。今天从以下6个维度36种分析模型和方法逐个简略介绍…

工作记录--(用HTTPS,为啥能被查出浏览记录?如何解决?)---每天学习多一点

由于网络通信有很多层&#xff0c;即使加密通信&#xff0c;仍有很多途径暴露你的访问地址&#xff0c;比如&#xff1a; DNS查询&#xff1a;通常DNS查询是不会加密的&#xff0c;所以&#xff0c;能看到你DNS查询的观察者&#xff08;比如运营商&#xff09;是可以推断出访问…

【蓝桥杯选拔赛真题67】Scratch鹦鹉学舌 少儿编程scratch图形化编程 蓝桥杯选拔赛真题解析

目录 scratch鹦鹉学舌 一、题目要求 编程实现 二、案例分析 1、角色分析

(三)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁…

4.CentOS7安装MySQL5.7

CentOS7安装MySQL5.7 2023-11-13 小柴你能看到嘛 哔哩哔哩视频地址 https://www.bilibili.com/video/BV1jz4y1A7LS/?vd_source9ba3044ce322000939a31117d762b441 一.解压 tar -xvf mysql-5.7.26-linux-glibc2.12-x86_64.tar.gz1.在/usr/local解压 tar -xvf mysql-5.7.44-…

高速高精运动控制,富唯智能AI边缘控制器助力自动化行业变革

随着工业大数据时代的到来&#xff0c;传统控制与决策方式无法满足现代数字化工厂对工业大数据分析与决策的需求&#xff0c;AI边缘控制器赋能现代化智慧工厂&#xff0c;实现工业智造与行业变革。 富唯智能AI边缘控制器&#xff0c;基于x86架构的IPC形态产品&#xff0c;通过…

用Powershell实现:删除所有不是与.json文件重名的.jpg文件

# 指定要搜索的目录路径 $directoryPath "C:\path\to\your\directory"# 获取该目录下的所有.jpg和.json文件 $jpgFiles Get-ChildItem -Path $directoryPath -Filter *.jpg $jsonFiles Get-ChildItem -Path $directoryPath -Filter *.json | Select-Object -Expan…

对于联邦政府来说,零信任只是一个开始

今年早些时候&#xff0c;美国空军国民警卫队的一名拥有绝密安全许可的成员向社交媒体平台 Discord 泄露了国家安全文件。 据报道&#xff0c;这些文件迅速传播到其他平台&#xff0c;其中包含有关美国和北约在俄罗斯军事行动的敏感信息&#xff0c;包括有关预期武器交付的详细…

2023年汽车驾驶员(高级)证考试题库及汽车驾驶员(高级)试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年汽车驾驶员&#xff08;高级&#xff09;证考试题库及汽车驾驶员&#xff08;高级&#xff09;试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;…

软件测试开发环境、测试环境、准生产环境、生成环境

在一个项目开发到发布的整个过程中&#xff0c;会使用到很多个环境进行测试和运行项目。最基本的开发环境、测试环境、准生产环境、生成环境 一、开发环境 开发环境顾名思义就是我们程序猿自己把项目放到自己的电脑上&#xff0c;配置好以后&#xff0c;跑起来项目&#xff0…

SpringCloud微服务:服务拆分

不同的数据库之间&#xff0c;如何共同调用&#xff1f;接下来讲讲两个数据库之间如何交互 1、微服务需要根据业务模块拆分&#xff0c;做到单一职责,不要重复开发相同业务 2、微服务可以将业务暴露为接口&#xff0c;供其它微服务使用 3、不同微服务都应该有自己独立的数据库…

(四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖…

【赠书第4期】机器学习与人工智能实战:基于业务场景的工程应用

文章目录 前言 1 机器学习基础知识 2 人工智能基础知识 3 机器学习和人工智能的实战案例 4 总结 5 推荐图书 6 粉丝福利 前言 机器学习与人工智能是当前最热门的领域之一&#xff0c;也是未来发展的方向。随着科技的不断进步&#xff0c;越来越多的企业开始关注和投入机…

哈希的介绍及开散列和闭散列的实现(c++)

本文主要对哈希的相关知识进行一定的介绍&#xff0c;并对哈希中结构的闭散列和开散列进行一定的介绍和部分功能的实现。 目录 一、哈希概念 二、哈希冲突 三、哈希函数 1. 直接定址法--(常用) 2. 除留余数法--(常用) 3. 平方取中法 4. 折叠法 5. 随机数法 6. 数学分析…

使用python批量修改图片名称

一、使用场景 修改模式&#xff1a;原图片名称.png 》 目标图片名称.png条件&#xff1a;目标图片名称 包含 原图片名称准备工作&#xff1a;目标图片名称填写在excel当中&#xff0c;把excel放进图片文件夹内 二、代码示例 import os import pandas as pd import numpy as …

C++ 11 新特性

目录 1. 支持特性的编译器版本2. 模板表达式中空格3. 空指针4. auto5. 统一初始化6. explict7. 范围for8. default&#xff0c;delete9. 化名模板&#xff08;alias template&#xff09;10. using11. noexcept12. override13. final14. decltype15. lambda16. Variadic Templa…