【C++进阶】继承详解

在这里插入图片描述

文章目录

  • 前言
  • 一、继承的概念及定义
    • 1.概念
    • 2.继承定义
      • 定义格式
      • 继承关系和访问限定
      • 继承基类成员访问方式的变化
  • 二、基类和派生类对象赋值转换
  • 三、继承中的作用域
  • 四、派生类的默认成员函数
  • 五、继承与友元
  • 六、继承与静态成员
  • 七、复杂的菱形继承及菱形虚拟继承
    • 1.单继承与多继承
    • 2.菱形继承的问题
    • 3.虚拟继承
    • 4.虚拟继承的模型及原理
  • 八、继承的总结和反思
    • 继承反思
    • 继承与组合

前言

万物皆对象,对象是具体的世界事物,面向对象的三大特征封装,继承多态封装,封装说明一个类行为和属性与其他类的关系,低耦合,高内聚;继承是父类和子类的关系,多态说的是类与类的关系。
前边我们对封装有了一个系统的认识,今天我们来学习继承的相关内容。

一、继承的概念及定义

1.概念

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

概念看起来不太好懂,我来举一个例子,让大家了解到继承是怎么样的。比如在一个学校管理系统里边,有很多不同的角色,例如老师,学生,保安,宿管阿姨,每实现一个角色都得去实现一个类,但是这些类里边有很多属性是相同的,例如这些角色都是人,他们都有人的属性,例如姓名,年龄,电话号等等,所以当我们需要把这些共有的属性实现为一个类,在实现每个不同的角色时进行继承,就可以节省空间,也可以使代码简洁。

class person
{
public:
	char* _name;
	int _age;
};
//进行继承
class student : public person
{
public:
	int _studentId;
};

class teacher : public person
{
public:
	int _workId;
};

为了验证上述的代码,我们来调试观察一下:
在这里插入图片描述
我们发现在学生类的老师类中也有person类的属性,这也就是继承成功了。

2.继承定义

定义格式

在上述的例子中,我们把person类称为基类,也叫父类,把继承父类的类称为子类,或者派生类,但是大家切记,子类和父类是对应的,派生类和基类对应的。
在继承时应该遵循下图的格式:
在这里插入图片描述

继承关系和访问限定

C++的继承关系和访问限定比较复杂,这是因为C++是早期开始面向对象的语言,所以在设计上难免有不妥当的地方,但是语言必须向前兼容,所以C++的继承关系和访问限定复杂就留了下来,下边来看他们:
在这里插入图片描述
在这里插入图片描述

继承基类成员访问方式的变化

由于在一个类中,访问限定有三种,分别是public,protected,private
而一个类在继承父类时,又有三种继承方式,分别是public,protested,private
所以按道理来说,在继承之后,子类中应该有九种情况要分别讨论:
在这里插入图片描述
如果让我们背过这几种关系也并不难,但是千万不能死记硬背,而是去寻找规律的记忆他们。
通过观察,我们应该可以得到以下的结论:
1.基类中的私有成员,在继承到派生类之后,都在派生类中不可见。这里的不可见是指基类的私
有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面
都不能去访问它。
2.我们认为权限的大小为public>protected>provate,有了这样的公式,我们发现当类成员的访问限定符与在子类中的继承方式相遇时,最后在子类中的访问关系取他们之中权限较小的一个,例如基类的public成员使用pretected方式继承,最终派生类中的成员的访问属性就是protected的。
3.class类和struct结构体如果没有写出继承方式,默认class与默认权限一样,都是private,struct默认继承方式和默认权限相同,都是public,但是最好写出继承方式。

代码示例:
上边的例子,当class不显式的给定继承关系时,会默认采用私有继承,但是私有继承的成员在子类中是不可见的,所以下边访问他们就报错。
在这里插入图片描述
4.在实际的使用中,并不会经常用到private/protected继承,而大多数只会使用公有继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里
面使用,实际中扩展维护性不强。

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

1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 或者切割。寓意把派生类中父类那部分切来赋值过去。
2.基类对象不能赋值给派生类对象
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。(这些东西后续的多态章节会讲到)。

