【C++进阶】第二节:多态
1、多态的概念
1.1 概念
多态的概念:通俗来说,就是多种形态。具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
2、多态的定义及实现
2.1 多态的构成条件
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如 Student 继承了Person。Person 对象买票全价,Student 对象买票半价。
继承中构成多态有两个条件:
- 必须通过基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

2.2 虚函数
虚函数:即被 virtual 修饰的类成员函数称为虚函数。
class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
2.3 虚函数的重写
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称派生类的虚函数重写了基类的虚函数。
// 多态的条件:
// 1. 虚函数重写
// 2. 基类的指针或者引用调用虚函数
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;Func(ps);Student st;Func(st);return 0;
}
虚函数重写的两个例外:
- 协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A
{};class B : public A
{};class Person
{
public:virtual A* f() {return new A;}
};class Student : public Person
{
public:virtual B* f() {return new B;}
};
- 析构函数的重写(基类与派生类析构函数的名字不同)
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 virtual 关键字,都与基类的析构函数构成重写。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成 destructor。
class Person
{
public:virtual ~Person() {cout << "~Person()" << endl;}
};class Student : public Person
{
public:virtual ~Student() { cout << "~Student()" << endl; }
};// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数
int main()
{Person* p1 = new Person;Person* p2 = new Student;// 析构是虚函数,才能正确调用析构函数// 先调用Person::~Person()析构函数,然后自动使用operator delete来释放p1指向的内存delete p1;// 先调用Student::~Student()析构函数,再调用基类Person::~Person()析构函数。最后,自动使用operator delete来释放p2指向的内存delete p2;return 0;
}


注意:
普通调用:调用函数的对象类型决定调的哪个函数
多态调用:调用指针或者引用指向的对象。指向母类调用母类的函数,指向女类调用女类的函数
2.4 C++11 override和final
C++11 提供了 override 和 final 两个关键字,可以帮助用户检测是否重写。
- final:修饰虚函数,表示该虚函数不能再被重写
// final 修饰类,不能被继承
// final 修饰虚函数,不能被重写
class Car
{
public:virtual void Drive() final{}
};class Benz : public Car
{
public:// 报错/*virtual void Drive() {cout << "Benz-舒适" << endl;}*/
};
- override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car
{
public:virtual void Drive(){}
};class Benz :public Car
{
public:virtual void Drive() override {cout << "Benz-舒适" << endl;}
};
2.5 重载、重写(覆盖)、隐藏(重定义)的对比

3、抽象类
3.1 概念
在虚函数的后面写上 =0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象,派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
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;}
};void Test()
{Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();
}
3.2 接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。
4、多态的原理
4.1 虚函数表
// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};
通过观察测试我们发现b对象是 8bytes,除了_b成员,还多一个 __vfptr 放在对象的前面,对象中的这个指针我们叫做虚函数表指针(v代表 virtual,f代表 function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。

class Base
{
public:virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};int main()
{Base b;Derive d;return 0;
}
通过观察和测试,我们发现了以下几点问题:
- 派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是基类继承下来的成员,另一部分是自己的成员。
- 基类b对象和派生类d对象虚表是不一样的,这里我们发现 Func1 完成了重写,所以d的虚表中存的是重写的 Derive::Func1,虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
- 另外 Func2 继承下来后是虚函数,所以放进了虚表,Func3 也继承下来了,但是不是虚函数,所以不会放进虚表。
- 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个 nullptr。
- 总结一下派生类的虚表生成:
- 先将基类中的虚表内容拷贝一份到派生类虚表中
- 如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
- 派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
虚函数存在哪的?虚表存在哪的?
虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。那么虚表存在哪的呢?vs下是存在常量区的。

// 验证虚表在vs下存在代码段
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};void func()
{cout << "void func()" << endl;
}int main()
{Base b1;Base b2;static int a = 0;int b = 0;int* p1 = new int;const char* p2 = "hello world";printf("栈:%p\n", &b);printf("堆:%p\n", p1);printf("静态区:%p\n", &a);printf("常量区:%p\n", p2);printf("虚表:%p\n", *((int*)&b1));printf("虚函数地址:%p\n", &Base::func1);// 函数名就是函数的地址(成员函数比较特殊,要加&)printf("普通函数地址:%p\n", func);return 0;
}
4.2 多态的原理

class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person
{
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};void Func(Person& p)
{p.BuyTicket();
}int main()
{Person Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}
-
观察下图的红色箭头我们看到,p是指向 mike 对象时,p->BuyTicket 在 mike 的虚表中找到虚函数是 Person::BuyTicket。
-
观察下图的蓝色箭头我们看到,p是指向 johnson 对象时,p->BuyTicket 在 johson 的虚表中找到虚函数是 Student::BuyTicket。
-
这样就实现出了不同对象去完成同一行为时,展现出不同的形态。
-
反过来思考我们要达到多态,有两个条件,一个是虚函数覆盖,一个是对象的指针或引用调用虚函数。
-
多态调用,是运行起来以后到对象的中查找的。普通调用,是在编译时确定的。

