[C++]类的继承

news2024/12/19 22:38:34

一、什么是继承

1.定义

        在 C++ 中,继承是一种机制,允许一个类(派生类)继承另一个类(基类)的成员(数据和函数)。继承使得派生类能够直接访问基类的公有和保护成员,同时也可以对这些成员进行扩展或修改。继承是一种“是一个”的关系,它允许一个类从另一个类继承其属性和方法,从而实现代码的复用。派生类是基类的一种特殊类型。

如何理解“是一个”?

如"狗是动物","猫是动物",Cat,Dog这两个类都继承了Animal这个类,它们是属于的关系,是“是一个”的关系。

2.继承的作用

        继承的主要作用是实现代码的复用:派生类可以重用基类的代码,而不需要重复编写相同的功能。同时,继承也允许派生类扩展或修改基类的行为。


二、基类与派生类

1.基类

        定义通用功能的类,供其他类继承。

2.派生类

        从基类继承的类,可以继承、扩展或修改基类的功能。

3.简单示例

        用一个简单的动物类和狗类示例,展示基类和派生类的关系。

#include <iostream>
using namespace std;

// 基类:动物
class Animal {
public:
    void eat() { 
        cout << "Animal is eating" << endl; 
    }
};

// 派生类:狗(继承了动物的功能)
class Dog : public Animal {
public:
    void bark() { 
        cout << "Dog is barking" << endl; 
    }
};

int main() {
    Dog dog;
    dog.eat(); // 狗继承了动物的吃饭功能
    dog.bark(); // 狗有自己的叫的功能
    return 0;
}

三、继承的访问控制

1.公有继承

        当派生类以 public 方式继承基类时,基类的公有成员和保护成员在派生类中保持原有的访问权限,而私有成员完全不可访问。

1.公有成员(public):可以通过派生类对象访问。

2.保护成员(protected):可以在派生类内部访问,但不能通过派生类对象直接访问。

3.私有成员(private):完全无法访问。

4.示例:

#include <iostream>
using namespace std;

class Animal {
public:
    void eat() { cout << "Animal eats." << endl; }  // 公有成员
protected:
    void sleep() { cout << "Animal sleeps." << endl; }  // 保护成员
private:
    void walk() { cout << "Animal walks." << endl; }  // 私有成员
};

class Dog : public Animal {  // 公有继承
public:
    void dogActions() {
        eat();   // 可以访问公有成员
        sleep(); // 可以访问保护成员
        // walk(); // 错误,不能访问私有成员
    }
};

int main() {
    Dog d;
    d.dogActions();
    return 0;
}
/*输出:
Animal eats.
Animal sleeps.
*/
1.解释:
  • 由于继承方式是 publicDog 类可以访问基类 Animal 的公有成员 eat() 和保护成员 sleep()
  • 但是,Dog 类不能访问基类 Animal 的私有成员 walk()

2.保护继承

        当派生类以 protected 方式继承基类时,基类的公有成员和保护成员都变成保护成员,只能在派生类及其子类中访问,不能通过派生类对象直接访问。

1.公有成员(public)变为保护成员(protected)。

2.保护成员(protected)仍然是保护成员。

3.私有成员(private)完全不可访问。

4.示例:

#include <iostream>
using namespace std;

class Animal {
public:
    void eat() { cout << "Animal eats." << endl; }  // 公有成员
protected:
    void sleep() { cout << "Animal sleeps." << endl; }  // 保护成员
private:
    void walk() { cout << "Animal walks." << endl; }  // 私有成员
};

class Dog : protected Animal {  // 保护继承
public:
    void dogActions() {
        eat();   // 可以访问保护成员
        sleep(); // 可以访问保护成员
        // walk(); // 错误,不能访问私有成员
    }
};

int main() {
    Dog d;
    d.dogActions();
    // d.eat(); // 错误,不能通过对象访问 public 成员
    return 0;
}
/*输出:
Animal eats.
Animal sleeps.
*/
1.解释:
  • 由于继承方式是 protectedDog 类可以访问基类 Animal 的公有成员 eat() 和保护成员 sleep(),但是这些成员不能通过派生类对象直接访问。
  • Dog 类不能访问 Animal 类的私有成员 walk()

