【C++之多态的知识】

C++学习笔记---018

  • C++之多态的知识
    • 1、C++多态的简单介绍
      • 1.1、多态的分类
      • 1.2、多态的构成条件
    • 2、虚函数
      • 2.1、虚函数的重写(覆盖)
    • 3、虚函数重写的两个例外
      • 3.1、协变:(基类与派生类虚函数返回值类型不同)
      • 3.2、析构函数的重写(基类与派生类析构函数的名字不同)
    • 4、两个关键字:override 和 final
      • 4.1、final
      • 4.2、override
    • 5、多态原理层的理解
      • 5.1、虚表指针
      • 5.2、虚函数表
      • 5.3、多态的切片原理
    • 6、单继承中的虚函数表
      • 6.1、单继承
      • 6.2、多继承
      • 6.3、单继承的虚函数,放在哪里呢?
    • 7、抽象类
      • 7.1、概念
      • 7.2、接口继承和实现继承
    • 8、总结

C++之多态的知识

前言:
前面篇章学习了C++对于继承的认识,接下来继续学习,C++的另一个特性多态等知识。
/知识点汇总/

封装:
1.数据和方法放在一起,把想给访问的成员,定义为公有,不想被访问的定义为私有或保护。
2.一个类型放在另一个类型里面,通过typedef成员函数调整,封装另一个全新的类型。
继承:
继承机制是面向对象程序设计使得代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产合新的类,称为派生类。
继承呈现了对象面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前接触的复用是函数复用,继承是属于类的复用。

1、C++多态的简单介绍

C++中的多态(Polymorphism)是面向对象编程的一个重要特性,它指的是不同的对象对同一消息做出不同的响应。简单来说,多态允许使用统一的接口来访问不同的对象,而这些对象在响应接口调用时可能会展现出不同的行为。
在C++中,多态性主要通过继承和虚函数来实现。
当一个类从另一个类继承时,它可以重写(override)基类中的虚函数。
当通过基类指针或引用调用虚函数时,实际调用的函数版本将取决于指针或引用所指向的对象的实际类型,这就是所谓的动态绑定或运行时多态。
不同的对象,完成同样的任务,展现不同的形态,结果会不一样。

1.1、多态的分类

静态多态:也称为静态绑定或前期绑定(早绑定)。它主要通过函数重载和运算符重载来实现。在编译期间,编译器根据函数实参的类型(可能会进行隐式类型转换)推断出要调用哪个函数。这种多态在编译时就确定了函数的调用版本,因此效率较高。
动态多态:也称为动态绑定或后期绑定(晚绑定)。它主要通过虚函数来实现。在程序执行期间(非编译期),根据引用或指针所指向的对象的实际类型来确定调用哪个版本的函数。这种多态提供了更高的灵活性和可扩展性,但由于需要在运行时查找虚函数表来确定调用的具体函数,因此可能会带来一定的性能损失。

1.2、多态的构成条件

那么在继承中要构成多态还有两个条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

2、虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数。

class Person
{public:virtual void BuyTicket(){ cout << "买票-全价" << endl;}
};

2.1、虚函数的重写(覆盖)

派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){ cout << "买票-全价" << endl;}
};
class Student : public Person
{
public:virtual void BuyTicket(){ cout << "买票-半价" << endl;}//注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因//为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议//这样使用//void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person ps;Student st;Func(ps);Func(st);//Func(&ps);//Func(&st);return 0;
}

小结

C++的多态需要满足两个条件:
1.父子类完成虚函数重写
(虚函数的重写要求三相同:1.返回值 2.参数 3.函数名)
虚函数是子类虚函数重写父类的虚函数。
2.父类的指针或引用去调用虚函数
且调用时,需要父类的指针或引用调用这个虚函数。
注意:这里的virtual void BuyTicket(),虚函数与虚继承的virtual无关
一个关键字virtual两个地方用,且virtual只能修饰成员函数,不能修饰全局函数
指向谁就调用谁

3、虚函数重写的两个例外

3.1、协变:(基类与派生类虚函数返回值类型不同)

派生类重写基类虚函数时,与基类虚函数返回值类型不同。
即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

#include <iostream>
using namespace std;
class A {};
class B : public A {};
class Person
{
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}
};
class Student : public Person
{
public:
//返回值不同,要求父子类的指针或引用 -- 协变virtual B* BuyTicket(){cout << "买票-半价" << endl;return nullptr;}
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}

