C++核心编程——多态与虚函数

C++核心编程——多态与虚函数

  • 多态的概念
  • 一个典型例子
  • 利用虚函数实现动态多态性
    • 虚函数的作用
    • 虚析构函数
  • 纯虚函数与抽象类

多态的概念

在面向对象方法中一般是这样表述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)
其实,我们已经多次接触过多态性的现象,例如函数的重载、运算符重载都是多态现象。只是那时没有用到多态性这一专门术语而已。
在C++中,多态性表现形式之一是:具有不同功能的函数可以用同一个函数名,这样就可以实现用一个函数名调用不同内容的函数。
系统实现的角度来看,多态性分为两类:静态多态性和动态多态性

  • 静态多态:
      通过函数重载实现的。由函数重载和运算符重载形成的多态性属于静态多态性,在程序编译时系统就能决定要调用的是哪个函数。静态多态性又称编译时的多态性
      静态多态性的函数调用速度快、效率高,但缺乏灵活性在程序运行前就已决定了执行的函数和方法。
  • 动态多态:
      不在编译时确定调用的是哪个函数,而是在程序运行过程中才动态地确定操作所针对的对象,具有很强的灵活性。它又称运行时的多态性。动态多态性是通过虚函数实现的。

一个典型例子

程序设计要求:
  先建立一个Point(点)类,包含数据成员x,y(坐标点)。以它为基类,派生出一个Circle(圆)类,增加数据成员r(半径),再以Circle类为直接基类,派生出一个Cylinder(圆柱体)类,再增加数据成员h(高)。要求编写程序,重载运算符“<<”和”使之能用于输出以上类对象。
(1)声明基类Point类

class  Point{
public:
	//构造函数 
	Point(float tx, float ty):x(tx),y(ty){}
	//功能函数声明 
	float getPX(); 
	float getPY();
	void setPoint(float tx, float ty);

	//重载输出函数 做友元 输出点坐标
	friend ostream & operator<<(ostream &output, Point &pt);
	
protected:
	float x,y;
};


/*************Point 类函数定义 **************/ 
float Point::getPX(){
	return x;
} 
float Point::getPY(){
	return y;
} 
void Point::setPoint(float tx, float ty){
	x = tx;
	y = ty;
}
//重载输出函数 输出点坐标 
ostream & operator<<(ostream &output, Point &pt)
{
	output << "(" << + pt.getPX() << ", " << pt.getPY() << ")";
	return  output; 
}

功能测试:

int main()
{
	Point p1(1,1);
	cout << p1 << endl;
	p1.setPoint(2,3);
	cout << p1 << endl;
	
	return 0;
}

验证基类及函数的功能,核心关注/回顾“<<”运算符重载函数。
(2)声明派生类Circle类

//构造圆类 
class Circle:public Point 
{
public:
	//构造函数 
	Circle(float tx, float ty, float tr):Point(tx,ty),r(tr){} 
	//功能函数声明 
	void setR(float tr);
	float getR();
	float getArea();
	
	//重载输出函数 做友元 输出圆信息
	friend ostream & operator<<(ostream &output, Circle &c);
protected:
	float r;
};


/*************派生类Circle 功能函数定义**************/ 
void Circle::setR(float tr){
	r = tr;
}
float Circle::getR(){
	return r;
}
float Circle::getArea(){
	return 3.14*r*r;
} 
//重载输出函数 输出点圆的信息 
ostream & operator<<(ostream &output, Circle &c)
{
	output << "Center: (" << + c.getPX() << ", " << c.getPY() << ") , r: " << c.getR() << " area= " << c.getArea();
	return  output; 
}

测试程序:

int main()
{
	Point p1(1,1);		//实例化一个点(2,2)
	Circle c1(1, 1, 2);	//实例化圆, 圆心(1,1) r=2
	cout << c1 << endl;
	c1.setPoint(2,2);	//修改点坐标为(2,2);
	c1.setR(4);			//修改圆半径为 4
	
	Point &pRef = c1;	//基类点引用
	//输出圆中点的信息
	cout << "pRef: " << pRef << endl;	//(2, 2) 
	return 0;
}