3.私有继承

        当派生类以 private 方式继承基类时,基类的公有成员和保护成员都变成私有成员,只能在派生类内部访问,不能通过派生类对象访问。

1.公有成员(public)变为私有成员(private)。

2.保护成员(protected)变为私有成员(private)。

3.私有成员(private)仍然不可访问。

4.示例:

#include <iostream>
using namespace std;

class Animal {
public:
    void eat() { cout << "Animal eats." << endl; }  // 公有成员
protected:
    void sleep() { cout << "Animal sleeps." << endl; }  // 保护成员
private:
    void walk() { cout << "Animal walks." << endl; }  // 私有成员
};

class Dog : private Animal {  // 私有继承
public:
    void dogActions() {
        eat();   // 可以访问私有成员(但只能在类内部访问)
        sleep(); // 可以访问保护成员(但只能在类内部访问)
        // walk(); // 错误,不能访问私有成员
    }
};

int main() {
    Dog d;
    d.dogActions();//通过派生类的成员函数访问基类的成员函数
    // d.eat(); // 错误,不能通过对象访问 private 成员
    return 0;
}
/*输出:
Animal eats.
Animal sleeps.
*/
1.解释:
  • 由于继承方式是 privateDog 类可以访问基类 Animal 的公有成员 eat() 和保护成员 sleep(),但是这些成员都变成了 private,所以不能通过派生类对象直接访问。
  • 由于 eat()sleep()Dog 类的成员函数中被调用,它们可以访问 Animal 类的公有成员和保护成员。
  • 但是,如果尝试从 Dog 类的对象外部调用 eat()sleep()(如 d.eat()d.sleep()),就会报错,因为它们被转换成了私有或保护成员,不能通过外部代码直接访问。但因为Dog类的dogActions()方法是对外公开的,而这个dogActions()方法可以访问到基类的eat()和sleep()方法,因此可以通过Dog类的公有成员方法间接访问到基类的eat和sleep方法。
  • 类外Dog 类不能访问 Animal 类的私有成员 walk()

4. 总结继承的访问控制

继承方式基类 public 成员基类 protected 成员基类 private 成员
public保持 public保持 protected不能访问
protected变为 protected变为 protected不能访问
private变为 private变为 private不能访问

四、 抽象类与纯虚函数

  • 抽象类:一个不能直接实例化的类,通常包含纯虚函数。
  • 纯虚函数:没有实现的函数,要求派生类实现。
  • 示例:展示如何定义抽象类,并解释其在接口设计中的应用。
    class Animal {
    public:
        virtual void sound() = 0;  // 纯虚函数
    };
    
    class Dog : public Animal {
    public:
        void sound() override { std::cout << "Bark" << std::endl; }
    };
    

关于抽象类与纯虚函数我的这篇笔记里有详细讲,大家可以转站这里


五、多级继承与多重继承

1.多级继承

1. 定义

多级继承是指类的继承关系形成一个层次结构(是逐级进行的,类与类之间有明确的层级关系,每一层只能继承一个父类),其中派生类不仅继承了基类的成员,还继承了另外一个派生类的成员。

具体来说,就是:

  1. 基类(祖父类):最顶层的类。
  2. 派生类(父类):继承自基类的类。
  3. 子类(孙子类):继承自派生类(父类)的类。

在这种情况下,子类(孙子类)不仅继承了派生类(父类)的成员,还间接继承了基类(祖父类)的成员。子孙类的继承就叫多级继承。

例如:

#include<iostream>
using namespace std;

class A {  // 基类
public:
    void showA() { cout << "Class A" << endl; }
};

class B : public A {  // 派生类 B 继承自 A
public:
    void showB() { cout << "Class B" << endl; }
};

class C : public B {  // 子类 C 继承自 B(间接继承自 A)
public:
    void showC() { cout << "Class C" << endl; }
};

int main() {
    C obj;
    obj.showA();  // 通过 C 访问 A(通过继承的方式)
    obj.showB();  // 通过 C 访问 B
    obj.showC();  // 通过 C 访问 C
    return 0;
}