注意:
如果是派生类对象赋值给基类对象不能实现多态,为什么?因为派生类的成员会拷贝给基类,但是不会拷贝虚函数表指针。
4.3 动态绑定与静态绑定
- 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
- 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。
5、单继承和多继承关系的虚函数表
需要注意的是在单继承和多继承关系中,下面我们去关注的是派生类对象的虚表模型,因为基类的虚表模型前面我们已经看过了,没什么需要特别研究的。
5.1 单继承中的虚函数表
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:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }virtual void func4() { cout << "Derive::func4" << endl; }
private:int b;
};
观察下图中的监视窗口中我们发现看不见 func3 和 func4。这里是编译器的监视窗口故意隐藏了这两个函数,也可以认为是他的一个小bug。那么我们如何查看d的虚表呢?下面我们使用代码打印出虚表中的函数。

// 虚函数的地址一定会被放进类的虚函数表
// 打印虚表
typedef void (*VFUNC)();
//void PrintVFT(VFUNC a[])
void PrintVFT(VFUNC* a)
{for (size_t i = 0; a[i] != 0; i++){printf("[%d]:%p->", i, a[i]);VFUNC f = a[i];f(); // 直接用函数指针调用这个函数//(*f)(); // 函数指针解引用调用}printf("\n");
}int main()
{void (*f1)(); // 定义函数指针VFUNC f2; // typedef后可以这样定义Base b;PrintVFT((VFUNC*)(*((int*)&b)));Derive d;X x;PrintVFT((VFUNC*)(*((int*)&d)));PrintVFT((VFUNC*)(*((int*)&x)));return 0;
}


5.2 多继承中的虚函数表
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 (*VFUNC)();
void PrintVFT(VFUNC* a)
{for (size_t i = 0; a[i] != 0; i++){printf("[%d]:%p->", i, a[i]);VFUNC f = a[i];f();}printf("\n");
}
int main()
{Derive d;PrintVFT((VFUNC*)(*(int*)&d));//PrintVFT((VFUNC*)(*(int*)((char*)&d + sizeof(Base1))));Base2* ptr = &d;PrintVFT((VFUNC*)(*(int*)ptr));return 0;
}

注意:
多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中。
6、继承和多态常见的面试问题
6.1 概念查考
1. ( )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体的对象。
A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定
2. 面向对象设计中的继承和组合,下面说法错误的是?( )
A:继承允许我们覆盖重写母类的实现细节,母类的实现对于女类是可见的,是一种静态复用,也称为白盒复用
B:组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动态复用,也称为黑盒复用
C:优先使用继承,而不是组合,是面向对象设计的第二原则
D:继承可以使女类能自动继承母类的接口,但在设计模式中认为这是一种破坏了母类的封装性的表现
3. 以下关于纯虚函数的说法,正确的是( )
A:声明纯虚函数的类不能实例化对象
B:声明纯虚函数的类是虚基类
C:子类必须实现基类的纯虚函数
D:纯虚函数必须是空函数
4. 关于虚函数的描述正确的是( )
A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型
B:内联函数不能是虚函数
C:派生类必须重新定义基类的虚函数
D:虚函数可以是一个static型的函数
5. 关于虚表说法正确的是( )
A:一个类只能有一张虚表
B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
C:虚表是在运行期间动态生成的
D:一个类的不同对象共享该类的虚表
6. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( )
A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
B:A类对象和B类对象前4个字节存储的都是虚基表的地址
C:A类对象和B类对象前4个字节存储的虚表地址相同
D:A类和B类虚表中虚函数个数相同,但A类和B类使用的不是同一张虚表
7. 下面程序输出结果是什么? ()
class A
{
public:A(const char* s){cout << s << endl;}~A(){}
};class B : virtual public A
{
public:B(const char* s1, const char* s2):A(s1){cout << s2 << endl;}
};class C : virtual public A
{
public:C(const char* s1, const char* s2):A(s1){cout << s2 << endl;}
};class D : public B, public C
{
public:D(const char* s1, const char* s2, const char* s3, const char* s4):B(s1, s2), C(s1, s3), A(s1){cout << s4 << endl;}
};
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
8. 多继承中指针偏移问题?下面说法正确的是( )
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
9. 以下程序输出结果是什么()
class A
{
public:virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}virtual void test(){ func();}
};class B : public A
{public:void func(int val = 0){ std::cout<<"B->"<< val <<std::endl; }
};int main(int argc ,char* argv[])
{B* p = new B;p->test();return 0;
}
A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确
参考答案:
1. D
2. C
3. A
B.包含纯虚函数的类叫做抽象类(也叫接口类)
4. B
5. D
6. D
虚基表和虚表不是一种:
7. A
- 构造函数先初始化列表再函数体,最后一个打印的是 class D
- 同一继承链中,任何虚基类只会在最终派生类中被构造一次,即使多个路径上都存在对它的继承。A是B和C的虚基类。
- 构造顺序:虚基类总在派生类进行其它任何初始化之前被初始化,即使在初始化列表中没有明确调用它,派生类构造函数会遵循:虚拟基类 -> 非虚基类 -> 自身成员。
- 当创建D类型的对象时,构造过程如下:虚基类A的构造函数被调用 -> B的构造函数被调用 -> C的构造函数被调用 -> D的构造函数的主体执行。
8. C
虽然 p1 和 p3 指向相同的位置,但是访问的内容不一样
9. B
p->test() 是一个普通调用,this->func() 是多态调用,会调用B类中的 func() 函数。
6.2 问答题
什么是多态
- 静态多态:通过函数重载和运算符重载实现,在编译时确定调用哪个函数
- 动态多态:1、基类的指针或者引用调用虚函数 2、虚函数完成重写,在运行时确定调用哪个函数。指向谁就调用谁的虚函数,实现多种形态
什么是重载、重写(覆盖)、重定义(隐藏)?
- 重载:同一作用域内,函数名相同但参数列表不同(参数类型、数量、顺序),是静态多态的一种形式。
- 重写:派生类重新定义基类中已经存在的虚函数,保持函数名、参数列表完全一致。重写的函数在运行时被调用时,会调用派生类的实现。即动态多态。
- 重定义:派生类定义了与基类同名但不是虚函数的成员函数或变量,会隐藏基类的成员。在这种情况下,调用基类的成员需要显式地使用作用域解析运算符。
多态的实现原理?参考课件
inline 函数可以是虚函数吗?
答:可以,普通调用,inline 起作用;多态调用,inline 不起作用
静态成员可以是虚函数吗?
答:不能,编译报错,因为静态成员函数没有 this 指针,可以指定类域调用,无法构成多态
构造函数可以是虚函数吗?
答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。虚函数多态调用,要到虚表中找,但是虚表指针都还没初始化。
析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
答:可以,并且最好把基类的析构函数定义成虚函数。
对象访问普通函数快还是虚函数更快?
答:首先如果是普通调用,是一样快的。如果是多态调用,则普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。
虚函数表是在什么阶段生成的,存在哪的?
答:虚函数表是在编译阶段就生成的,一般情况下存在常量区。
什么是抽象类?抽象类的作用?
答:参考(3、抽象类)。抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。
相关文章:
【C++进阶】第二节:多态
1、多态的概念 1.1 概念 多态的概念:通俗来说,就是多种形态。具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 2、多态的定义及实现 2.1 多态的构成条件 多态是在不同继承关系的类对象,去调用同一函数&a…...
梯度下降法以及 Python 实现
文章目录 1. 引言2. 梯度法3. 例子4. 代码实现5. 讨论 — 学习率 η \eta η5.1 当 η \eta η 设置过大5.2 当 η \eta η 设置过小 参考 1. 引言 梯度下降法,可以根据微分求出的斜率计算函数的最小值。 在人工智能中,经常被应用于学习算法。 2. 梯…...
Postman cURL命令导入导出
你是否曾为在Postman和终端之间切换、整理请求而抓狂?其实,Postman支持与cURL命令的无缝互通,通过导入导出,极大提升效率。用好这个功能,分分钟让接口测试更高效! Postman如何快速导入cURL命令?…...
Java 在Json对象字符串中查找和提取特定的数据
1、在处理JSON数据时,需要提出个别字段的值,通过正则表达式提取特定的数据 public static void main(String[] args) {//定义多个JSON对象字符串类型,假设每个对象有a,b,c 字段String strJson "{\"a\":1.23,\"b\"…...
synchronized的特性
1.互斥 对于synchronized修饰的方法及代码块不同线程想同时进行访问就会互斥。 就比如synchronized修饰代码块时,一个线程进入该代码块就会进行“加锁”。 退出代码块时会进行“解锁”。 当其他线程想要访问被加锁的代码块时,就会阻塞等待。 阻塞等待…...
领域泛化与领域自适应
领域泛化(Domain Generalization)和领域适应(Domain Adaptation)是机器学习领域中处理不同数据分布场景下模型训练与应用的两种策略,领域泛化在泛化到目标领域时不需要进行调整,而领域自适应在适应到目标领…...
使用aspx,完成一个转发http的post请求功能的api接口,url中增加目标地址参数,传递自定义header参数
使用aspx,完成一个转发http的post请求功能的api接口,url中增加目标地址参数,传递自定义header参数 首先,简单实现一下,如何在ASPX页面中实现这个功能实现代码说明:注意事项: 然后进阶࿰…...
实际车辆行驶轨迹与预设路线偏离检测的Java实现
准备工作 本项目依赖于两个关键库:JTS Topology Suite(简称JTS),用于几何对象创建和空间分析;以及GeoTools,用于处理坐标转换和其他地理信息任务。确保开发环境中已经包含了这两个库,并且正确配…...
从excel数据导入到sqlsever遇到的问题
1、格式问题时间格式,excel中将日期列改为日期未生效,改完后,必须手动单击这个单元格才能生效,那不可能一个一个去双击。解决方案如下 2、导入之后表字段格式问题,数据类型的用navicat导入之后默认是nvarchar类型的&a…...
Linux操作系统——Linux的磁盘管理系统、文件inode及软硬链接
目录 前言 一、磁盘 1、物理结构 2、存储结构 3、磁盘的逻辑结构 二、文件系统 1、基本概念 2、组的概念 1)Data Blaocks 2)inode Table 3)inode Bitmap 4)Blocks Bitmap 5)Group Descriptor Table 6)Sup…...
算法刷题Day11: BM33 二叉树的镜像
点击题目链接 思路 转换为子问题:左右子树相反转。遍历手法:后序遍历 代码 class Solution:def Transverse(self,root: TreeNode):if root None:return rootnewleft self.Transverse(root.left)newright self.Transverse(root.right)# 对root节点…...
WPF+MVVM案例实战与特效(三十五)- 掌握 Windows 屏幕键盘控制的艺术(TouchKeyBoardHelper 类)
文章目录 1、概述2、TouchKeyBoardHelper 类1、代码实现2、代码解释3、实际应用1、帮助类库与文件创建2、项目引用运行效果3、答疑解惑1、概述 在WPF应用程序开发中,有时需要提供启动或关闭屏幕键盘(On-Screen Keyboard, OSK)的功能。为了实现这一需求,我们创建了一个名为…...
Python+OpenCV系列:绘制中文的方法
绘制中文的方法 方法一:使用Pillow(PIL)与OpenCV结合方法二:使用Matplotlib与OpenCV结合方法三:结合第三方库OpenCV-ZH注意事项 在Python中,使用OpenCV绘制中文需要处理字体加载问题,因为OpenCV…...
精品推荐 | StarLighter 1×dsDNA HS Assay Kit
关键词:核酸浓度测定,核酸定量检测试剂盒,dsDNA浓度测定,dsDNA定量检测 产品简介 StarLighter 1dsDNA HS Assay Kit是一种快速简便的双链DNA(dsDNA)荧光定量检测试剂盒,具有极高的检测灵敏度&…...
挑战用React封装100个组件【010】
Hello,大家好,今天我挑战的组件是这样的! 今天这个组件是一个打卡成功,或者获得徽章后的组件。点击按钮后,会弹出礼花。项目中的勋章是我通过AI生成的,还是很厉害的哈!稍微抠图直接使用。最后面…...
burp suite 5
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...
锐捷Web认证
文章目录 Web认证二代 Web 认证配置 🏡作者主页:点击! 🤖Datacom专栏:点击! ⏰️创作时间:2024年12月6日11点40分 Web认证 Portal 认证、Web认证 Web认证的介绍 Web 认证使用浏览器进行身份验…...
【开源免费】基于Vue和SpringBoot的服装生产管理系统(附论文)
博主说明:本文项目编号 T 066 ,文末自助获取源码 \color{red}{T066,文末自助获取源码} T066,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…...
每日速记10道MySQL面试题16
其他资料 每日速记10道java面试题01-CSDN博客 每日速记10道java面试题02-CSDN博客 每日速记10道java面试题03-CSDN博客 每日速记10道java面试题04-CSDN博客 每日速记10道java面试题05-CSDN博客 每日速记10道java面试题06-CSDN博客 每日速记10道java面试题07-CSDN博客 每…...
云计算考试题
1、与SaaS不同的,这种“云”计算形式把开发环境或者运行平台也作为一种服务给用户提供。(B) A、软件即服务 B、基于平台服务 C、基于WEB服务 D、基于管理服务 2、云计算是对(D)技术的发展与运用 A、并行计算 B、网格计算 C、分布式计算 D、三个选项都是 3、Amazon.com公司…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...