程序分析:
(1)在Point类中声明了一次运算符“<<”重载函数,在Circle类中又声明了一次运算符“<<”,两次重载的运算符“<<”内容是不同的,在编译时编译系统会根据输出项的类型确定调用哪一个运算符重载函数。
(2)注意main函数中的第8行
   Point &pRef = c1;
定义了Point类的引用pRef,并用派生类Circle对象c1对其初始化。在继承与派生中提到,派生类对象可以替代基类对象向基类对象的引用初始化或赋值。
pRef 不能认为是c的别名,它只是c1 中基类部分的别名,得到了c1的起始地址,与c1中基类部分共享同一段存储单元。所以用“cout <<pRef”输出时调用的不是在Circle中声明的运算符重载函数,而是在Point中声明的运算符重载函数输出的是“点”的信息,而不是“圆”的信息。

输出结果:

Center: (1, 1) , r: 2 area= 12.56
pRef: (2, 2)
在这里插入图片描述

(3)声明Circle的派生类Cylinder

//圆柱体类 
class Cylinder:public  Circle
{
public:
	//圆柱构造函数 
	Cylinder(float tx, float ty, float tr, float th): Circle(tx, ty, tr),h(th){} 
	//功能函数声明 
	void setH(float th);
	float getH();
	float getArea();
	float getVolume(); 
	//重载输出函数 做友元 输出圆柱体信息 
	friend ostream & operator<<(ostream &output, Cylinder &c);
	
protected:
	float h;	
};

/*************派生圆柱体类功能函数实现*************/
void Cylinder::setH(float th){
	h = th;
} 

float Cylinder::getH(){
	return h;
} 
float Cylinder::getArea(){
	return 2*Circle::getArea() + 2*3.14*r*h; 
}
float Cylinder::getVolume()
{
	return (Circle::getArea() * h); 
}
//重载运算符函数 
ostream & operator<<(ostream &output, Cylinder &c)
{
	output << "Center: (" << + c.getPX() << ", " << c.getPY() << ") , r: " << c.getR() << " h: " << c.getH();
	output << "\narea= " << c.getArea() << "\t    volume= " << c.getVolume();
	return  output; 
}

测试函数

int main()
{
	Point p1(1,1);			//实例化一个点(1,1)
	Circle c1(1, 1, 2);		//实例化圆, 圆心(1,1) r=2
	Cylinder cy1(1, 1, 2, 4);	//实例化圆柱,底面圆心(1,1) 半径 2 高度 4
	cout << "cy1 Info: " << cy1<< endl;
	
	cy1.setPoint(2,2);		//修改圆柱底面圆心为(2,2)
	cy1.setR(3);			//修改圆柱底面半径为 3
	cy1.setH(5);			//修改圆柱高为 5
	cout << "new cylinder Info: " << cy1 << endl;
	
	Point &pRef=cy1;		//定义基类点的引用   输出圆柱中Point类的信息
	cout << "pRef as point: " << pRef << endl;
	Circle &cRef=cy1;		//定义派生类 圆的引用 输出圆柱中Circle类的信息
	cout << "cRef as point: " << cRef;	
	
	return 0;
}

程序分析:

  • cy1.getArea()调用的是Cylinder类的getArea函数而不是Circle类的getArea函数。请注意这两个area函数不是重载,而是分别在基类和派生类中,属于同名覆盖
  • 注意测试程序中的点引用pRe是Point类型的引用,用cy1初始化,但pRef只是cy1基类Point部分的别名,以此在输出时也只输出点的信息。同理cRef是Circle类的引用,即使用cy1初始化,但在输出时只输出圆的信息。

输出结果:

cy1 Info: Center: (1, 1) , r: 2 h: 4
area= 75.36 volume= 50.24
new cylinder Info: Center: (2, 2) , r: 3 h: 5
area= 150.72 volume= 141.3
pRef as point: (2, 2)
cRef as point: Center: (2, 2) , r: 3 area= 28.26
在这里插入图片描述