3.2、析构函数的重写(基类与派生类析构函数的名字不同)

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,
都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,
看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理, 编译后析构函数的名称统一处理成destructor。

补充:3.派生类重写虚函数可以不加virtual,因为可理解为已经继承父类的了。(不过建议都写上)

#include <iostream>
using namespace std;
class A {};
class B : public A {};
class Person
{
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}//~Person() -- 需要加上virtual,不然容易混乱的调用,因为底层析构函数被统一成destructorvirtual ~Person(){ cout << "~Person()" << endl;}
};
class Student : public Person
{
public:virtual B* BuyTicket(){cout << "买票-半价" << endl;return nullptr;}// ~Student()virtual ~Student(){ cout << "~Student()" << endl;}
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{//Person ps;//Student st;Person* p1 = new Person;Person* p2 = new Student;//p1->destructor + operator delete(p1)// 期望:指向父类的调用父类析构//p2->destructor + operator delete(p2)// 期望:指向子类的调用子类析构//结论:建议在派生类场景使用析构函数时,加上virtual关键字delete p1;delete p2;return 0;
}

4、两个关键字:override 和 final

final:修饰虚函数,表示该虚函数不能再被重写
override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

4.1、final

#include <iostream>
using namespace std;//class Car
//{
//public:
//	virtual void Drive() final {}//限制虚函数不能被继承
//};
//class Benz :public Car
//{
//public:
//	virtual void Drive()//继承失败
//	{ 
//		cout << "Benz-舒适" << endl;
//	}
//};//限制一个类不能被继承
//两个方法:
//1.private
//2.final修饰整个类//class Car
class Car final//C++11,被·final修饰的类叫最终类,无法被继承
{
public:
private://C++98 --- 写成私有//子类的构造无法生成和实现,导致子类对象无法实例化Car(){}
};class Benz : public Car//被final修饰无法继承
{
public:};
int main()
{//方法1://派生类的构造必调用父类的构造//因此满足限制一个类不被继承//Benz b;//方法2:用final修饰整个类return 0;
}

4.2、override

用于派生类中的。检查其虚函数是否完成重写

#include <iostream>
using namespace std;class Car {
public:virtual void Drive() {}
};
class Benz :public Car
{
public://virtual void Drive(int) override//error --> 未完成重写virtual void Drive() override{ cout << "Benz-舒适" << endl;}
};
int main()
{Benz b;return 0;
}

小结

三个概念的对比:重载、重写(覆盖)、隐藏(重定义)
1.重载:
两个函数在同一个作用域,函数名相同与参数类型不同
2.重写(覆盖):
两个函数分别在基类和派生类的作用域;
函数名、参数类型、返回值都必须相同(协变例外);
两个函数必须是虚函数
3.隐藏(重定义):
两个函数分别在基类和派生类的作用域;
函数名相同,变量名不同
两个基类和派生类的同名函数不构成重写就是重定义
注意:隐藏与重写属于包含关系,具有重叠性

5、多态原理层的理解

5.1、虚表指针

#include <iostream>
using namespace std;
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;char _ch = 'x';
};int main()
{cout << sizeof(Base) << endl;//12//按照之前的内存对齐理解应该是8,为啥是12呢?//因为只要存在虚函数在底层就有一个虚函数表指针 --》简称虚表指针//其作用就是实现多态return 0;
}

5.2、虚函数表

虚函数表,存放所有的虚函数地址 按照声明的顺序存放

#include <iostream>
using namespace std;
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}virtual void Func2(){cout << "Func2()" << endl;}void Func3(){cout << "Func3()" << endl;}
private:int _b = 1;
};
//查看监视创窗口的内存窗口
int main()
{cout << sizeof(Base) << endl;Base b;return 0;
}

5.3、多态的切片原理

如何实现指向谁调用谁? – 切片

#include <iostream>
using namespace std;class Person {
public://不满足多态时,在编译时根据调用的函数类型就找到了BuyTicket地址//void BuyTicket()//满足多态时:运行时去指向对象虚函数表中找到BuyTicket的地址virtual void BuyTicket(){ cout << "买票-全价" << endl;}
private:int _i = 1;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl;}
private:int _j = 2;
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person Mike;Func(Mike);Student Johnson;//这里执行切片,把子类当中的父类部分切出来Func(Johnson);//满足多态条件://那么这里调用生成的指令,就会去指向对象的虚表中找对应的虚函数进行调用---》指向谁调用谁(运行时去序表找对应的虚函数地址调用)//指向父类调用父类的虚函数//指向子类调用子类的虚函数//每一个对象都有自己的虚表//同类型的对象序公用一个虚表,不同类型的对象有各自的虚表//Person Mike;//Func(&Mike);//Person p1;//Func(&p1);//Student Johnson;//Func(&Johnson);return 0;
}

