【C++基础】07:多态

news2024/12/24 0:51:04

多态


OVERVIEW

  • 多态
      • 一、多态
        • 1.基本概念:
        • 2.E1-计算器类
        • 3.纯虚函数&抽象类:
        • 4.E2-制作饮品
        • 5.虚析构&纯虚析构:
        • 6.E3-电脑组装
      • 二、运算符重载
        • 1.加号运算符重载:
        • 2.左移>>运算符重载:
        • 3.递增++运算符重载:
        • 4.赋值=运算符重载:
        • 5.关系运算符重载:
        • 6.函数调用运算符重载:

一、多态

1.基本概念:

多态是面向对象三大特性之一,多态分为两类:静态多态、动态多态

  1. 静态多态:函数重载运算符重载属于静态多态(复用函数名)
  2. 动态多态:派生类和虚函数实现运行时多态

静态多态与动态多态的区别:

静态多态的函数地址早绑定,编译阶段确定函数地址。

动态多态的函数地址晚绑定,运行阶段确定函数地址。

动态多态满足条件:

  1. 继承关系
  2. 子类需要重写父类的虚函数

地址绑定早,在编译阶段确定函数地址:

#include<iostream>
using namespace std;

class Animal{
public:
    void speak(){
        cout << "动物在说话" << endl;
    }
};

class Cat:public Animal{
public:
    void speak(){
        cout << "小猫在说话" << endl;
    }
};

class Dog:public Animal{
public:
    void speak(){
        cout << "小狗在说话" << endl;
    }
};

//地址早绑定 在编译阶段确定函数地址:Animal &animal = animal;
Animal &animal = cat;
void doSpeak(Animal &animal){
    animal.speak();
}