利用虚函数实现动态多态性

虚函数的作用

在上述程序中,Circle和Cylinder类中都有getArea这个函数,这两个函数不仅名字相同,而且参数个数相同,但功能不同,函数体是不同的。这是合法的,因为它们不在同一个类中。编译系统按照同名覆盖的原则决定调用的对象。
  通过指针调用,同一个语句pt->display();,可以调用不同派生层次中的 display 函数,只须在调用前临时给指针变量 pt 赋予不同的值(使之指向不同的类对象)即可。
典例: 声明两个类Student和Graduate有同样的方法display输出信息,通过指针访问不同类,输出不同信息。
这个例子开始时没有使用虚函数,然后再讨论使用虚函数的情况。

#include <iostream>
#include <string>
using namespace std;

//构造 Student类 
class  Student{
public:
	Student(int n, string nam, float sco):num(n), name(nam), score(sco){}
	
	void display(){
		cout << "num: " << num << "\nname: " << name << "\nscore: " << score << endl;
	}
	
protected:
	int num;
	string name;
	float score;
	
};

//构造Graduate类 
class Graduate:public Student 
{
public:
	Graduate(int n, string nam, float sco, float w):Student(n, nam, sco),wage(w){}

	void display(){
		Student::display();
		cout << "wage: " << wage << endl;
	}
	
protected:
	float wage;
};


int main()
{
	Student stu1(1001,"zhangsan",80);
	Graduate grad1(1002,"lisi",95,600);
	
	Student *pt = &stu1;
	pt->display();
	
	pt = &grad1;
	pt->display();
	
	return 0;
}

程序运行结果:

num: 1001
name: zhangsan
score: 80
num: 1002
name: lisi
score: 95

程序分析:
  pt为Student类型的指针,即使指向grad1,也只能访问grad1基类数据,无法访问Graduate成员数据。
用虚函数就能顺利地解决这个问题。下面对程序作一点修改,在 Student 类中声明display函数时,在最左面加一个关键字virtual,即

virtual void display();

这样就把Student类的display 函数声明为虚函数。程序其他部分都不改动,再编译和运行就行正常输出Graduate类的数据。
用同一种调用形式pt->display(),而且pt是同一个基类指针,可以调用同一类族中不同类的虚函数。这就是多态性,对同一消息,不同对象有不同的响应方式。

虚函数的使用方法:

  • 在基类中用virtual声明成员函数为虚函数。在类外定义虚函数时,不必再加virtual
  • 在派生类中重新定义此函数,函数名、函数类型、函数参数个数和类型必须与基类的虚函数相同,根据派生类的需要重新定义函数体。
  • 定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。
  • 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。

虚析构函数

#include <iostream>
using namespace std;

class Point
{
public:
	Point(){}
	//virtual ~Point()
	~Point()
	{
		cout << "基类析构函数调用" << endl; 
	} 
};

class Circle:public Point
{
public:
	Circle(){}
	~Circle(){
		cout << "派生类虚构函数调用" << endl;
	}
};

int main()
{
	Point *pt = new Circle;
	delete pt;  
	return 0;
 } 

程序分析:
  pt是指向基类的指针变量,指向new开辟的动态存储空间,因此程序运行只执行基类的析构函数,如果希望能执行派生类 circle 的析构函数,需要将基类的析构函数声明为虚析构函数,如:

virtual ~Point();

最好把基类的析构函数声明为虚函数,这将使所有派生类的析构函数自动成为虚函数。

纯虚函数与抽象类

在第一个程序:一个典型的例子中,基类Point中没有求面积的getArea函数因为“点”是没有面积的,也就是说基类本身不需要这个函数。所以在Point类中没有定义getArea函数。但是,在其直接派生类Circle 和间接派生类Cylinder 中都需要有getArea 函数,而且这两个函数的功能不同,一个是求圆面积,另一个是求圆柱体表面积。我们自然会想到,在这种情况下应当将getArea声明为虚函数。可以在基类Point中加一个getArea函数并声明为虑函数:

virtual float getArea() const{
	return 0;
}

其返回值为0,表示“点”是没有面积的。其实,在基类中并不使用这个函数。为了简化,可以不要写出这种无意义的函数体。只给出函数的原型,并在后面加上“=0”,如

virtual float getArea() const = 0;

这就将getArea声明为一个纯虚函数。纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是:
  virtual 函数类型 函数名 (参数表列) = 0;
注意:

  • 纯虚函数没有函数体
  • 最后面的“=0”并不表示函数返回值为 0,它只起形式上的作用,告诉编译系统“这是纯虚函数”
  • 这是一个声明语句,最后应有分号
  • 纯虚函数不具备函数的功能,不能被调用,如要用,需要在派生类中重新定义

纯虚函数的作用:
  在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。如果在基类中没有保留函数名字,则无法实现多态性。

抽象类

如果声明了一个类,一般可以用它定义对象。但是在面向对象程序设计中,往往有一些类,它们不用来生成对象,目的是用它作为基类去建立派生类。

抽象类的派生类需要对基类中的纯虚函数进行定义,这个派生类就是可以用来定义对象的具体类

虽然抽象类不能定义对象,但是可以定义指向抽象类数据的指针变量。当派生类成为具体类之后,就可以用这种指针指向派生类对象,然后通过该指针调用虚函数实现多态性的操作。