6、单继承中的虚函数表

6.1、单继承

#include <iostream>
using namespace std;class Base {
public:virtual void func1(){ cout << "Base::func1" << endl;}virtual void func2() { cout << "Base::func2" << endl;}
private:int a = 1;
};
class Derive :public Base {
public:virtual void func1(){ cout << "Derive::func1" << endl;}virtual void func3() { cout << "Derive::func3" << endl;}virtual void func4() { cout << "Derive::func4" << endl;}
private:int b = 2;
};//实现打印虚表
//虚函数表的本质是一个函数指针数组
typedef void(*VFPTR)();//virtual function table
//void PrintVFT(VFPTR vft[])
void PrintVFT(VFPTR* vft)
{for (size_t i = 0; i < 4; i++){printf(" %p -> ", vft[i]);VFPTR pf = vft[i];//函数指针的调用(*pf)();}
}
int main()
{Base b;Derive d;//继承下来了func1和func2,func1重写了,func2没重写//func3和func4//函数指针//void (*p)();//VFPTR p1;//函数指针数组//void (*p[10])();//VFPTR p2[10];//任意自由转VFPTR* ptr = (VFPTR*)(*((int*) & d));PrintVFT(ptr);return 0;
}

6.2、多继承

#include <iostream>
using namespace std;
class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1;
};
class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2;
};
class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{cout << " 虚表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d个虚函数地址 :0X%x->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Derive d;cout << sizeof(d) << endl;//20-->Base1 8 + Base2 8 + 4VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);PrintVTable(vTableb1);VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));PrintVTable(vTableb2);return 0;
}

6.3、单继承的虚函数,放在哪里呢?

答:虚表是运行时就加载到常量区的,而初始化是构造函数的初始化列表的时候完成的

#include <iostream>
using namespace std;
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};
class Derive :public Base {
public:Derive(){}virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }virtual void func4() { cout << "Derive::func4" << endl; }
private:int b = 0;
};typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数cout << " 虚表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d个虚函数地址 :0X%x->", i, vTable[i]);VFPTR f = vTable[i];f(); //多继承中的虚函数表}cout << endl;
}
int main()
{Base b;Derive d;VFPTR* vTableb = (VFPTR*)(*(int*)&b);PrintVTable(vTableb);VFPTR* vTabled = (VFPTR*)(*(int*)&d);PrintVTable(vTabled);return 0;
}

7、抽象类

7.1、概念

在虚函数的后面写上 = 0 ,则这个函数为纯虚函数。
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。
派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。
纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

#include <iostream>
using namespace std;//包含纯虚函数的类称为抽象类
//抽象类不能直接实例化对象;
//并且间接强制派生类重写虚函数
//override是已经重写的,用于检查是否重写,目的是检查语法问题
class Car
{
public://纯虚函数virtual void Drive() = 0;
};
class Benz :public Car
{
public://继承并完成重写virtual void Drive(){cout << "Benz-舒适" << endl;}
};
class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};
int main()
{//Car c;抽象类不能直接实例化对象//抽象类强制了派生类去执行重写 -->重写的是实现Benz b1;BMW b2;//可以利用指针实现实例化Car* ptr = &b1;ptr->Drive();Car* ptr = &b2;ptr->Drive();return 0;
}

7.2、接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。
所以如果不实现多态,不要把函数定义成虚函数。

#include <iostream>
using namespace std;
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
//多态调用:指向父类调用父类的虚函数,指向子类调用子类的虚函数
//void Func(Person p)//不是父类的指针或引用,那么就不满足多态,则属于普通调用,就编译时找函数的地址
//多态中的虚表:虚函数表,存虚函数的地址,目的是为了实现多态
//虚继承中的虚表:存的是当前位置距离虚基类部分的位置的偏移量,本质是解决菱形继承中的数据二义性。
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}

看出满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的。 不满足多态的函数调用时编译时确认好的。

虚函数存放在哪里?

答:虚函数跟普通函数一样都存在内存的代码段,不是存放在虚表中的,虚表存放的是虚函数的起始地址。

虚表存放在哪个区域?