/*输出
    Class A
    Class B
    Class C
*/

在这个例子中:

  • A 是基类。
  • B 是派生类,它直接继承自 A
  • C 是子类,它继承自 B,但间接继承了 A 的成员。
  • C 类继承自 B 类,B 类又继承自 A 类。因此,C 类间接继承了 A 类的成员,这就是“继承链条中,派生类继续继承另一个派生类,形成一个层次结构”。
  • 子类(如 C)不仅可以访问直接继承的父类(如 B)的成员,还可以访问间接继承的祖父类(如 A)的成员。

2. 特性

  • 在多级继承中,继承链条从基类开始,一层一层向下继承。
  • 子类可以直接访问祖先类的公有成员,但需要注意继承的访问权限。例如,C 类可以通过继承链访问 A 类的公有成员。

3. 访问控制

  • 基类成员的访问控制(公有、保护、私有)会影响到继承链条中的派生类对基类成员的访问。
  • 在多级继承中,派生类不仅能访问自己类的成员,还能访问祖先类的公有成员和保护成员。

2.多重继承

1. 定义

多重继承是指一个类可以同时继承多个基类。也就是说,派生类可以同时继承多个父类的成员,具有多个基类。这是 C++ 允许的继承方式。例如:

class A {
public:
    void showA() { cout << "Class A" << endl; }
};

class B {
public:
    void showB() { cout << "Class B" << endl; }
};

class C : public A, public B {  // C 同时继承 A 和 B
public:
    void showC() { cout << "Class C" << endl; }
};

在这个例子中:

  • C 类继承了 A 类和 B 类,意味着 C 类将拥有 AB 类的成员。、

2. 特性

  • 派生类可以有多个直接的父类。
  • 每个父类的成员都可以被派生类访问。
  • 可能会发生命名冲突,如果多个父类有同名的成员,派生类需要通过作用域解析符来明确指定使用哪个父类的成员。
  • 如果两个基类有相同的成员,可能会出现钻石问题,但可以通过虚拟继承解决。

3. 访问控制

  • 和多级继承类似,多重继承中基类成员的访问控制(公有、保护、私有)依然适用。
  • 如果两个基类有同名的成员(钻石问题),在派生类中需要通过作用域解析符来区分它们。

4. 实例

#include <iostream>
using namespace std;

class A {
public:
    void showA() { cout << "Class A" << endl; }
};

class B {
public:
    void showB() { cout << "Class B" << endl; }
};

class C : public A, public B {
public:
    void showC() { cout << "Class C" << endl; }
};

int main() {
    C obj;
    obj.showA();  // 通过 C 访问 A
    obj.showB();  // 通过 C 访问 B
    obj.showC();  // 通过 C 访问 C
    return 0;
}
//输出:Class A Class B Class C

在这个例子中,C 类继承了 AB 两个类,所以它可以直接访问 AB 的公有成员。

5. 潜在的问题(如钻石问题)

多重继承可能导致一些潜在问题,最典型的就是 钻石问题。例如,如果两个基类有相同的成员,派生类可能无法明确继承哪个成员。C++ 通过 虚拟继承 来解决这个问题。

1.钻石问题的例子
#include <iostream>
using namespace std;

class A {
public:
    void showA() { cout << "Class A" << endl; }
};

class B : public A {
public:
    void showB() { cout << "Class B" << endl; }
};

class C : public A {
public:
    void showC() { cout << "Class C" << endl; }
};

class D : public B, public C {  // D 同时继承 B 和 C
public:
    void showD() { cout << "Class D" << endl; }
};

int main() {
    D obj;
    obj.showA();  // 错误:不明确的继承(钻石问题)
    return 0;
}

在这个例子中,D 类继承了 BC,而 BC 都继承了 A 类。这样,D 类有两个 A 类的副本,因此不明确的继承会导致编译错误。

2.解决钻石问题:虚拟继承

通过使用虚拟继承(virtual 关键字),C++ 会确保基类 A 只有一个副本。