应用实例

  在一个典型例子中介绍了以Point 为基类的“点一圆一圆柱体”类的层次结构。现在程序中使用虚函数和抽象基类,并且类的层次结构的顶层是抽象基类 Shape(形状) Point(点)Circle(圆),Cylinder(圆柱体)都是 Shape 类的直接派生类和间接派生类。
  • (1)声明抽象基类Shape

    //声明抽象基类Shape
    class Shape
    {
    public:	
    	virtual float getArea() const {return 0.0;}		//虚函数 求面积
    	virtual float getVolume() const {return 0.0;}	//虚函数
    	virtual void shapeName() const = 0;				//纯虚函数
    };
    
  • (2)声明Point类
    函数具体自定义实现参考上述一个典型例子的程序,该程序主要在原程序上进行对虚函数的重新定义

    //声明Point类
    class  Point:public Shape
    {
    public:
    	//函数具体自定义实现参考上述一个典型例子程序
    	
    	//构造函数 
    	Point(float tx, float ty):x(tx),y(ty){}
    	
    	//功能函数声明 
    	void setPoint(float tx, float ty);
    	float getPX () const {return x;}; 
    	float getPY () const {return y;}; 
    	
    	//对虚函数进行重新定义
    	virtual void shapeName() const {cout <<"Point: ";}
    	
    	//重载输出函数 做友元 输出点坐标
    	friend ostream & operator<<(ostream &output, const Point &pt);
    	
    protected:
    	float x,y;
    };
    
    /*************Point 类函数定义 **************/
    void Point::setPoint(float tx, float ty){
    	x = tx;
    	y = ty;
    }
    //重载输出函数 输出点坐标 
    ostream & operator<<(ostream &output, const Point &pt)
    {
    	output << "(" << + pt.x<< ", " << pt.y << ")";
    	return  output; 
    }
    

    Point从Shape 继承了3个成员函数,由于“点”是没有面积和体积的因此不必重新定义getArea和getVolume,但Point类仍然从Shape类继承了这两个函数以便其派生类继承它们。shapeName函数在Shape类中是纯虚函数在Point类中要进行定义。Point类还有自己的成员函数(setPoint, getX,getY)和数据成员(x和y)。

  • (3)声明Circle类
    函数具体自定义实现参考上述一个典型例子的程序,该程序主要在原程序上进行对getArea和shapeName虚函数的重新定义

    //构造圆类 
    class Circle:public Point 
    {
    public:
    	//构造函数 
    	Circle(float tx, float ty, float tr):Point(tx,ty),r(tr){} 
    	
    	//功能函数声明 
    	void setR(float tr);	//设定半径
    	float getR() const;				//获取半径的值
    	
    	//对虚函数进行再定义
    	virtual float getArea() const;
    	virtual void shapeName() const  {cout <<"Circle:";}
    	
    	//重载输出函数 做友元 输出圆信息
    	friend ostream & operator<<(ostream &output, const Circle &c);
    	
    protected:
    	float r;
    };
    
    /*************派生类Circle 功能函数定义**************/ 
    void Circle::setR(float tr){
    	r = tr;
    }
    float Circle::getR() const{
    	return r;
    }
    float Circle::getArea() const{
    	return 3.14*r*r;
    } 
    //重载输出函数 输出点圆的信息 
    ostream & operator<<(ostream &output, const Circle &c)
    {
    	output << "(" << + c.x << ", " << c.y<< ") , r: " << c.r;
    	return  output; 
    }
    
  • (4)同理,声明Cylinder类

    //圆柱体类 
    class Cylinder:public  Circle
    {
    public:
    	//圆柱构造函数 
    	Cylinder(float tx, float ty, float tr, float th): Circle(tx, ty, tr),h(th){} 
    	
    	//功能函数声明 
    	void setH(float th);
    	float getH();
    	
    	//虚函数重定义
    	virtual float getArea() const;		//重载虚函数
    	virtual float getVolume() const; 
    	virtual void shapeName() const  {cout <<"Cylinder:";}
    	
    	//重载输出函数 做友元 输出圆柱体信息 
    	friend ostream & operator<<(ostream &output, const Cylinder &c);
    
    protected:
    	float h;	
    };
    
    
    /*************派生圆柱体类功能函数实现*************/
    void Cylinder::setH(float th){
    	h = th;
    } 
    
    float Cylinder::getH(){
    	return h;
    } 
    float Cylinder::getArea() const{
    	return 2*Circle::getArea() + 2*3.14*r*h; 
    }
    float Cylinder::getVolume() const{
    	return (Circle::getArea() * h); 
    }
    //重载运算符函数 
    ostream & operator<<(ostream &output, const Cylinder &c)
    {
    	output << "(" << + c.x << ", " << c.y << ") , r: " << c.r << " h: " << c.h;
    	return  output; 
    }
    
  • (5)main函数

    int main() 
    {
    	Point p1(3.2,4.5);				//实例化一个点(3.2,4.5)
    	Circle c1(2.4, 1.2, 5.6);		//实例化圆, 圆心(2.4, 1.2) r = 5.6
    	Cylinder cy1(3.5, 6.4, 5.2, 10.5);	//实例化圆柱,底面圆心(3.5, 6.4) 半径 5.2 高度 10.5	
    	
    	p1.shapeName();					//用对象名建立静态关联
    	cout << p1 << endl;				//输出点的信息
    	c1.shapeName();	
    	cout << c1 << endl;
    	cy1.shapeName();
    	cout << cy1 << endl << endl;
    
    	Shape *pt;				//定义基类指针
    	pt = &p1;				//使指针指向point对象
    	pt->shapeName();		//用指针建立动态关联
    	cout << "x=" << p1.getPX() << ", y=" << p1.getPY() << endl;
    	cout << "area=" << pt->getArea() << "\nvolume=" << pt->getVolume() <<endl<< endl << endl;
    	
    	pt = &c1;				//使指针指向point对象
    	pt->shapeName();		//用指针建立动态关联
    	cout << "x=" << c1.getPX() << ", y=" << c1.getPY() << endl;
    	cout << "area=" << pt->getArea() << "\nvolume=" << pt->getVolume() <<endl<< endl << endl;
    	
    	pt = &cy1;				//使指针指向point对象
    	pt->shapeName();		//用指针建立动态关联
    	cout << "x=" << cy1.getPX() << ", y=" << cy1.getPY() << endl;
    	cout << "area=" << pt->getArea() << "\nvolume=" << pt->getVolume() <<endl<< endl << endl;
    
    	return 0;
    }
    