void test01(){
    Cat cat;
    doSpeak(cat);
    Dog dog;
    doSpeak(dog);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220217210337285

在父类的speak函数前添加virtual关键字转为虚函数(子类再重写函数),实现动态多态。

地址晚绑定,在运行阶段进行绑定:

#include<iostream>
using namespace std;

class Animal{
public:
    virtual void speak(){
        cout << "动物在说话" << endl;
    }
};

class Cat:public Animal{
public:
    void speak(){
        cout << "小猫在说话" << endl;
    }
};

class Dog:public Animal{
public:
    void speak(){
        cout << "小狗在说话" << endl;
    }
};

//地址晚绑定 在运行阶段进行绑定:Animal &animal = cat; Animal &animal = dog;
void doSpeak(Animal &animal){
    animal.speak();
}

void test01(){
    Cat cat;
    doSpeak(cat);
    Dog dog;
    doSpeak(dog);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220217211006674

注意:与函数重载不同,函数重写是指重写的函数的返回值类型、函数名、参数列表完全一致。

父类的指针 or 引用指向子类对象Cat、Dog

image-20220217212907159

2.E1-计算器类

多态的优点:

  1. 代码组织结构清晰
  2. 可读性强
  3. 利于前期和后期的扩展以及维护

分别使用普通写法和多态技术,设计实现两个操作数进行运算的计数器类:

#include<iostream>
using namespace std;

class Calculator{
public:
    int getResult(string oper){
        if(oper == "+"){
            return num1 + num2;
        }else if(oper == "-"){
            return num1 - num2;
        }else if(oper == "*"){
            return num1 * num2;
        }
    }
    int num1;
    int num2;
};

void test01(){
    Calculator c;
    c.num1 = 10;
    c.num2 = 20;
    cout << c.num1 << " + " << c.num2 << " = " << c.getResult("+") << endl;
    cout << c.num1 << " - " << c.num2 << " = " << c.getResult("-") << endl;
    cout << c.num1 << " * " << c.num2 << " = " << c.getResult("*") << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220217220048120

如果需要扩展计算机的新功能,需要对源码进行修改。

#include<iostream>
using namespace std;

//实现计数器的基类
class AbstractCalculator{
public:
    virtual int getResult(){
        return 0;
    }
    int num1;
    int num2;
};

class AddCalculator:public AbstractCalculator{
public:
    int getResult(){
        return num1 + num2;
    }
};

class SubCalculator:public AbstractCalculator{
public:
    int getResult(){
        return num1 - num2;
    }
};

class MulCalculator:public AbstractCalculator{
public:
    int getResult(){
        return num1 * num2;
    }
};

void test01(){
    AbstractCalculator *abc;
    //1.加法运算
    abc = new AddCalculator;
    abc->num1 = 10;
    abc->num2 = 30;
    cout << abc->num1 << " + " << abc->num2 << " = " << abc->getResult() << endl;
    //new创建的数据存放在堆区,使用后需要手动释放
    delete(abc);

    //2.减法运算
    abc = new SubCalculator;
    abc->num1 = 10;
    abc->num2 = 30;
    cout << abc->num1 << " - " << abc->num2 << " = " << abc->getResult() << endl;
    //new创建的数据存放在堆区,使用后需要手动释放
    delete(abc);

    //3.乘法运算
    abc = new MulCalculator;
    abc->num1 = 10;
    abc->num2 = 30;
    cout << abc->num1 << " * " << abc->num2 << " = " << abc->getResult() << endl;
    //new创建的数据存放在堆区,使用后需要手动释放
    delete(abc);

}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220217215914734

3.纯虚函数&抽象类:

在多态中通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容(因此可以将虚函数改为纯虚函数

当一个类中有了纯虚函数,这个类也可称为抽象类。

纯虚函数语法:virtual 返回值类型 函数名(参数列表) = 0;

抽象类特点:

  1. 无法实例化对象
  2. 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include<iostream>
using namespace std;

class Base{
public:
    virtual void func() = 0;
};

class Son1:public Base{
    virtual void func(){
        cout << "Son1::func()函数调用" << endl;
    }
};

class Son2:public Base{
    virtual void func(){
        cout << "Son2::func()函数调用" << endl;
    }
};

void test01(){
    Base *base;
    base = new Son1;
    base->func();
    delete(base);
    base = new Son2;
    base->func();
    delete(base);
}

int main(){
    test01();
    system("pause");
    return 0;
}

4.E2-制作饮品

制作饮品的流程分为:煮水->冲泡->倒入杯中->加入辅料,利用多态技术实现本案例:

#include<iostream>
using namespace std;

class Base{
public:
    virtual void Boil() = 0;
    virtual void Brew() = 0;
    virtual void PourInCup() = 0;
    virtual void PutSomething() = 0;
    void makeDrink(){
        Boil();
        Brew();
        PourInCup();
        PutSomething();
    }
};

class Coffee:public Base{
    virtual void Boil(){
        cout << "煮一壶自来水" << endl;
    }
    virtual void Brew(){
        cout << "冲泡即食咖啡" << endl;
    }
    virtual void PourInCup(){
        cout << "倒入咖啡杯中" << endl;
    }
    virtual void PutSomething(){
        cout << "加入糖和牛奶" << endl;
    }
};

class Tea:public Base{
    virtual void Boil(){
        cout << "煮一壶自来水" << endl;
    }
    virtual void Brew(){
        cout << "冲泡茶叶" << endl;
    }
    virtual void PourInCup(){
        cout << "倒入茶杯中" << endl;
    }
    virtual void PutSomething(){
        cout << "加入枸杞" << endl;
    }
};

void doWork(Base *base){
    base->makeDrink();
    delete base;
}

void test01(){
    doWork(new Coffee);
    cout << "--------------------" << endl;
    doWork(new Tea);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220217222819693

注意:该案例中另外创建了doWork函数,将函数调用操作与堆区内存释放操作简化(只保留父类指针指向修改)优化程序结构。

5.虚析构&纯虚析构:

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。

需要将父类中的析构函数改为虚构函数 or 存虚构函数

虚析构:virtual ~类名(){}

纯虚析构:virtual ~类名() = 0; 类名::~类名(){}

虚析构与纯虚构共性:

  1. 可以解决父类指针释放,无法调用子类析构函数
  2. 都需要有具体的函数实现,否则报错。

虚析构与纯虚构区别:

  1. 如果是纯虚析构,该类属于抽象类(无法实例化)
#include<iostream>
using namespace std;

class Animal{
public:
    Animal(){
        cout << "Animal构造函数调用" << endl;
    }
    virtual void speak() = 0;
    ~Animal(){
        cout << "Animal析构函数调用" << endl;
    }
};

class Cat:public Animal{
public:
    Cat(string s){
        cout << "Cat构造函数调用" << endl;
        name = new string(s);
    }
    virtual void speak(){
        cout << *name << "小猫在说话" << endl;
    }
    ~Cat(){
        if(name != NULL){
            cout << "Cat析构函数调用" << endl;
            delete name;
            name = NULL;
        }
    }
    string *name;//让小猫的name数据创建在堆区,利用一个指针去维护
};

void test01(){
    Animal *animal;
    animal = new Cat("Tom");
    animal->speak();
    delete animal;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220217224942024

父类指针在释放时,不会调用到子类的析构函数(子类如果有堆区数据,会出现内存泄漏)。

为父类Animal析构函数添加virtual关键字,将子类的析构函数改为虚析构解决。

image-20220217225855310

image-20220217225755662

子类对象Cat析构函数调用,堆区内存被释放。

6.E3-电脑组装

电脑主要组成部件为CPU、显卡、内存条。

将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenove厂商

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口,测试时组装三台不同的电脑进行工作:

#include<iostream>
using namespace std;

class CPU{
public:
    virtual void calculate() = 0;
};

class VideoCard{
public:
    virtual void display() = 0;
};

class Memory{
public:
    virtual void storage() = 0;
};

class Computer{
public:
    Computer(CPU *c, VideoCard *v, Memory *m){
        cpu = c;
        vcd = v;
        mem = m;
    }
    //(1)提供工作的函数
    void work(){
        cpu->calculate();
        vcd->display();
        mem->storage();
    }
    //(2)提供析构函数,释放开辟在堆区内存的三个电脑零件数据
    ~Computer(){
        if(cpu != NULL){
            delete cpu;
            cpu = NULL;
        }
        if(vcd != NULL){
            delete vcd;
            vcd = NULL;
        }
        if(mem != NULL){
            delete mem;
            mem = NULL;
        }
    }
private:
    CPU *cpu;
    VideoCard *vcd;
    Memory *mem;
};

//Intel厂商
class IntelCPU:public CPU{
public:
    void calculate(){
        cout << "Intel的CPU开始计算了" << endl;
    }
};

class IntelVideoCard:public VideoCard{
public:
    void display(){
        cout << "Intel的VideoCard开始显示了" << endl;
    }
};

class IntelMemory:public Memory{
public:
    void storage(){
        cout << "Intel的内存条开始存储了" << endl;
    }
};

//Lenovo厂商
class LenovoCPU:public CPU{
public:
    void calculate(){
        cout << "Lenovo的CPU开始计算了" << endl;
    }
};

class LenovoVideoCard:public VideoCard{
public:
    void display(){
        cout << "Lenovo的VideoCard开始显示了" << endl;
    }
};

class LenovoMemory:public Memory{
public:
    void storage(){
        cout << "Lenovo的内存条开始存储了" << endl;
    }
};

void test01(){
    //1.创建第一台电脑
    CPU *cpu = new IntelCPU;
    VideoCard *vcd = new IntelVideoCard;
    Memory *mem = new IntelMemory;
    Computer *pc1 = new Computer(cpu, vcd, mem);
    pc1->work();
    delete pc1;
    //2.创建第二台电脑
    Computer *pc2 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);
    pc2->work();
    delete pc2;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220220233342960

核心内容:

  1. 对于零件层(都是抽象处理、纯虚函数),不同的厂商实现不同的零件(具体厂商子类实现)
  2. 三个指针接收数据开辟在堆区的三个零件,通过一个work()函数使三个零件工作(堆区开辟的数据需利用父类析构函数释放)
  3. 利用多态的架构实现优化程序结构

二、运算符重载

c++中的运算符重载是指,对已有的运算符进行重新定义(赋予其另一种功能),以适应不同的数据类型。

1.加号运算符重载:

  1. 成员函数方式重载+运算符:p1.operator+(p2)
  2. 全局函数方式重载+运算符:operator+(p1, p2)

通过重载加号运算符,可以实现自定义数据类型运算规则的定制。

#include<iostream>
using namespace std;

class Person{
public:
    int a;
    int b;
    //1.成员函数重载+号本质:Person p3 = p1.operator+(p2);
    /*Person operator+(Person &p){
        Person temp;
        temp.a = this->a + p.a;
        temp.b = this->b + p.b;
        return temp;
    }
    */
};

//2.全局函数重载+号本质:Person p3 = operator+(p1, p2);
Person operator+(Person &p1, Person &p2){
    Person temp;
    temp.a = p1.a + p2.a;
    temp.b = p1.b + p2.b;
    return temp;
}

Person operator+(Person &p1, int num){
    Person temp;
    temp.a = p1.a + num;
    temp.b = p1.b + num;
    return temp;
}

void test01(){
    Person p1;
    p1.a = 10;
    p1.b = 10;
    Person p2;
    p2.a = 20;
    p2.b = 20;
    Person p3 = p1 + p2;
    cout << "p3.a = " << p3.a << endl;
    cout << "p3.b = " << p3.b << endl;
    Person p4 = p3 + 50;
    cout << "p4.a = " << p4.a << endl;
    cout << "p4.b = " << p4.b << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220210133452827

注意:对于内置的数据类型的表达式的运算符是不可能改变的(对于int、double等内置数据类型的运算规则已经不能修改)

2.左移>>运算符重载:

通过重载左移运算符,可以输出自定义的数据类型。

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

class Person{
public:
    int a;
    int b;
    //1.不能利用成员函数重载<<运算符,因为无法实现cout出现在左侧
    //p.operator<<(cout) === p << cout
};

//2.只能利用全局函数重载<<运算符
//operator<<(cout, p) === cout << p
ostream & operator<<(ostream &out, Person &p){//cout对象全局只有一个需要利用&引用的方式传递
    out << "a = " << p.a << "   " << "b = " << p.b;
    return out;
}

void test01(){
    Person p1;
    p1.a = 10;
    p1.b = 10;
    Person p2;
    p2.a = 20;
    p2.b = 20;
    cout << p1 << endl;
    cout << p2 << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220210140253098

注意:要进一步理解左移运算符重载,必须深刻理解cout与cin对象(cout属于ostream类型、cin属于istream类型)

3.递增++运算符重载:

通过重载递增运算符++,可以自己模拟一个整型变量。

step1:首先重载左移运算符,实现对自定义的整型MyInteger的输出操作。

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

class MyInteger{
    friend ostream& operator<<(ostream& out, MyInteger myint);
public:
    MyInteger(){
        num = 0;
    }
private:
    int num;
};

//利用全局函数重载<<运算符
ostream& operator<<(ostream& out, MyInteger myint){//cout对象全局只有一个需要利用&引用的方式传递
    out << myint.num;
    return out;
}

void test01(){
    MyInteger myint;
    cout << myint << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220211131650267

step2:对自增运算符进行重载,实现自定义整型的自增操作(需要分别重载前置自增 与 后置自增)。

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

class MyInteger{
    friend ostream& operator<<(ostream& out, MyInteger myint);
public:
    MyInteger(){
        num = 0;
    }
    //1.重载前置自增运算符,返回引用是实现一直对一个数据类型进行操作
    MyInteger& operator++(){
        num++;
        return *this;
    }
    //2.重载后置自增运算符,返回的是一个值(若返回引用&temp释放后为非法操作)
    MyInteger operator++(int){
        MyInteger temp = *this;
        num++;
        return temp;
    }
private:
    int num;
};

//利用全局函数重载<<运算符
ostream& operator<<(ostream& out, MyInteger myint){//cout对象全局只有一个需要利用&引用的方式传递
    out << myint.num;
    return out;
}

void test01(){
    MyInteger myint;
    cout << ++(++myint) << endl;
    cout << myint << endl;
}

void test02(){
    MyInteger myint;
    cout << myint++ << endl;
    cout << myint << endl;
}

int main(){
    cout << "前置自增测试" << endl;
    test01();
    cout << "后置自增测试" << endl;
    test02();
    system("pause");
    return 0;
}

image-20220211133358995

练习:对比自增重载的过程,尝试完成自减运算符的重载操作。

4.赋值=运算符重载:

cpp编译器至少会给一个类添加4个函数:

默认构造函数、默认析构函数、默认拷贝构造函数(对属性进行值拷贝)、赋值运算符operator=(对属性进行值拷贝)

重载赋值运算符=,可以实现自定义数据类型的赋值操作。

需要注意:如果类中有属性指向堆区,做赋值操作时会出现深浅拷贝问题。

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

class Person{
public:
    Person(int n){
        age = new int(n);//将数据创建在堆区,并利用age指针维护堆区的数据
    }
    ~Person(){
        if(age != NULL){
            delete age;
            age = NULL;
        }
    }
    int *age;
};

void test01(){
    Person p1(18);
    Person p2(20);
    p2 = p1;
    cout << "p1的年龄age指针指向的地址为:" << p1.age << endl;
    cout << "p1的年龄age指针解引用后为:" << *p1.age << endl;
    cout << "p2的年龄age指针指向的地址为:" << p2.age << endl;
    cout << "p2的年龄age指针解引用后为:" << *p2.age << endl;
}

int main(){
    test01();

    system("pause");
    return 0;
}

image-20220211205530761

返回值出现异常-1073740940

简单的赋值=操作中存在的浅拷贝问题,使得堆区内存重复释放(报错),必须利用深拷贝解决。

image-20220211205135980

image-20220211205326510

重载赋值运算符=,利用深拷贝解决浅拷贝中出现的堆区内存重复释放问题:

#include<iostream>
using namespace std;

class Person{
public:
    Person(int n){
        age = new int(n);//将数据创建在堆区,并利用age指针维护堆区的数据
    }
    ~Person(){
        if(age != NULL){
            delete age;
            age = NULL;
        }
    }
    //重载赋值运算符
    Person& operator=(Person &p){
        //1.age = p.age;(编译器默认提供浅拷贝)
        //2.应该先判断是否有属性在堆区,如果有先释放干净然后再进行深拷贝
        if(age != NULL){
            delete age;
            age = NULL;
        }
        age = new int(*p.age);//深拷贝操作
        //3.返回对象本身
        return *this;
    }
    int *age;
};

void test01(){
    Person p1(18);
    Person p2(20);
    Person p3(24);
    p2 = p1;
    cout << "p1的年龄age指针指向的地址为:" << p1.age << endl;
    cout << "p1的年龄age指针解引用后为:" << *p1.age << endl;
    cout << "p2的年龄age指针指向的地址为:" << p2.age << endl;
    cout << "p2的年龄age指针解引用后为:" << *p2.age << endl;
    p3 = p2 = p1;
    cout << "p3的年龄age指针指向的地址为:" << p3.age << endl;
    cout << "p3的年龄age指针解引用后为:" << *p3.age << endl;
}

int main(){
    test01();

    system("pause");
    return 0;
}

image-20220211210755857

总结:深拷贝不同于浅拷贝只是做了简单的指针复制操作(每个地址都不同),另外在cpp中连续的赋值操作是被允许的。

5.关系运算符重载:

重载关系运算符,可以实现自定义数据类型的比较操作。

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

class Person{
public:
    Person(string n, int a){
        name = n;
        age = a;
    }
    //重载关系运算符==
    bool operator==(Person &p){
        if(this->name == p.name && this->age == p.age) {
            return true;
        }
        return false;
    }
    bool operator!=(Person &p){
        if(this->name == p.name && this->age == p.age) {
            return false;
        }
        return true;
    }
    string name;
    int age;
};

void test01(){
    Person p1("lch", 21);
    Person p2("luochenhao", 21);
    Person p3("lch", 21);
    cout << "p1 != p2:" << (p1 != p2) << endl;
    cout << "p1 == p3:" << (p1 == p3) << endl;
    cout << "p2 == p3:" << (p2 == p3) << endl;
}

int main(){
    test01();

    system("pause");
    return 0;
}

image-20220211214821205

6.函数调用运算符重载:

函数调用运算符()也可以进行重载操作,

由于重载后使用的方式非常像函数的调用,因此被称为仿函数

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

class MyPrint{
public:
    //重载函数调用运算符
    void operator()(string text){
        cout << "()运算符重载(仿函数):" << endl;
        cout << text << endl;
    }
};

void func(string text){
    cout << "正常函数调用:" << endl;
    cout << text << endl;
}

void test01(){
    MyPrint myPrint;
    myPrint("author:lch!");//使用方式与函数调用十分相似,所以()运算符重载也被称为仿函数
    func("author:lch!");
}

int main(){
    test01();

    system("pause");
    return 0;
}

image-20220211220212768

仿函数没有固定的写法(返回值、参数列表根据需要可以非常灵活):

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

class MyPrint{
public:
    void operator()(string text){
        cout << "()运算符重载(仿函数):" << endl;
        cout << text << endl;
    }
};

class MyAdd{
public:
    int operator()(int a, int b){
        return a + b + 100;
    }
};

void test01(){
    MyPrint myPrint;
    myPrint("author:lch!");//使用方式与函数调用十分相似,所以()运算符重载也被称为仿函数
}

void test02(){
    MyAdd myAdd;
    int ret = myAdd(100, 100);
    cout << "ret(a, b) = " << ret << endl;
}

int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

image-20220211221234667

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

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

相关文章

数据结构课设-小老鼠走迷宫(JAVA版)

学校题目: 题目内容: 程序开始运行时显示一个迷宫地图&#xff0c;迷宫中央有一只老鼠&#xff0c;迷宫的右下方有一个粮仓。游戏的任务是使用键盘上的方向健操纵老鼠在规定的时间内走到粮仓处。 基本要求&#xff1a; ⑴老鼠形象可以辨认&#xff0c;可用键盘操纵老鼠上下…

小米万兆路由器里的Docker安装可道云(Kodexplorer)私有网盘

小米2022年12月份发布了万兆路由器&#xff0c;里面可以使用Docker。 今天尝试在小米的万兆路由器里安装可道云(Kodexplorer)私有网盘。 准备工作 先将一块USB外接硬盘格式化为ext4格式&#xff0c;然后外接到小米路由器上&#xff0c; 然后创建虚拟内存&#xff0c;我这里最…

Vectornet源码详解

代码资源地址见最后 1.数据与环境配置 首先,我们需要下载这个API,方便对数据做特征工程,目前这个api还不支持windows版本 https://github.com/argoverse/argoverse-api 按照上面的步骤所说,第一步需要下载这个项目,第二步需要下载一些配置文件,并按照上述方…

Linux 信号处理简析

1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 分析背景 本文基于 ARM32 架构 Linux 4.14 内核源码进行分析。 3. 信号概述 3.1 信号分类 信号这个概念&#xff0c;起始于 UNIX 操作系统&…

4、程序计数器PC

介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。这里&#xff0c;并非是广义上所指的物理寄存器&#xff0c;或许…

XINDOO的2022年年终总结

已经好几个月没有认认真真写一篇博客了&#xff0c;借着年底静下心来认认真真写一篇年终总结&#xff0c;这也是我人生中第10篇的年终总结了。 先看下去年立的flag&#xff0c;不用想去年立的flag一个都没完成。首先1 算是勉强及格&#xff1b;2 redis的博客一篇没写&#xff1…

前端学习第二站——JavaScript

目录 1. 简介 2. 变量与数据类型 2.1 声明变量 2.2 基本类型 2.3 对象类型 2.3.1 函数 Function ​ 2.3.2 数组Array 2.3.3 对象 Object ⭐️⭐️ 3. 运算符和表达式 1) 2) || 4) ... 5) [] {} 4. 控制语句 1) for in 2) for of 3) try catch 1. 简介 JavaScr…

移动应用安全过去及未来发展情况思考汇总

声明 本文是学习移动安全总结 2019. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 序言 随着2019年的逝去&#xff0c;二十一世纪第二个十年也已随之结束。回顾过去的十年&#xff0c;我们的生活随着科技的进步发生了翻天覆地的变化&#x…

MySQL索引最佳实践及Explain详解

一、Explain工具介绍 使用EXPLAIN关键字可以模拟优化器执行SQL语句&#xff0c;分析你的查询语句或是结构的性能瓶颈在 select 语句之前增加 explain 关键字&#xff0c;MySQL 会在查询上设置一个标记&#xff0c;执行查询会返回执行计划的信息&#xff0c;而不是执行这条SQL …

leetcode 169. 多数元素-java题解

题目所属分类 超经典问题 可以选用投票法 原题链接 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 代码案例&#xff1a;输入…

java 瑞吉外卖优化day2 读写分离 ShardingJDBC

问题分析&#xff1a; mysql主从复制 介绍&#xff1a; 补充&#xff1a;从库可以有多个 提前准备好两台服务器&#xff0c;分别安装Mysql并启动服务成功 主库Master 192.168.138.100 从库Slave 192.168.138.101 Window系统则是在my.ini文件里直接配置 mysql -uroot -…

Day 16-Vue3 技术_新的组件

1.Fragment —— 片段组件 在Vue2中: 组件必须有一个根标签。 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个内置Fragment虚拟元素中&#xff0c; 最后是不参与页面渲染的&#xff01; 好处: 减少标签层级, 减小内存占用。 <template><fragment>…

UART实验

目录 一、UART 1.1简介 1.2并行通信和串行通信 1.3单工和双工 1.4波特率 1.5UART帧格式 1.6UART硬件连接 1.7UART控制器 二、Exynos4412的UART控制器 三、UART寄存器 四、UART编程 五、输入输出重定向 六、使用串口控制LED 一、UART 1.1简介 Universal Asynch…

Elasticsearch学习

基本概念 运维角度物理概念 分片&#xff08;shard&#xff09;&#xff1a;一个索引所占用的物理空间分配 primary shard&#xff1a;解决数据水平扩展问题&#xff0c;分片数据量过大时&#xff0c;可以通过增加DataNode节点扩容。一个主分片等于一个lucene实例。创建索引时…

windows认证之本地认证

windows认证包括本地认证、网络认证和域认证三个部分windows认证和密码的抓取可以说是内网渗透的第一步。 1、window认证流程 Windows的登陆密码是储存在系统本地的SAM文件中的&#xff0c;在登陆Windows的时候&#xff0c;系统会将用户输入的密码与 SAM文件中的密码进行对比&…

JavaScript基础系列之引用类型细节总结

1. 前言 这里不罗列 API&#xff0c;如果需要 API 可以自行查询。只会强调一些不起眼但是很重要的细节问题 2. Object 2.1 生成对象几种方式&#xff1a; 曾经被面试过哦&#xff0c; 虽然很基础&#xff0c;但是划重&#xff01;&#xff01;&#xff01; const obj new Obj…

【机器学习】线性回归(理论)

线性回归&#xff08;理论&#xff09; 目录一、概论1、何为线性回归2、问题的抽象3、误差的引入4、极大似然估计的引入5、目标函数的优化二、梯度下降1、何为梯度下降2、利用梯度下降进行函数寻优3、梯度下降的一些问题Ⅰ 迭代步长Ⅱ 算法的初始位置Ⅲ 数据的取值范围差异Ⅳ 鞍…

i.MX8MP平台开发分享(IOMUX篇)- uboot驱动

专栏目录:专栏目录传送门 平台内核i.MX8MP5.15.71文章目录 1. pinfunc.h2.iomux驱动3.pinctrl_select_state_full4.imx_pinctrl_set_state1. pinfunc.h pinfunc.h中定义了所有的引脚,命名方式是MX8MP_IOMUXC___,例如下面的MX8MP_IOMUXC_GPIO1_IO00__GPIO1_IO00定义了MUX寄存…

网络信息安全-LSB图像隐写与检测的设计实现

任务目标&#xff1a; 本选题需要学习经典的图像信息隐藏算法&#xff0c;包括基于空域的隐写算法和数字水印算法。 接着你将使用某种编程语言实现这些算法&#xff0c;实现在图片中嵌入一些信息&#xff0c;例如字符串和一些 文件。除此之外&#xff0c;还需要尝试一些基础的…

吴恩达《机器学习》——Logistic多分类与神经网络

Logistic多分类与神经网络1. MINIST数据集与Logistic多分类MINIST简介数据集可视化Logistic如何实现多分类&#xff1f;One-Hot向量Python实现2. 神经网络(Neural Network, NN)神经网络前馈传播Python实现3. 基于PyTorch框架的网络搭建数据集、源文件可以在Github项目中获得 链…