class A {
public:
    void showA() { cout << "Class A" << endl; }
};

class B : virtual public A {
public:
    void showB() { cout << "Class B" << endl; }
};

class C : virtual public A {
public:
    void showC() { cout << "Class C" << endl; }
};

class D : public B, public C {
public:
    void showD() { cout << "Class D" << endl; }
};

int main() {
    D obj;
    obj.showA();  // 现在可以访问 A,因为 A 只有一个副本
    return 0;
}
//输出:Class A

3.总结

1. 多级继承

  • 一种继承方式,子类继承父类,父类又继承祖父类,形成继承链条。
  • 子类能够访问基类和祖父类的成员(根据访问权限)。

2. 多重继承

  • 一个子类可以继承多个父类。
  • 如果多个父类有同名成员,可能会导致命名冲突(钻石问题),需要使用作用域解析符来明确调用。
  • 可以通过虚拟继承来避免钻石问题。
特性多级继承多重继承
继承关系逐级的,类与类之间形成一个明确的层级关系类同时继承多个父类,父类之间不一定有层级关系
父类数量每一层只有一个父类一个派生类可以有多个直接的父类
结构继承链是单向的,层次化的继承关系是并列的,可以有多个父类
命名冲突不容易发生命名冲突如果父类有相同成员,可能会发生命名冲突
例子class C : public B { ... }class B : public A { ... }class C : public A, public B { ... }

六、虚函数与多态

  • 虚函数:基类中的函数被声明为虚函数时,派生类可以重写该函数,实现运行时多态。
  • 多态:解释静态多态和动态多态的区别。
  • 示例:展示虚函数如何实现动态绑定,通过基类指针或引用调用派生类的函数。
    class Animal {
    public:
        virtual void sound() { std::cout << "Animal makes a sound" << std::endl; }
    };
    
    class Dog : public Animal {
    public:
        void sound() override { std::cout << "Dog barks" << std::endl; }
    };
    
    int main() {
        Animal* animal = new Dog();
        animal->sound();  // 动态绑定,调用 Dog 类中的 sound()
        delete animal;
        return 0;
    }
    

虚函数的详细笔记 

多态的详细笔记


七、继承中的构造函数与析构函数

1. 构造函数的继承行为

基本规则:
  • 派生类的构造函数会调用基类的构造函数。这意味着在派生类的构造函数中,基类的构造函数会先被调用,然后才会执行派生类的构造代码。
  • 基类的构造函数被自动调用,即使你没有显式调用它。默认情况下,如果基类有无参构造函数,那么它会在派生类构造函数中自动调用。
  • 如果基类有带参构造函数,派生类必须显式调用基类的构造函数(使用初始化列表)。
示例:构造函数继承行为
#include <iostream>
using namespace std;

class Base {
public:
    Base() {  // 无参构造函数
        cout << "Base class constructor" << endl;
    }
    
    Base(int x) {  // 带参构造函数
        cout << "Base class constructor with value: " << x << endl;
    }
};

class Derived : public Base {
public:
    Derived() : Base(10) {  // 显式调用基类的构造函数
        cout << "Derived class constructor" << endl;
    }
};

int main() {
    Derived d;  // 创建派生类对象
    return 0;
}

/*输出:
Base class constructor with value: 10
Derived class constructor*/

解释:

  • 当创建派生类对象 d 时,首先会调用基类 Base 的构造函数(带参构造函数 Base(int x)),并传入参数 10,然后才会执行派生类的构造函数。

2. 析构函数的继承行为

基本规则:
  • 派生类的析构函数会先执行。当对象销毁时,析构的顺序是:首先调用派生类的析构函数,然后再调用基类的析构函数。
  • 基类的析构函数应该是虚拟的:
    • 这是为了确保正确地析构派生类对象,避免资源泄漏。当你有一个基类和一个派生类,并且你通过基类指针去删除派生类对象时,如果基类的析构函数不是虚拟的,那么在删除对象时,程序就只会执行基类的析构函数派生类的析构函数就不会执行
    • 这种情况下,派生类对象在销毁时可能会留下未释放的资源(比如动态分配的内存、打开的文件或其他重要的资源),导致资源泄漏,即这些资源被占用但没有被正确释放。
    • 而如果基类的析构函数是虚拟的,程序就知道在删除派生类对象时,先执行派生类的析构函数,释放派生类分配的资源,然后再执行基类的析构函数,释放基类的资源。这样就能确保资源得到完全释放,避免浪费