可能在看完文字描述之后大家还不是很清楚,我们来通过图片看一下什么叫做切片:
在这里插入图片描述
当我们使用person继承得到一个student类时,在公有继承的情况下,我们会发现,子类成员有了父类成员所有的属性,除过私有属性,这时,我们就可以通过赋值转换,也叫切片技术来使用子类给父类赋值。

class Person
{
protected:
	string _name; // 姓名
	string _sex; //性别
	int _age; // 年龄
};
class Student : public Person
{
public:
	int _No; // 学号
};
void Test()
{
	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;
}

通过上述的代码我们发现:
1.子类对象可以给父类对象赋值,也可以是指针和引用。
2.而父类不能给子类赋值,这点应该很容易想清楚,父类成员数量一定是小于等于子类的,所以不能对子类赋值。
3.父类指针可以通过强转赋值给子类的指针,但是可以会越界,其实指针只是一段地址,即使他们的地址相同,但是指针类型的不同意味着他们能往后看到空间的大小不同,理解这一点是很重要的。

三、继承中的作用域

1.父类和子类都有自己的作用域。
2.如果父类和子类有成员变量名是相同的,也是可以存在的,但是我们直接访问,访问到的是子类中的成员,如果想访问到父类继承下来的成员,必须指定作用域
3.函数变量在继承时,只要函数名相同就会构成隐藏,并不会判断函数的参数是否相同。

我们先通过一段程序来验证一下,在父类和子类中有相同的成员变量,直接访问就是子类的变量。

class Person
{
protected:
	string _name = "小李迪"; // 姓名
	int _num = 111; 	   // 身份证号
};

class Student : public Person
{
public:
	void Print()
	{
		cout << " 姓名:" << _name << endl;
		cout << " 学号:" << _num << endl; // 999
		cout << " 身份证号:" << Person::_num << endl; // 111
	}
protected:
	int _num = 999; // 学号
};

在这里插入图片描述
接下来继续通过程序来验证一下函数名相同时的情况:

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};

class B : public A
{
public:
	void fun(int i)
	{
		cout << "func(int i)->" << i << endl;
	}
};
int main()
{
	B b;
	b.fun(10);
	b.A::fun();
	return 0;
}

在这里插入图片描述
我们先来思考一下问题,这两个函数名相同的函数构成函数重载吗?答案肯定不构成,因为函数重载的前提是在同一作用域,而这里分为父类和子类两个作用域。此处的两个同名函数构成隐藏。

四、派生类的默认成员函数

在前边的类与对象章节学习过,C++类中有六个默认成员函数,但是最常用的是前四个默认成员函数,也是我们要重点学习的。
我们可以认为,子类继承时,对父类的操作都必须把父类成员看成一个整体,会调用父类的相关函数,而子类成员调用子类相关函数,所以可以把自己看做是合成形成的。
在这里插入图片描述
在这里插入图片描述

  1. 默认的构造函数对内置类型不做处理,自定义类型回去调用他们的构造函数,而对于从父类继承的变量,会回去调用他们父类的构造函数。派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
    保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 派生类对象析构清理先调用派生类析构再调基类的析构。
  7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲
    解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加
    virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

(1)派生类的构造函数

class A
{
public:
	A(const string& name)
		//:_name(name)
	{}
	string _name;
};
class B : public A 
{
public:
	B(int age = 18)
		//:A("tmt")
		:_age(age)
	{

	}
	int _age;
};

int main()
{
	B b;
	return 0;
}

在这里插入图片描述
如果父类没有默认构造函数,就必须在子类显式在初始化列表显式调用。

class A
{
public:
	A(const string& name)
		:_name(name)
	{}
	string _name;
};
class B : public A 
{
public:
	B(int age = 18,string name = "tmt")
		:A(name)
		,_age(age)
	{

	}
	int _age;
};

int main()
{
	B b;
	return 0;
}

在这里插入图片描述

在这里插入图片描述
(2)子类的析构函数
子类函数的析构函数,对于自定义类型去调用它的析构函数,对于内置类型不做处理,对于继承的成员回去调用父类的析构函数。但是不用去显式调用父类的析构函数,为了保证父类成员先构造先析构,我们不能去主动析构父类成员,而是在子类的析构函数后自动回去调用父类析构函数。
并且,还有一个细节,就是父子类的析构函数会构成隐藏,虽然我们看到函数名称不同,但是编译器会将他们都处理成constructer,所以构成隐藏,调用时必须加上域作用符。