答:也是代码段

#include <iostream>
using namespace std;class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};int main()
{int i = 0;static int j = 1;int* p1 = new int;const char* p2 = "xxxx";printf("栈:%p\n", &i);printf("静态区:%p\n", &j);printf("堆:%p\n", p1);printf("常量区:%p\n", p2);Person p;Student s;Person* p3 = &p;Student* p4 = &s;printf("Person虚表地址p3:%p\n", *(int*)p3);printf("Student虚表地址p4:%p\n", *(int*)p4);//说明虚表存放在常量区/代码段的return 0;
}

8、总结

C++多态的优点包括:

灵活性:多态允许使用基类指针或引用调用派生类的方法,从而提供了更高的灵活性和可扩展性。
可维护性:多态性使得代码更易于维护和修改,因为新增一个派生类不需要修改已有的代码,只需要添加新的派生类即可。
可读性:多态性可以使代码更简洁和易读,因为派生类的特定实现只出现在派生类的定义中,而不是在整个代码中分散出现。

C++多态也存在一些缺点:

性能损失:运行时的动态绑定会导致一定的性能损失,因为需要在运行时查找虚函数表来确定调用的具体函数。
调试困难:由于动态多态涉及到运行时类型的确定和虚函数表的查找,因此可能会增加调试的难度。

总之,C++中的多态是一种强大的特性,它允许程序员以统一的方式处理不同的对象类型,并提供了更高的灵活性和可扩展性。然而,在使用多态时需要注意其可能带来的性能损失和调试困难等问题。

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

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

相关文章

redis运维篇下篇

最近在学redis&#xff0c;由于笔者是学运维的&#xff0c;所以推荐学习运维的小伙伴参考&#xff0c;希望对大家有帮助&#xff01; redis运维篇上篇:http://t.csdnimg.cn/MfPud 附加redis多用户管理:http://t.csdnimg.cn/DY3yx 目录 十.redis慢日志 十一.redis的key的有效…

dvwa kali SQL注入

high: 1.txt的来源 1.txt的内容 手动添加&#xff1a; id1&SubmitSubmit 执行&#xff1a; sqlmap -r /root/1.txt -p id --second-url "http://192.168.159.128:20000/vulnerabilities/sqli_blind/" --batch medium&#xff1a; 换链接&#xff0c;换cook…

HDFS存取策略联系

书上关于这部分分了三个点&#xff1a; 1.数据存放 2.数据读取 3.数据复制 但数据存放和数据复制都是数据写操作过程中的&#xff0c;“存放”体现一种思想&#xff0c;“复制”体现过程&#xff0c;整个数据写操作过程如下&#xff1a; 1.分块&#xff1a;当客户端写入一个…

【JS篇之】异常

前言&#xff1a;在代码编写过程中&#xff0c;最常遇到的就是程序异常。其实异常并非坏事&#xff0c;它可以让开发人员及时发现、定位到错误&#xff0c;提醒我们做正确的事情&#xff0c;甚至在某些时候&#xff0c;我们还会手动抛出异常。 1.异常的分类 在JS中&#xff0…

2021 OWASP Top 10-零基础案例学习

文章目录 A01:2021 – 权限控制失效情境 #1: SQL 注入攻击风险风险与后果解决方案情境 #2: 未经授权的访问控制漏洞风险与后果解决方案 A02:2021 – 加密机制失效情境 #1: 自动解密的信用卡卡号与SQL注入情境 #2: 弱SSL/TLS使用与会话劫持情境 #3: 不安全的密码存储与彩虹表攻击…

http实现post请求时本地没问题,线上报413错误、nginx配置免费https、nginx反向代理

MENU 错误原因解决其他方式关于nginx的文章 错误原因 前端发送请求以后后端没有收到请求 而客户端却报了413错误 是请求实体过大的异常 如果请求夹带着文件就可能造成请求实体过大 那这里是什么原因造成的呢 在基础的后端开发中 都会用到nginx反向代理 默认大小为1M 超过1M都会…

LinkedList与链表

文章目录 ArrayList的缺陷链表链表的概念及结构链表的实现 LinkedList的使用什么是LinkedListLinkedList具体使用 ArrayList和LinkedList的区别 ArrayList的缺陷 通过源码知道&#xff0c;ArrayList底层使用数组来存储元素 由于其底层是一段连续空间&#xff0c;当在ArrayList任…

Windows 11 系统安装时如何跳过联网和逃避微软账号登录