示例:析构函数继承行为
#include <iostream>
using namespace std;

class Base {
public:
    Base() {
        cout << "Base class constructor" << endl;
    }

    ~Base() {  // 基类的析构函数
        cout << "Base class destructor" << endl;
    }
};

class Derived : public Base {
public:
    Derived() {
        cout << "Derived class constructor" << endl;
    }

    ~Derived() {  // 派生类的析构函数
        cout << "Derived class destructor" << endl;
    }
};

int main() {
    Derived d;  // 创建派生类对象
    return 0;
}

/*输出
Base class constructor
Derived class constructor
Derived class destructor
Base class destructor
*/

解释:

  • 创建对象 d 时,首先调用基类的构造函数,然后调用派生类的构造函数。
  • d 被销毁时,先调用派生类的析构函数,再调用基类的析构函数。
虚拟析构函数

在多态情况下,派生类对象是通过基类指针删除的,这时必须将基类的析构函数声明为虚拟的,以确保派生类的析构函数得到调用。否则,可能会导致资源泄漏。

#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() {  // 基类的虚拟析构函数
        cout << "Base class destructor" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {  // 派生类的析构函数
        cout << "Derived class destructor" << endl;
    }
};

int main() {
    Base* b = new Derived();  // 基类指针指向派生类对象
    delete b;  // 通过基类指针删除派生类对象
    return 0;
}

/*
Derived class destructor
Base class destructor
*/

解释:

  • delete b 时,基类的虚拟析构函数确保先调用派生类的析构函数,然后再调用基类的析构函数。否则,如果基类的析构函数没有声明为虚拟的,就只会调用基类的析构函数,导致派生类的析构函数没有执行,可能造成资源泄漏。

八、继承与组合的比较

1.组合

1.定义:

组合是表示类之间**“有一个(has-a)”**关系的机制。它通过将一个类的对象作为另一个类的成员来实现功能复用。

2.特点:

  • 组合实现了类与类之间的松散耦合关系。
  • 被组合的对象可以是其他类的实例。

3.组合与继承的区别:

  • 继承:适用于表示 "是一个" 关系的情况。
  • 组合:适用于表示 "有一个" 关系的情况。类之间通过成员对象组合实现功能。

4.示例

#include <iostream>
using namespace std;

class Leg {
public:
    void walk() {
        cout << "腿在跑。" << endl;
    }
};

class Dog {
private:
    Leg leg;  // Dog 有一个 Leg
public:
    void walk() {
        leg.walk();  // 通过组合对象调用其功能
    }
};

int main() {
    Dog dog;
    dog.walk(); // 调用组合对象的方法
    return 0;
}

2.继承 vs 组合:如何选择?

1.组合与继承的对比

方面继承组合
关系类型是一个(is-a)有一个(has-a)
耦合程度紧密耦合松散耦合
灵活性低(子类依赖父类)高(可以动态修改组合关系)
代码复用子类复用父类代码通过组合成员实现功能复用
使用场景表示类之间是一种“类型”的关系,如动物与狗表示类之间有一个成员的关系,如狗与腿

2. 综合示例

下面是继承和组合的综合使用示例,展示它们的区别和使用场景:

#include <iostream>
using namespace std;

// 父类:动物
class Animal {
public:
    void eat() {
        cout << "Animal is eating." << endl;
    }
};

// 组合类:腿
class Leg {
public:
    void walk() {
        cout << "Leg is walking." << endl;
    }
};

// 子类:狗
class Dog : public Animal { // 继承:狗是动物
private:
    Leg leg; // 组合:狗有一个腿
public:
    void walk() {
        leg.walk(); // 使用组合对象的功能
    }
    void bark() {
        cout << "Dog barks!" << endl;
    }
};