程序分析:
  首先先分别定义了Point类对象跑,Circle类对象从和Cylinder 类对象cy1。然后分别通过对象名p1,c1和cy1调用了shapeName 函数,这属于静态关联。
  后续定义一个指向基类Shape对象的指针变量pt,使它先后指向3个派生类对象point,Circle和cylinder,然后通过指针调用各函数,如pt->shapeName(),pt ->getArea()等。这时是通过动态关联分别确定应该调用哪个函数,分别输出不同类对象的信息。
  
结果输出:

Point: (3.2, 4.5)
Circle:(2.4, 1.2) , r: 5.6
Cylinder:(3.5, 6.4) , r: 5.2 h: 10.5

Point: x=3.2, y=4.5
area=0
volume=0

Circle:x=2.4, y=1.2
area=98.4704
volume=0

Cylinder:x=3.5, y=6.4
area=512.699
volume=891.509

在这里插入图片描述

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

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

相关文章

渲染农场对工业产品渲染带来的意义与优势?

随着科技的进步&#xff0c;利用精细渲染图来呈现和推广工业设计的创新已成为行业标准。这些图像在产品研发、设计评审和营销阶段起着关键作用&#xff0c;同时对产品最终的成功也产生深远影响。然而&#xff0c;由于产品设计日渐复杂&#xff0c;制作渲染图的任务变得极具挑战…

VisualSVN Server的安装全过程

目录 背景: 安装过程&#xff1a; 步骤1&#xff1a; 步骤2&#xff1a; 步骤3&#xff1a; 步骤4&#xff1a; 步骤5&#xff1a; 安装出现的bug&#xff1a; 问题: 解决办法: 总结: 背景: VisualSVN Server 是一款免费的 SVN (Subversion) 服务器软件&#xff0c…

30、Linux安全配置

文章目录 一、Linux安全配置简介二、Linux安全配置2.1 网络配置2.2 防火墙配置2.2.1 确定防火墙区域配置 2.3 日志和审核2.4 访问、认证和授权2.4.1 SSH配置2.4.2 PAM模块配置 一、Linux安全配置简介 Linux种类较多&#xff0c;常用的有Redhat、Ubantu、Centos等。这里以Cento…

数据结构第六课 -----排序

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Java开发环境详解(安装,工作流程,程序结构与终端运行)

参考书籍&#xff1a; 《明解Java》 《Java轻松学》 《Head First Java》 《Java核心技术卷I》 《Java核心技术卷II》 参考视频&#xff1a; Java零基础学习视频通俗易懂 Java入门基础视频教程&#xff0c;java零基础自学就选黑马程序员Java入门教程 参考网站&#xff1a; Kuan…

DNSLog漏洞探测(一)之DNSLog介绍

前言 DNSLog是一种基于DNS协议的信息收集技术,它可以用于网络安全领域的渗透测试、漏洞挖掘等方面。DNSLog的原理是利用DNS协议的特性,将需要收集的信息编码成DNS查询请求,然后将请求发送到DNS服务器,最后通过DNS服务器的响应来获取信息。DNSLog的实现方式有很多种,其中最常见…

.Net中的集合

所有的集合都是继承自IEnumerable。集合总体可以分为以下几类&#xff1a;关联/非关联型集合&#xff0c;顺序/随机访问集合&#xff0c;顺序/无序集合&#xff0c;泛型/非泛型集合&#xff0c;线程集合。 各集合类底层接口关系图 泛型与非泛型集合类的分析 泛型集合是类型安…

智能优化算法应用:基于入侵杂草算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于入侵杂草算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于入侵杂草算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.入侵杂草算法4.实验参数设定5.算法结果6.…

Qt之自定义QToolTip,去掉显示动画和隐藏延时

一.效果 先来看看Qt原生QToolTip的缺点: 1.当提示内容无变化时,弹窗无法移动。只能先传个空字符串强制弹窗隐藏,然后在新位置再传个字符串。 If the text is the same as the currently shown tooltip, the tip will not move. You can force moving by first hiding the t…

MIT18.06线性代数 笔记3