class Person
{
public:
	//提供了默认构造函数
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	//拷贝构造
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	//赋值重载
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}
	//析构函数
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
//公有继承
class Student : public Person
{
public:
	Student(const char* name ,int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
void Test()
{
	Student s1("张三",18);
	Student s3("王五",17);
}

在这里插入图片描述
(3)子类的拷贝构造函数

class Person
{
public:
	//提供了默认构造函数
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	//拷贝构造
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	//赋值重载
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}
	//析构函数
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
//公有继承
class Student : public Person
{
public:
	Student(const char* name ,int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}
	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
void Test()
{
	Student s1("张三",18);
	Student s2(s1);
}

在这里插入图片描述
(4)子类的复制重载函数

class Person
{
public:
	//提供了默认构造函数
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	//拷贝构造
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	//赋值重载
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}
	//析构函数
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
//公有继承
class Student : public Person
{
public:
	Student(const char* name ,int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}
	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
void Test()
{
	Student s1("张三",18);
	Student s2("李四",19);
	s2 = s1;
}

在这里插入图片描述
在这里插入图片描述

五、继承与友元

友元不能继承,也就是说基类的友元函数内部不能访问子类的私有和保护成员。

//后边会使用,所以先声明
class Student;
class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}
int main()
{
	Student s;
	Person p;
	Display(p, s);
}

在这里插入图片描述

六、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子
类,都只有一个static成员实例 。也就是说,即使子类继承了父类的静态成员,静态成员也只有一份,存在静态区,不属于任何一个对象。

下边通过一段程序来验证一下,当我们通过student类来修改人数时,我们来观察person类的count是否会发生变化:

class Person
{
public:
	//调用一次构造,人数加一
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
 string _seminarCourse; // 研究科目
};
void TestPerson()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	Student::_count = 0;
	cout << " 人数 :" << Person::_count << endl;
}

在这里插入图片描述

七、复杂的菱形继承及菱形虚拟继承

1.单继承与多继承

单继承:
一个子类只有一个直接父类的继承方式,叫做单继承。
在这里插入图片描述

多继承:
友两个或两个以上的直接父类的继承方式,叫做多继承。
在这里插入图片描述
菱形继承:
是多继承的一种特殊情况,继承图形好像一个菱形。
在这里插入图片描述

2.菱形继承的问题

但是菱形继承势必会出现许多的问题,当student和teacher类继承了person类的成员后,assistant类再来继承他们,这是person类中的内容就会被继承两份,这肯定会造成代码冗余和二义性的问题。
在这里插入图片描述
为了解决二义性的问题,我们可以通过指定作用域来进行对重复部分的赋值和查看:

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

但是代码冗余的问题还是没有解决,所以我们要引入虚拟继承。

3.虚拟继承

使用virtual关键字就可以实现虚拟继承,在继承方式前加上virtual关键字,这个继承就成为虚拟继承,但是切记在菱形继承的问题中,应该是在父类为同一个类的两个子类继承时变为虚拟继承。

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

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 ; // 主修课程
};
void Test ()
{
 Assistant a ;
 a._name = "peter";
}

此时再去直接访问就不会报错了,因为只生成一份公有的成员,不论指定哪一个作用域,都是同一份。

4.虚拟继承的模型及原理

我们通过以下的程序通过内存和监视窗口来探究一下,虚继承的模型。

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._a = 1;
	d._a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

在这里插入图片描述
在这里插入图片描述
通过调试,我们可以很清楚的看到,_a这个共有的成员,首先被存储在了最后的位置,然后再去按顺序存储其他的变量,但是在内存界面,有两个看不懂的东西是什么,其实那是地址,地址存储什么呢?我们再来看看:
在这里插入图片描述
在这里插入图片描述
十六进制的14就是十进制的20,而十六进制的C就是十进制的12,那么他们代表什么意思呢?其实是代表了他们与公有成员的距离。


所以虚拟继承的类,有以下的模型:
在这里插入图片描述
有个上边模型的理解,再来理解每个类的大小,就很容易了:
在这里插入图片描述
在这里插入图片描述
由于虚拟继承的类会存储一个地址,该地址存储有关虚基类的相关内容,所以大小会发生变化。


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

八、继承的总结和反思

继承反思