int main() {
    Dog dog;
    dog.eat();  // 继承自 Animal
    dog.walk(); // 组合的功能
    dog.bark(); // Dog 类的独特功能
    return 0;
}

3.最后:

  • 在设计复杂系统时,应尽量优先使用组合而非继承,因为组合更加灵活并且降低了类之间的耦合性。
  • 如果需要扩展父类的功能或表示一种“类型”的关系,则继承是合理的选择。

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

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

相关文章

Docker 用法详解

文章目录 一、Docker 快速入门1.1 部署 MYSQL1.2 命令解读&#xff1a; 二、Docker 基础2.1 常见命令&#xff1a;2.1.1 命令介绍&#xff1a;2.1.2 演示&#xff1a;2.1.3 命令别名&#xff1a; 2.2 数据卷&#xff1a;2.2.1 数据卷简介&#xff1a;2.2.2 数据卷命令&#xff…

【自动化】Python SeleniumUtil 油猴 工具 自动安装用户脚本

【自动化】Python SeleniumUtil 油猴 工具 【自动化】Python SeleniumUtil 工具-CSDN博客【自动化】Python SeleniumUtil 工具。https://blog.csdn.net/G971005287W/article/details/144565691 油猴工具 import timefrom selenium.webdriver.support.wait import WebDriverW…

盛元广通畜牧与水产品检验技术研究所LIMS系统

一、系统概述 盛元广通畜牧与水产品检验技术研究所LIMS系统集成了检测流程管理、样品管理、仪器设备管理、质量控制、数据记录与分析、合规性管理等功能于一体&#xff0c;能够帮助实验室实现全流程的数字化管理。在水产、畜牧产品的质检实验室中&#xff0c;LIMS系统通过引入…

clickhouse-数据库引擎

1、数据库引擎和表引擎 数据库引擎默认是Ordinary&#xff0c;在这种数据库下面的表可以是任意类型引擎。 生产环境中常用的表引擎是MergeTree系列&#xff0c;也是官方主推的引擎。 MergeTree是基础引擎&#xff0c;有主键索引、数据分区、数据副本、数据采样、删除和修改等功…

GEE+本地XGboot分类

GEE本地XGboot分类 我想做提取耕地提取&#xff0c;想到了一篇董金玮老师的一篇论文&#xff0c;这个论文是先提取的耕地&#xff0c;再做作物分类&#xff0c;耕地的提取代码是开源的。 但这个代码直接在云端上进行分类&#xff0c;GEE会爆内存&#xff0c;因此我准备把数据下…

Docker搭建kafka环境

系统&#xff1a;MacOS Sonoma 14.1 Docker版本&#xff1a;Docker version 27.3.1, build ce12230 Docker desktop版本&#xff1a;Docker Desktop 4.36.0 (175267) 1.拉取镜像 先打开Docker Desktop&#xff0c;然后在终端执行命令 docker pull lensesio/fast-data-dev …

校园点餐订餐外卖跑腿Java源码

简介&#xff1a; 一个非常实用的校园外卖系统&#xff0c;基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化&#xff0c;提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合&am…

Linux文件属性 --- 硬链接、所有者、所属组

三、硬链接数 1.目录 使用“ll”命令查看&#xff0c;在文件权限的后面有一列数字&#xff0c;这是文件的硬链接数。 对于目录&#xff0c;硬链接的数量是它具有的直接子目录的数量加上其父目录和自身。 下图的“qwe”目录就是“abc”目录的直接子目录。 2.文件 对于文件可…

Centos7 部署ZLMediakit

1、拉取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init 2、安装编译器 sudo yum -y install gcc 3、安装cmake sudo yum -y install cmake 4…

无管理员权限 LCU auth-token、port 获取(全网首发 go)

一&#xff1a; 提要&#xff1a; 参考项目&#xff1a; https://github.com/Zzaphkiel/Seraphine 想做一个 lol 查战绩的软件&#xff0c;并且满足自己的需求&#xff08;把混子和大爹都表示出来&#xff09;&#xff0c;做的第一步就是获取 lcu token &#xff0c;网上清一色…