文章目录 对称矩阵及正定性复数矩阵和快速傅里叶变换正定矩阵和最小值相似矩阵和若尔当形奇异值分解线性变换及对应矩阵基变换和图像压缩单元检测3复习左右逆和伪逆期末复习 对称矩阵及正定性 特征值是实数特征向量垂直>标准正交 谱定理&#xff0c;主轴定理 为什么对称矩…

网上很火的记事软件有哪些?可以分类记事的工具选哪个

日常记事在生活及工作方面都是非常重要&#xff0c;选择好用的记事软件可以督促各项任务的按时完成&#xff0c;。随着科技的发展&#xff0c;越来越多的记事软件涌现出来&#xff0c;让人眼花缭乱。那么&#xff0c;网上很火的记事软件有哪些&#xff1f;可以分类记事的工具应…

Java服务占用过高CPU排除思路

一、背景说明 如果线上通过 java -jar xxx.jar 的方式启动的Java服务占用过高的CPU&#xff0c;我们通过top命令是可以查看到的。 那么问题来了&#xff0c;如果通过top命令查看到是因为java服务引起的占用过高的CPU时间&#xff0c;该如何进行排查呢&#xff1f; 二、排查思路…

【论文阅读】Reachability and distance queries via 2-hop labels

Cohen E, Halperin E, Kaplan H, et al. Reachability and distance queries via 2-hop labels[J]. SIAM Journal on Computing, 2003, 32(5): 1338-1355. Abstract 图中的可达性和距离查询是许多应用的基础&#xff0c;从地理导航系统到互联网路由。其中一些应用程序涉及到巨…

【模拟】LeetCode-48. 旋转图像

旋转图像。 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6]…

Python unittest单元测试框架 —— 断言assert !

assertEqual(a,b&#xff0c;[msg]):断言a和b是否相等&#xff0c;相等则测试用例通过。 assertNotEqual(a,b&#xff0c;[msg]):断言a和b是否相等&#xff0c;不相等则测试用例通过。 assertTrue(x&#xff0c;[msg])&#xff1a;断言x是否True&#xff0c;是True则测试用例…

现代雷达车载应用——第2章 汽车雷达系统原理 2.3节

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.3 信号模型 雷达的发射机通常发出精心设计和定义明确的信号。然而&#xff0c;接收到的返回信号是多个分量的叠加&#xff0c;包括目标的反射、杂波…

Fiddler中AutoResponder的简单使用

AutoResponder&#xff0c;自动回复器&#xff0c;用于将 HTTP 请求重定向为指定的返回类型。 这个功能有点像是一个代理转发器&#xff0c;可以将某一请求的响应结果替换成指定的资源&#xff0c;可以是某个页面也可以是某个本地文件 1.使用 打开“Fiddler”&#xff0c;点击…

经典策略筛选-20231212

策略1&#xff1a; 龙头战法只做最强&#xff1a;国企改革 ----四川金顶 1、十日交易内出现 涨停或 &#xff08;涨幅大于7个点且量比大于3&#xff09; 2、JDK MACD RSI OBV BBI LWR MTM 六指标共振 3、均线多头 4、 筹码峰 &#xff08;锁仓&#xff09; 5、现价>…

用友 U8 Cloud upload.jsp 文件上传漏洞复现

0x01 产品简介 用友U8 Cloud 提供企业级云ERP整体解决方案,全面支持多组织业务协同,实现企业互联网资源连接。 U8 Cloud 亦是亚太地区成长型企业最广泛采用的云解决方案。 0x02 漏洞概述 用友U8 Cloud upload.jsp接口存在任意文件上传漏洞,攻击者可通过该漏洞上传木马,远…

网络基础(八):路由器的基本原理及配置

目录 1、路由概述 2、路由器 2.1路由器的工作原理 2.2路由器的转发原理 3、路由表 3.1路由表的概述 3.2路由表的形成 4、静态路由配置过程&#xff08;使用eNSP软件配置&#xff09; 4.1两个静态路由器配置过程 4.2三个静态路由器配置过程 5、默认路由配置过程 5.…