问题描述 Windows 11 是从 22H2 版本之后开始强制联网何登录微软账号的。 这就带来两个问题&#xff1a; 1、如果我的电脑没有网络或者网卡驱动有问题&#xff0c;那就无法继续安装系统了。 2、如果我有强怕症&#xff0c;就是不想登录微软账号&#xff0c;害怕个人信息泄露…

SpringEL表达式编译模式SpelCompilerMode详解

https://docs.spring.io/spring-framework/reference/core/expressions.html 在构建SpringEL表达式解析器时候&#xff0c;发现可以传递个SpelCompilerMode参数&#xff0c;这个值不传的话默认是OFF // SpelParserConfiguration config new SpelParserConfiguration(); Spel…

uniApp+Vue3+vite+Element UI或者Element Plus开发学习,使用vite构建管理项目,HBuilderX做为开发者工具

我们通常给小程序或者app开发后台时&#xff0c;不可避免的要用到可视化的数据管理后台&#xff0c;而vue和Element是我们目前比较主流的开发管理后台的主流搭配。所以今天石头哥就带大家来一起学习下vue3和Element plus的开发。 准备工作 1&#xff0c;下载HBuilderX 开发者…

Portworx安装和使用

Portworx安装和使用 Portworx介绍 Portworx是一家美国存储初创公司&#xff0c;它研发了业界第一个容器定义存储系统Portworx。Portworx提供了全新的、统一的Scale out存储栈&#xff0c;其核心架构是共享的、松耦合的、分布式、基于元数据的块存储层(卷、块设备、全局共享卷…

【webrtc】MessageHandler 8: 基于线程的消息处理:处理音频输入输出断开

m98代码,看起来m114 去掉了MessageHandler :音频的录制和播放 都使用了on message,但只是用来通知并处理流的断开的。AAudioRecorder AAudioRecorder 处理流断开 OnErrorCallback :有可能 错误回调是别处来的,是其他线程, 但是这个错误的处理要再自己的线程执行: 音频播…

Go中为什么不建议用锁?

Go语言中是不建议用锁&#xff0c;而是用通道Channel来代替(不要通过共享内存来通信&#xff0c;而通过通信来共享内存)&#xff0c;当然锁也是可以用&#xff0c;锁是防止同一时刻多个goroutine操作同一个资源&#xff1b; GO语言中&#xff0c;要传递某个数据给另一个gorout…

JavaScript原型链深度剖析

目录 前言 一、原型链 1.原型链的主要组成 原型&#xff08;Prototype&#xff09; 构造函数&#xff08;Constructor&#xff09; 实例&#xff08;Instance&#xff09; 2.原型链的工作原理 前言 在JavaScript的世界中&#xff0c;原型链&#xff08;Prototype Chain&…

R语言的学习——day1

将数据框中某一列数据改成行名 代码 结果

第四十八节 Java 8 Nashorn JavaScript

Nashorn 一个 javascript 引擎。 从JDK 1.8开始&#xff0c;Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性&#xff0c;其中包含在JDK 7中引入的 invokedynamic&#xff…

.net core ef 连表查询

Information和TypeInfo连表查询 类似&#xff1a; select st.Title1,si.* from [Star_Information] si left join Star_TypeInfo st on si.typeId2st.id 先在EfCoreDbContext.cs配置 protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(b…

基于SSM的文物管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的文物管理系统拥有俩种角色 管理员&#xff1a;个人信息管理、用户管理、分类管理、文物信息管理、文物外借管理、文物维修管理、留言板管理等 用户&#xff1a;登录注册、分类…

[华为OD] C卷 服务器cpu交换 现有两组服务器QA和B,每组有多个算力不同的CPU 100

题目&#xff1a; 现有两组服务器QA和B,每组有多个算力不同的CPU,其中A[i]是A组第i个CPU的运算能 力&#xff0c;B[i]是B组第i个CPU的运算能力。一组服务器的总算力是各CPU的算力之和。 为了让两组服务器的算力相等&#xff0c;允许从每组各选出一个CPU进行一次交换。 求两…

Linux 权限的简单讲解

1、前言 当我们分别使用 touch、mkdir 命令创建一名为 test1 的文件和名为 test2 的目录&#xff0c;发现其中有些参数不一样&#xff0c;本文就来给大家来剖析一下。 2、 参数讲解 我们可以通过切片分为下面几个区域&#xff0c;本文就只简单讲解文件类型、权限、所属用户、所…