《云原生安全攻防》-- K8s安全框架:认证、鉴权与准入控制

从本节课程开始&#xff0c;我们将来介绍K8s安全框架&#xff0c;这是保障K8s集群安全比较关键的安全机制。接下来&#xff0c;让我们一起来探索K8s安全框架的运行机制。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; K8s安全框架&#xff1a;由认证、鉴权和准入控…

spring\strust\springboot\isp前后端那些事儿

后端 一. 插入\更新一条数据&#xff08;老&#xff09; Map<String, Object> parameterMap MybatisUtil.initParameterSave("Send_ProjectFrozenLog", sendProjectFrozenLog); commonMapper.insert(parameterMap);parameterMap MybatisUtil.initParameter…

UE5安装Fab插件

今天才知道原来Fab也有类似Quixel Bridge的插件&#xff0c;于是立马就安装上了&#xff0c;这里分享一下安装方法 在Epic客户端 - 库 - Fab Library 搜索 Fab 即可安装Fab插件 然后重启引擎&#xff0c;在插件面板勾选即可 然后在窗口这就有了 引擎左下角也会多出一个Fab图标…

对于使用exe4j打包,出现“NoClassDefFoundError: BOOT-INF/classes”的解决方案

jar使用exe4j打包exe&#xff0c;出现NoClassDefFoundError: BOOT-INF/classes 注意选取的jar包是使用build&#xff0c;而不是maven中的install 本文介绍解决这个方法的方案 点击Project Structure 按照如图所示选择 选择main class&#xff0c;选择你要打的main 如果遇到/M…

文件上传之文件内容检测

一.基本概念 介绍&#xff1a;文件内容检测就是检测上传的文件里的内容。 文件幻数检测 通常情况下&#xff0c;通过判断前10个字节&#xff0c;基本就能判断出一个文件的真实类型。 文件加载检测 一般是调用API或函数对文件进行加载测试。常见的是图像渲染测试&#xff0c;再…

WebSpoon9.0(KETTLE的WEB版本)编译 + tomcatdocker部署 + 远程调试教程

前言 Kettle简介 Kettle是一款国外开源的ETL工具&#xff0c;纯Java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;绿色无需安装&#xff0c;数据抽取高效稳定 WebSpoon是Kettle的Web版本&#xff0c;由Kettle社区维护&#xff0c;不受Pentaho支持&#xff0c;…

搭建Tomcat(三)---重写service方法

目录 引入 一、在Java中创建一个新的空项目&#xff08;初步搭建&#xff09; 问题&#xff1a; 要求在tomcat软件包下的MyTomcat类中编写main文件&#xff0c;实现在MyTomcat中扫描myweb软件包中的所有Java文件&#xff0c;并返回“WebServlet(url"myFirst")”中…

CAN配置---波特率中断引脚等---autochips-AC7811-ARM-M3内核

1、配置工具 虽然不怎么好用&#xff0c;但比没有强多了。具体看图&#xff1a; 时钟选着 NVIC配置 GPIO配置 2、生成的具体配置信息 NXP的配置工具里面&#xff0c;具体的波特率可以直接显示&#xff0c;这个工具没有&#xff0c;怎么办&#xff1f; 它放到了生成的代码里面…

matlab Patten的使用(重要)(Matlab处理字符串一)

原文连接&#xff1a;https://www.mathworks.com/help/releases/R2022b/matlab/ref/pattern.html?browserF1help 能使用的搜索函数&#xff1a; contains确定字符串中是否有模式matches确定模式是否与字符串匹配count计算字符串中模式的出现次数endsWith确定字符串是否以模式…

Docker创建一个mongodb实例,并用springboot连接 mongodb进行读写文件

一、通过Docker 进行运行一个 mongodb实例 1、拉取镜像 docker pull mongo:5.0.5 2、创建 mongodb容器实例 docker run -d --name mongodb2 \-e MONGO_INITDB_ROOT_USERNAMEsalaryMongo \-e MONGO_INITDB_ROOT_PASSWORD123456 \-p 27017:27017 \mongo:5.0.5 3、进入容器&am…