  1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱
    形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设
    计出菱形继承。否则在复杂度及性能上都有问题。
  2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如Java。

继承与组合

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

例如:我们可以说玫瑰花是一种植物,玫瑰花一定继承了植物的相关属性,他们是一种is-a的关系,还有人和动物也是is-a的关系,但是生活中也有很多has-a的关系,例如车和轮胎的关系就是,车里边有轮胎。
总结:

优先使用对象组合,而不是类继承 。
继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。
对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。
实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。

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

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

相关文章

软件 工程

目录 第十章、软件工程1、瀑布模型&#xff08;SDLC&#xff09;2、快速原型模型3、增量模型4、螺旋模型5、Ⅴ模型6、喷泉模型7、构建组装模型&#xff08;CBSD&#xff09;8、统一过程&#xff08;RUP&#xff09;9、敏捷开发方法10、信息系统开发方法11、需求开发12、结构化设…

收藏|必读10本pcb设计书籍推荐

1."High-Speed Digital Design: A Handbook of Black Magic"。 作者是Howard Johnson和Martin Graham。这是一本关于高速数字电路设计的优秀教材&#xff0c;适合那些需要设计高速电路的工程师。 作为比较早出来的信号完整性参考书&#xff0c;对国内的信号完整性研…

H.265/HEVC编码原理及其处理流程的分析

H.265/HEVC编码原理及其处理流程的分析 H.265/HEVC编码的框架图&#xff0c;查了很多资料都没搞明白&#xff0c;各个模块的处理的分析网上有很多&#xff0c;很少有把这个流程串起来的。本文的主要目的是讲清楚H.265/HEVC视频编码的处理流程&#xff0c;不涉及复杂的计算过程。…

第3天学习Docker-Docker部署常见应用(MySQL、Tomcat、Nginx、Redis、Centos)

前提须知&#xff1a; &#xff08;1&#xff09;搜索镜像命令 格式&#xff1a;docker search 镜像名 &#xff08;2&#xff09;设置Docker镜像加速器 详见文章&#xff1a;Docker设置ustc的镜像源&#xff08;镜像加速器&#xff09; 1、部署MySQL 拉取镜像&#xff08;这…

从0到1无比流畅的React入门教程

无比流畅的React入门教程TOC React 是什么 简介 用于构建 Web 和原生交互界面的库React 用组件创建用户界面通俗来讲&#xff1a;是一个将数据渲染为HTML视图的开源JS库 其他信息 Facebook 开发&#xff0c;并且开源 为什么使用React? 原生JS使用DOM-API修改UI代码很繁…

4年外包出来人废了,5次面试全挂....

我的情况 大概介绍一下个人情况&#xff0c;男&#xff0c;毕业于普通二本院校非计算机专业&#xff0c;18年跨专业入行测试&#xff0c;第一份工作在湖南某软件公司&#xff0c;做了接近4年的外包测试工程师&#xff0c;今年年初&#xff0c;感觉自己不能够再这样下去了&…

软件设计模式介绍与入门

目录 1、软件设计模式的起源 2、什么是设计模式&#xff1f; 2.1、设计模式的设计意图 2.2、设计模式的分类准则 3、为什么要学习设计模式 4、如何学习设计模式 5、最后 VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&#x…

Redis--弱口令未授权访问漏洞

Redis--弱口令未授权访问漏洞 一、漏洞简介二、危险等级三、漏洞影响四、入侵事件五、漏洞复现--Redis CrackIT入侵事件5.1、以root启动的redis&#xff0c;可以远程登入到redis console--------A主机5.2、生成公钥5.3、执行: redis-cli flushall 清空redis(非常暴力&#xff0…

《终身成长》笔记四——如何面对失败

目录 经典摘录 秉性 一个英雄具备的所有特质 ​编辑 什么是成功 什么是失败 掌控成功 领导力与固定型思维模式 成长型思维模式领导者的行为 害羞 经典摘录 秉性 天才们&#xff0c;因为自己拥有的优势而得意忘形&#xff0c;不去学习如何努力奋斗以及如何面对挫折。…

分享一个程序员接私活、兼职的平台

分享一个程序员接私活、兼职的平台 1、技术方向满足任一即可2、技术要求3、最后 1、技术方向满足任一即可 Python&#xff1a;熟练掌握Python编程语言&#xff0c;能够使用Python进行数据处理、机器学习和深度学习等相关工作。 MATLAB&#xff1a;熟练掌握MATLAB编程语言&…

MathType7公式编辑器新版详细介绍下载安装

由于CSDN这边不能发相关的教程等&#xff0c;若仅用于学习体验&#xff0c;请移步&#xff0c;有能力请支持正版。 wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;mathtype 免费获取MathType-win-zh.exe安装包 它是一款用于数学公式编辑和排版的软件。MathType可以在Mi…

粪菌移植——一种治疗人体疾病的新型疗法

谷禾健康 粪菌移植是一项近年来备受关注的医疗技术&#xff0c;它涉及将健康捐赠者的粪便物质转移至患有疾病或障碍患者的胃肠道。 简单来说就是选择健康合适的人粪便&#xff0c;通过科学方法提取出有用的微生物&#xff0c;去除有害与无用的部分&#xff0c;然后制成制剂&…

基于布谷鸟优化算法(CS)在微电网优化中的应用研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

ChatGPT应用场景巡航之广告文案

此文为ChatGPT应用场景巡航第二篇&#xff1a;广告文案。 写出成功的文案&#xff0c;需要专业的技术水准&#xff0c;如果加以辅助工具&#xff0c;那会更加如虎添翼&#xff0c;事半功倍&#xff0c;本文会给大家介绍一下广告文案的写作技巧和辅助工具的使用。 01 — 指导原…

如何使用jenkins、ant、selenium、testng搭建自动化测试框架

如果在你的理解中自动化测试就是在eclipse里面讲webdriver的包引入&#xff0c;然后写一些测试脚本&#xff0c;这就是你所说的自动化测试&#xff0c;其实这个还不能算是真正的自动化测试&#xff0c;你见过每次需要运行的时候还需要打开eclipse然后去选择运行文件吗&#xff…

文献阅读:A Lite Distributed Semantic Communication System for Internet of Things

目录 动机&#xff1a;为什么作者想要解决这个问题&#xff1f;贡献&#xff1a;作者在这篇论文中完成了什么工作(创新点)&#xff1f;规划&#xff1a;他们如何完成工作&#xff1f;理由&#xff1a;通过什么实验验证它们的工作结果自己的看法 动机&#xff1a;为什么作者想要…

【接口自动化测试】cookie绕过验证码自动登录?(保持登录状态)

目录 前言 cookie工作原理 cookie绕过登录 【自动化测试工程师学习路线】 1、自动化测试必备Python编程内容​ 2、Web UI 自动化测试基础内容​ 3、Web UI 自动化测试实战内容​ 4、APP UI 自动化测试基础内容​ 5、APP UI 自动化测试实战内容​ 6、API 接口自动化测…

从‘discover.partitions‘=‘true‘分析Hive的TBLPROPERTIES

从’discover.partitions’true’分析Hive的TBLPROPERTIES 前言 Hive3.1.2先建表&#xff1a; show databases ;use db_lzy;show tables ;create external table if not exists test_external_20230502(id int,comment1 string,comment2 string ) stored as parquet ;creat…

高精度示波器keysight是德DSOS054、MSOS054销售回收

安捷伦Keysight DSOS054A MSOS054 500MHZ高清晰度示波器 特征&#xff1a; 带宽&#xff1a;500 MHz&#xff0c;具有平坦的频率响应&#xff0c;可实现高信号保真度 频道&#xff1a;4 最大存储深度&#xff1a;800 Mpts&#xff08;2 通道&#xff09;&#xff0c;400 Mpt…

2023五一杯数学建模竞赛ABC题思路解析+代码+论文

AB题见文末&#xff0c;下面是C C题&#xff1a;“双碳”目标下低碳建筑研究 “双碳”即碳达峰与碳中和的简称&#xff0c;我国力争2030年前实现碳达峰&#xff0c;2060年前实现碳中和。“双碳”战略倡导绿色、环保、低碳的生活方式。我国加快降低碳排放步伐&#xff0c;大力推…