学习c++的第十一天

目录

继承和派生

基类 & 派生类

访问控制和继承

派生类的构造函数

派生类的析构函数

继承类型

多继承

重载运算符和重载函数

函数重载

运算符重载

可重载运算符/不可重载运算符

运算符重载实例


继承和派生

先来说继承,这与现实生活中的继承意思差不多,比如一个人继承另一个人的财产、以及子承父业等等是一个意思,拥有的这个过程就叫做继承。同样,在C++中,比如有两个类,新类拥有原有类的全部属性叫做继承!原有类产生新类的过程叫做派生!而我们把原有的这个类称之为父类基类,由基类派生出的类叫做派生类或者叫做子类。大家从名字上就可以看出他们的关系。

// 基类
class Animal {// eat() 函数// sleep() 函数
};//派生类
class Dog : public Animal {// bark() 函数
};

 那么继承和派生有什么好处呢?为什么C++要有这种机制呢?

  1. 体现面向对象的编程思想,更好的表达各类型之间的关系。
  2. 派生类除了可以继承基类的全部信息外,还可以添加自己的那些不同的、有差异的信息,就像生物进化的道理一样,派生类在拥有基类的全部基础之上还将更强大。
  3. 派生类继承到基类的成员是自动、隐藏的拥有,即不需要我们重新定义,这就节省了大量的代码,体现了代码重用的软件工程思想。
  4. 继承和派生可以实现代码的模块化和分层,使得程序的结构更加清晰,易于理解和维护。通过继承和派生,我们可以将相似的代码抽象出来,形成一个基类,然后派生出不同的子类,分别实现各自的特定行为。

  5. 继承和派生可以实现多态性,使得程序的功能更加灵活和可扩展。通过基类的指针或引用,我们可以调用派生类的方法,从而实现对不同类型对象的统一操作。

  6. C++引入继承和派生机制,是为了继承C语言的优点,同时弥补其不足。C语言没有面向对象的概念,缺乏代码重用和抽象能力。通过继承和派生,C++可以更好地实现抽象、封装和多态等面向对象编程的特性。

基类 & 派生类

基类和派生类是面向对象编程中的两个重要概念。基类是指在继承关系中处于上层的、最通用的类,它定义了一组通用的属性和方法,派生类则是指在继承关系中处于下层、更具体的类,它从基类继承通用的属性和方法,并可以添加自己的特定属性和方法。

基类的概念源于面向对象编程的封装和抽象原则,其目的是将相似或相关的代码抽象出来,形成一个通用的类,避免代码冗余,提高代码的可维护性和可扩展性。基类通常包含一些数据成员和函数成员,而且这些成员的访问权限可以是public、protected或private。

派生类则是基于基类构建的,通过继承和派生机制,它可以从基类继承代码和数据,但也可以添加自己的特有信息。派生类可以覆盖或增加基类成员函数,重载基类成员函数,添加新的成员函数,以及定义新的数据成员等。派生类可以进一步派生出更加具体的子类,形成类似于树状结构的类层次体系。形式如下:

class derived-class: access-specifier base-class

其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。 

在C++中,使用关键字class或struct定义一个类,它可以从一个或多个基类继承,例如:

class Animal {
public:void eat();void move();
protected:int age;
};class Mammal : public Animal {
public:void giveBirth();void nurse();
private:int numOfPaws;
};class Dog : public Mammal {
public:void bark();
private:string breed;
};

在上面的例子中,Animal是一个基类,它包含两个公共成员函数和一个保护的数据成员age。Mammal是一个派生类,它从Animal继承了eat()、move()和age成员,并添加了giveBirth()、nurse()和numOfPaws成员。Dog是Mammal的派生类,它除了继承Mammal的所有成员之外,还添加了一个私有成员breed和一个公共成员函数bark()。

基类和派生类的概念是面向对象编程中非常重要的基础,它们提供了代码重用、层次化组织、多态性等重要特性。

访问控制和继承

派生类可以访问基类中的所有非私有成员,这意味着基类成员如果不想被派生类的成员函数访问,应该将其声明为私有(private)。

根据访问权限,可以总结出以下不同的访问类型:

  1. 同一个类:在同一个类中,所有成员(无论是public、protected还是private)都是可以访问的。这意味着类中的任何成员函数都可以访问该类的所有成员。

  2. 派生类:派生类可以访问基类中所有的非私有(public和protected)成员,包括变量和函数。但是,对于基类中声明为私有(private)的成员,在派生类中是无法直接访问的。

  3. 外部的类:外部的类只能访问基类中声明为公共(public)的成员,对于受保护(protected)和私有(private)成员是无法访问的。这意味着,如果一个类想要访问另一个类的受保护和私有成员,那么它必须通过该类的公共成员函数来实现。

此外,在派生类继承基类时,派生类会继承基类的所有方法,但有几个例外情况:

  • 基类的构造函数、析构函数和拷贝构造函数:派生类需要定义自己的构造函数、析构函数和拷贝构造函数来处理自己的特定需求,而不是直接继承基类的相应函数。
  • 基类的重载运算符:派生类可以定义自己的重载运算符,但不会继承基类的重载运算符。
  • 基类的友元函数:派生类不能访问基类的友元函数,因为友元函数只对基类可见。

以下是一个示例: 

#include<iostream>class BaseClass {
public:void publicMethod() { /* 实现代码 */ }
protected:void protectedMethod() { /* 实现代码 */ }
private:void privateMethod() { /* 实现代码 */ }
};class DerivedClass : public BaseClass {// 可以访问继承的 public 和 protected 成员public:void derivedMethod() {publicMethod();       // 可以访问继承的 public 成员protectedMethod();    // 可以访问继承的 protected 成员// privateMethod();   // 不能访问继承的 private 成员}
};int main() {DerivedClass obj;obj.publicMethod();    // 可以通过对象访问继承的 public 成员// obj.protectedMethod(); // 不能通过对象访问继承的 protected 成员// obj.privateMethod();  // 不能通过对象访问继承的 private 成员return 0;
}

在上面的示例中,DerivedClass 是一个派生类,它继承了 BaseClass 的成员函数 publicMethod() 和 protectedMethod()。在派生类中,它们保持了原有的访问权限。派生类可以在自己的成员函数中使用继承的公共和受保护的成员函数。但是,私有成员函数 privateMethod() 对于派生类来说是不可见的。 

总的来说,通过继承,派生类可以访问基类中的成员,但对于私有成员、友元函数以及一些特殊情况(构造函数、析构函数、拷贝构造函数和重载运算符),存在一些限制。理解这些概念对于正确设计和使用继承关系是非常重要的。

派生类的构造函数

派生类的构造函数是在继承了基类的构造函数的基础上,对派生类自身进行初始化的一种特殊方法。派生类的构造函数可以调用基类的构造函数来初始化从基类继承而来的成员变量和方法。同时,派生类的构造函数也可以扩展或修改基类的构造函数。

派生类的构造函数的语法形式如下:

class Derived : public Base {
public:Derived(参数列表) : Base(参数列表) {// 派生类自身操作}
};

其中,Derived 是派生类的名称,Base 是基类的名称,参数列表 是传递给构造函数的参数,: Base(参数列表) 表示调用基类的构造函数进行初始化,// 派生类自身操作 则是派生类自己的初始化代码。

需要注意的是,如果派生类没有显式地调用基类的构造函数,则编译器会自动调用基类的默认构造函数进行初始化。如果基类没有默认构造函数,或者基类的构造函数不可访问(比如是私有构造函数),则派生类必须显式地调用基类的构造函数,否则会编译错误。

此外,在使用派生类的构造函数时,还需要遵循一些规则和约定,例如构造函数的名称与类名相同,构造函数不能有返回值等。

代码示例

#include <iostream>
using namespace std;class Base {
public:Base(int a) { // 基类构造函数this->a = a;cout << "Base构造函数被调用" << endl;}void print() {cout << "Base::a = " << a << endl;}
private:int a;
};class Derived : public Base {
public:Derived(int a, int b) : Base(a) { // 派生类构造函数this->b = b;cout << "Derived构造函数被调用" << endl;}void print() {Base::print(); // 调用基类的 print 方法cout << "Derived::b = " << b << endl;}
private:int b;
};int main() {Derived d(1, 2);d.print();return 0;
}

在上面的代码中,Base 是基类,Derived 是派生类。Base 类有一个带有整型参数的构造函数,Derived 类继承了 Base 类,并添加了一个整型成员变量 b,同时还定义了一个带有两个整型参数的构造函数来初始化 b。

在 Derived 构造函数的初始化列表中,先调用了基类 Base 的构造函数来初始化从基类继承而来的成员变量 a,然后再将参数 b 赋值给派生类自身的成员变量 b。在构造函数中,输出一些调试信息,以便观察构造函数的执行情况。

在 Derived 类中,还重写了基类 Base 的 print 方法,在输出基类 Base 的成员变量 a 的值后,再输出派生类 Derived 的成员变量 b 的值。在 main 函数中,创建了一个 Derived 类型的对象 d 并调用了它的 print 方法。

当程序运行时,先输出 "Base构造函数被调用" 和 "Derived构造函数被调用",然后输出基类 Base 的成员变量 a 和派生类 Derived 的成员变量 b 的值。最终输出:

Base构造函数被调用
Derived构造函数被调用
Base::a = 1
Derived::b = 2

 总结:调用顺序是先调用基类的构造函数再调用派生类的构造函数

派生类的析构函数

派生类的析构函数是在派生类对象被销毁时调用的一种特殊成员函数。与构造函数相对应,在析构函数中可以进行清理操作,例如释放动态分配的内存、关闭文件等等。

派生类的析构函数与基类的析构函数的关系比较特殊。如果基类有虚析构函数,那么派生类的析构函数也应该是虚析构函数;如果基类没有虚析构函数,那么派生类的析构函数就不必是虚析构函数。这是因为,只有当我们使用基类的指针或引用来删除一个派生类对象时,才需要调用虚析构函数来确保正确地释放内存。

派生类的析构函数的语法形式如下:

class Derived : public Base {
public:~Derived() {// 派生类自身的清理操作}
};

以下是一个派生类析构函数的代码示例,它与前面的派生类构造函数的示例非常类似:

#include <iostream>
using namespace std;class Base {
public:Base(int a) { // 基类构造函数this->a = a;cout << "Base构造函数被调用" << endl;}virtual ~Base() { // 基类虚析构函数cout << "Base析构函数被调用" << endl;}void print() {cout << "Base::a = " << a << endl;}
private:int a;
};class Derived : public Base {
public:Derived(int a, int b) : Base(a) { // 派生类构造函数this->b = b;cout << "Derived构造函数被调用" << endl;}~Derived() { // 派生类析构函数cout << "Derived析构函数被调用" << endl;}void print() {Base::print(); // 调用基类的 print 方法cout << "Derived::b = " << b << endl;}
private:int b;
};int main() {Base* p = new Derived(1, 2); // 使用基类指针来管理派生类对象p->print();delete p; // 删除派生类对象return 0;
}

在上面的代码中,Base 和 Derived 类的定义与前面的示例相同。不同之处在于,在 Base 类中添加了一个虚析构函数,在 Derived 类中添加了一个析构函数。在 main 函数中,使用基类指针 p 来管理一个 Derived 类型的对象,并调用它的 print 方法。最后,通过 delete 运算符删除 p 指向的对象,观察析构函数的执行情况。

当程序运行时,先输出 "Base构造函数被调用" 和 "Derived构造函数被调用",然后输出基类 Base 的成员变量 a 和派生类 Derived 的成员变量 b 的值。接着,当 delete p 执行时,先调用派生类 Derived 的析构函数,再调用基类 Base 的虚析构函数,输出 "Derived析构函数被调用" 和 "Base析构函数被调用"。最终输出:

Base构造函数被调用
Derived构造函数被调用
Base::a = 1
Derived析构函数被调用
Base析构函数被调用

总结:

在创建派生类对象时,首先会调用基类的构造函数,然后再调用派生类自身的构造函数。这是因为派生类继承了基类的成员变量和成员函数,需要先初始化基类的部分,然后才能执行派生类的构造过程。因此,在构造对象时,构造函数的调用顺序是从基类到派生类。

而在销毁派生类对象时,先调用派生类的析构函数,然后再调用基类的析构函数。这是因为派生类继承了基类的资源,需要先释放派生类自身的资源,然后再释放基类的资源。因此,在销毁对象时,析构函数的调用顺序是从派生类到基类。

继承类型

在C++中,有三种基本的继承类型:公共继承、保护继承和私有继承。它们决定了派生类如何继承基类的成员。

1、公共继承(public inheritance):
公共继承是最常用的继承方式。在公共继承中,基类中的公共成员和受保护成员都会成为派生类的公共成员和受保护成员。而基类中的私有成员仍然是基类自己的私有成员,无法被派生类访问。

公共继承的语法格式为:

class DerivedClass : public BaseClass
{//...
};

2、保护继承(protected inheritance):
在保护继承中,基类中的所有成员都将成为派生类的受保护成员。这意味着,基类中的公共成员将变为派生类的受保护成员,而基类中的私有成员仍然是基类自己的私有成员,无法被派生类访问。

保护继承的语法格式为:

class DerivedClass : protected BaseClass
{//...
};

3、私有继承(private inheritance):
在私有继承中,基类中的所有成员都将成为派生类的私有成员。这意味着,基类中的公共成员和受保护成员都将变为派生类的私有成员,而基类中的私有成员仍然是基类自己的私有成员,无法被派生类访问。

私有继承的语法格式为:

class DerivedClass : private BaseClass
{//...
};

需要注意的是,无论是公共继承、保护继承还是私有继承,都只影响到成员的访问权限,对于成员函数的重载、重写、隐藏等行为都没有影响。此外,派生类可以通过 using 关键字来改变从基类继承的成员的访问权限。

总的来说,继承类型决定了派生类如何继承基类的成员,理解它们的区别很重要,以便正确地设计和使用继承关系。在大多数情况下,公共继承是最常用的继承方式。

多继承

在面向对象编程中,一个类可以派生自多个基类,这被称为多重继承。通过使用类派生列表,我们可以指定派生类的多个基类。

类派生列表的语法如下:

class DerivedClass : access-specifier BaseClass1, access-specifier BaseClass2, ...
{// 类成员声明和定义
};

其中,access-specifier 是访问修饰符,可以是 public、protected 或 private,用于指定从每个基类继承的成员的访问权限。如果没有显式地指定访问修饰符,则默认为 private 访问权限。

通过多重继承,派生类可以从每个基类继承其成员函数和数据成员。需要注意的是,当多个基类拥有同名的成员函数或数据成员时,派生类必须通过作用域解析运算符明确指定要使用的成员。

下面是一个示例:

class BaseClass1 {// 基类1的成员和方法
};class BaseClass2 {// 基类2的成员和方法
};class DerivedClass : public BaseClass1, public BaseClass2 {// 派生类的成员和方法
};

在上述示例中,DerivedClass 是从 BaseClass1 和 BaseClass2 这两个基类中进行多继承的派生类。派生类将同时拥有这两个基类的成员和方法。

需要注意的是,在多继承中可能存在以下问题和注意事项:

1、名称冲突:如果多个基类具有相同的成员或方法名称,派生类在访问该成员或方法时需要进行限定,以避免歧义。

2、菱形继承问题:当多个基类之间存在继承关系时,派生类可能会继承相同的成员和方法多次,这被称为菱形继承问题。为了解决这个问题,可以使用虚继承(virtual inheritance)来避免重复继承。

class BaseClass {// 基类的成员和方法
};class IntermediateClass1 : public virtual BaseClass {// 中间类1的成员和方法
};class IntermediateClass2 : public virtual BaseClass {// 中间类2的成员和方法
};class DerivedClass : public IntermediateClass1, public IntermediateClass2 {// 派生类的成员和方法
};

通过在基类之间使用 virtual 关键字,可以确保在派生类中只有一个实例共享基类的成员和方法。

多继承是一种强大的工具,但需要谨慎使用,特别是在存在名称冲突或复杂继承关系的情况下。

代码示例

#include<iostream>
using namespace std;// 定义基类
class BaseClass {
public:// 基类的公有方法void baseFunc() {cout << "调用了基类的方法" << endl;}
};// 定义中间类1,使用虚继承 BaseClass
class IntermediateClass1 : public virtual BaseClass {
public:// 中间类1的公有方法void intermediate1Func() {cout << "调用了中间类1的方法" << endl;}
};// 定义中间类2,使用虚继承 BaseClass
class IntermediateClass2 : public virtual BaseClass {
public:// 中间类2的公有方法void intermediate2Func() {cout << "调用了中间类2的方法" << endl;}
};// 定义派生类,从中间类1和中间类2进行多继承
class DerivedClass : public IntermediateClass1, public IntermediateClass2 {
public:// 派生类的公有方法void derivedFunc() {cout << "调用了派生类的方法" << endl;}
};int main() {// 创建派生类对象DerivedClass obj;// 调用派生类的方法obj.derivedFunc();// 调用中间类1的方法obj.intermediate1Func();// 调用中间类2的方法obj.intermediate2Func();// 调用基类的方法obj.BaseClass::baseFunc();return 0;
}

在上述示例中,我们定义了一个基类 BaseClass,然后分别定义了两个中间类 IntermediateClass1 和 IntermediateClass2,它们都从基类 BaseClass 进行虚继承。最后,我们定义了派生类 DerivedClass,它从这两个中间类进行多重继承。

在 main 函数中,我们创建了 DerivedClass 的一个对象,并且分别调用了它自己的方法、以及从中间类和基类继承过来的方法。此外,我们还使用作用域限定符 :: 来标识调用基类 BaseClass 的方法。

编译并运行上述代码,可以得到如下输出:

调用了派生类的方法
调用了中间类1的方法
调用了中间类2的方法
调用了基类的方法

这表明虚继承和作用域限定符都起到了作用,确保派生类只继承了一份基类的成员和方法。

重载运算符和重载函数

C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

当调用一个重载函数重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策

函数重载

函数重载是指在同一个作用域内,定义多个同名但参数列表不同的函数。通过函数重载,可以根据不同的参数类型和数量来调用相应的函数。

下面是一个使用函数重载的示例代码:

#include <iostream>
using namespace std;// 函数重载示例
void print(int num) {cout << "整数: " << num << endl;
}void print(double num) {cout << "浮点数: " << num << endl;
}void print(char ch) {cout << "字符: " << ch << endl;
}int main() {int a = 10;double b = 3.14;char c = 'A';// 调用不同参数类型的重载函数print(a);print(b);print(c);return 0;
}

在上述示例中,我们定义了三个重载的 print 函数,分别接受整数、浮点数和字符作为参数,并打印出对应的类型和值。

在 main 函数中,我们声明了一个整数变量 a、一个浮点数变量 b、一个字符变量 c,然后分别调用 print 函数来打印它们的值。由于参数类型不同,编译器会自动匹配调用相应类型的函数。

编译并运行上述代码,可以得到如下输出:

整数: 10
浮点数: 3.14
字符: A

这表明函数重载使得根据传入的参数类型和数量自动选择合适的函数成为可能。注意,函数重载的条件是参数列表不同,包括参数类型、参数个数或参数顺序。返回类型不同的函数不能构成重载。

运算符重载

运算符重载是指在类中重新定义和使用已有的运算符,使其适用于自定义类型的对象。通过运算符重载,可以方便地对对象执行各种操作,提高代码的可读性和易用性。

下面是一个使用运算符重载的示例代码:

#include <iostream>
using namespace std;// 定义一个复数类
class Complex {
private:double real;double imaginary;public:// 构造函数Complex(double r = 0.0, double i = 0.0) : real(r), imaginary(i) {}// 运算符重载:+Complex operator+(const Complex& other) const {return Complex(real + other.real, imaginary + other.imaginary);}// 运算符重载:-Complex operator-(const Complex& other) const {return Complex(real - other.real, imaginary - other.imaginary);}// 运算符重载:*Complex operator*(const Complex& other) const {double r = real * other.real - imaginary * other.imaginary;double i = real * other.imaginary + imaginary * other.real;return Complex(r, i);}// 运算符重载:<<friend ostream& operator<<(ostream& os, const Complex& c) {os << "(" << c.real << ", " << c.imaginary << "i)";return os;}
};int main() {Complex c1(2.0, 3.0);Complex c2(1.0, 4.0);// 使用重载的运算符进行操作Complex sum = c1 + c2;Complex diff = c1 - c2;Complex product = c1 * c2;// 输出结果cout << "c1 + c2 = " << sum << endl;cout << "c1 - c2 = " << diff << endl;cout << "c1 * c2 = " << product << endl;return 0;
}

在上述示例中,我们定义了一个复数类 Complex,其中包含实部和虚部。然后,我们通过运算符重载重新定义了 +、-、* 运算符,使其适用于 Complex 类型的对象。

此外,我们还通过友元函数重载了输出运算符 <<,以便能够通过 cout 打印复数对象。

在 main 函数中,我们创建了两个复数对象 c1 和 c2,并使用重载的运算符对它们进行操作,将结果存储到相应的变量中。最后,我们使用 cout 打印出运算结果。

编译并运行上述代码,可以得到如下输出:

c1 + c2 = (3, 7i)
c1 - c2 = (1, -1i)
c1 * c2 = (-10, 11i)

这表明通过运算符重载,我们可以像使用内置类型一样对自定义类型的对象执行各种操作。

可重载运算符/不可重载运算符

下面是可重载的运算符列表:

双目算术运算符+ (加),-(减),*(乘),/(除),% (取模)
关系运算符==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于)
逻辑运算符||(逻辑或),&&(逻辑与),!(逻辑非)
单目运算符+ (正),-(负),*(指针),&(取地址)
自增自减运算符++(自增),--(自减)
位运算符| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
赋值运算符=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空间申请与释放new, delete, new[ ] , delete[]
其他运算符()(函数调用),->(成员访问),,(逗号),[](下标)

下面是不可重载的运算符列表:

  • 成员访问运算符 .  用于访问类或结构体的成员。
  • 成员指针访问运算符 .* 和 ->* 用于访问类成员指针指向的成员。
  • 域运算符 :: 用于访问命名空间、类、结构体、枚举等的成员。
  • 长度运算符 sizeof 用于获取类型或表达式的字节大小。
  • 条件运算符 ?: 是三元运算符,用于根据条件选择两个表达式之一的值。
  • 预处理符号 # 用于预处理指令中的字符串化操作。

运算符重载实例

下面提供了各种运算符重载的实例,帮助您更好地理解重载的概念。

序号运算符和实例
1一元运算符重载
2二元运算符重载
3关系运算符重载
4输入/输出运算符重载
5++ 和 -- 运算符重载
6赋值运算符重载
7函数调用运算符 () 重载
8下标运算符 [] 重载
9类成员访问运算符 -> 重载

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

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

相关文章

[直播自学]-[汇川easy320]搞起来(1)给PLC供电

从没正儿八经的用一用PLC&#xff0c;所以双11在淘宝入手一个EASY320&#xff0c;大概1000出头。 到货后&#xff0c;汇川官网搜了一下资料&#xff0c;搜到这几个&#xff1a; 首先是给PLC供电吧&#xff0c;看了下PLC前面是24V&#xff0c;不知道供电范围多宽&#xff0c;于…

YoloV8目标检测与实例分割——目标检测onnx模型推理

一、模型转换 1.onnxruntime ONNX Runtime&#xff08;ONNX Runtime或ORT&#xff09;是一个开源的高性能推理引擎&#xff0c;用于部署和运行机器学习模型。它的设计目标是优化执行使用Open Neural Network Exchange&#xff08;ONNX&#xff09;格式定义的模型&#xff0c;…

https原理

首先说一下几个概念&#xff1a;对称加密、非对称加密 对称加密&#xff1a; 客户端和服务端使用同一个秘钥&#xff0c;分两种情况&#xff1a; 1、所有的客户端和服务端使用同一个秘钥&#xff0c;这个秘钥被泄漏后数据不再安全 2、每个客户端生成一个秘钥&…

[云原生案例2.1 ] Kubernetes的部署安装 【单master集群架构 ---- (二进制安装部署)】节点部分

文章目录 1. 常见的K8S安装部署方式1.1 Minikube1.2 Kubeadm1.3 二进制安装部署 2. Kubernetes单master集群架构 ---- &#xff08;二进制安装部署&#xff09;2.1 前置准备2.2 操作系统初始化2.3 部署 docker引擎 ---- &#xff08;所有 node 节点&#xff09;2.4 部署 etcd 集…

HTML_案例1_注册页面

用纯html页面&#xff0c;不用css画一个注册页面。 最终效果如下&#xff1a; html页面代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册页面</title> </head>…

ffmpeg命令帮助文档

一&#xff1a;帮助文档的命令格式 ffmpeg -h帮助的基本信息ffmpeg -h long帮助的高级信息ffmpeg -h full帮助的全部信息 ffmpeg的命令使用方式&#xff1a;ffmpeg [options] [[infile options] -i infile] [[outfile options] outfile] 二&#xff1a;将帮助文档输出到文件 …

部署ELK

一、elasticsearch #拉取镜像 docker pull elasticsearch:7.12.1 #创建ELK docker网络 docker network create elk #启动ELK docker run -d --name es --net elk -P -e "discovery.typesingle-node" elasticsearch:7.12.1 #拷贝配置文件 docker cp es:/usr/share/el…

【2023年MathorCup高校数学建模挑战赛-大数据竞赛】赛道A:基于计算机视觉的坑洼道路检测和识别 python 代码解析

【2023年MathorCup高校数学建模挑战赛-大数据竞赛】赛道A&#xff1a;基于计算机视觉的坑洼道路检测和识别 python 代码解析 1 题目 坑洼道路检测和识别是一种计算机视觉任务&#xff0c;旨在通过数字图像&#xff08;通常是地表坑洼图像&#xff09;识别出存在坑洼的道路。这…

Pycharm 对容器中的 Python 程序断点远程调试

pycharm如何连接远程服务器的docker容器有两种方法&#xff1a; 第一种&#xff1a;pycharm通过ssh连接已在运行中的docker容器 第二种&#xff1a;pycharm连接docker镜像&#xff0c;pycharm运行代码再自动创建容器 本文是第一种方法的教程&#xff0c;第二种请点击以上的链接…

个人服务器到期,项目下线,新的开始

告别旧服务器 2023.11.06服务器到期&#xff0c;所有项目正式下线 时间真的过的很快&#xff0c;从开始踏入编程的大门&#xff0c;到现在不知不觉已经陆续经手了两台服务器了&#xff0c;目前这台服务器是一年前的阿里云活动白嫖的嘿嘿嘿&#xff0c;该服务器上目前运行的项…

项目实战:service业务逻辑组件引入

1、第一层DAO层 1.1、FruitDao接口 package com.csdn.fruit.dao; import com.csdn.fruit.pojo.Fruit; import java.util.List; //dao &#xff1a;Data Access Object 数据访问对象 //接口设计 public interface FruitDao {void addFruit(Fruit fruit);void delFruit(String fn…

计网【链路带宽100Mbps代表什么,“翻译”成人话是?】

这里写目录标题 带宽的概念本来的意思【通信领域】计网中的意思 结论【100Mbps代表什么】 带宽的概念 本来的意思【通信领域】 带宽这个概念本来是通信领域的&#xff0c;表示通信线路允许通过的信号频带范围&#xff0c;单位是赫兹Hz 感觉最简单的意思&#xff0c;例如如果…

ActiveMq学习⑧__ActiveMQ的消息持久化机制

ActiveMQ的消息存储和持久化 MQ的高可用 事务持久签收可持久化 &#xff08;类似于与mq消息的同步机制&#xff09; 为了避免意外宕机以后丢失信息&#xff0c;需要做到重启后可以恢复消息队列&#xff0c;消息系统一半都会采用持久化机制。 ActiveMQ的消息持久化机制 Act…

SurfaceFlinger的硬件Vsync深入分析-千里马android framework车机手机系统开发

背景&#xff1a; 学过或者你看过surfaceflinger相关文章同学都知道&#xff0c;vsync其实都是由surfaceflinger软件层面进行模拟的&#xff0c;但是软件模拟有可能会有误差或偏差&#xff0c;这个时候就需要有个硬件vsync帮忙校准。 故才会在surfaceflinger的systrace出现如下…

C语言从入门到精通之【第一个程序hello world】

编程步骤 通常&#xff0c;我们按照以下步骤进行 确立目标设计程序编写代码编译程序运行程序测试&调试修改维护 输出hello world 每个学编程的人都会从最经典的【输出hello world】开始。 https://lightly.teamcode.com/ 我们可以使用这个在线IDE学习C语言。 代码很简…

Go语言与Python语言的性能比较

目录 一、背景与意义 二、执行速度 三、内存消耗 四、并发性能 五、编译速度与开发效率 六、综合考虑 七、应用场景 八、未来发展趋势 总结 一、背景与意义 在编程世界中&#xff0c;Go语言和Python语言都占有一席之地。Go语言是由Google开发的&#xff0c;其设计初衷…

【JavaEE】JVM 剖析

JVM 1. JVM 的内存划分2. JVM 类加载机制2.1 类加载的大致流程2.2 双亲委派模型2.3 类加载的时机 3. 垃圾回收机制3.1 为什么会存在垃圾回收机制?3.2 垃圾回收, 到底实在做什么?3.3 垃圾回收的两步骤第一步: 判断对象是否是"垃圾"第二步: 如何回收垃圾 1. JVM 的内…

Python爬虫技术系列-04Selenium库的使用

Python爬虫技术系列-04Selenium库的使用 1 Selenium库基本使用1.1 Selenium库安装1.2 Selenium库介绍 2 Selenium库的使用2.1 各个版本的区别2.1.1 Selenium IDE介绍与使用2.1.2 Selenium Grid介绍与使用2.1.3 Selenium RC介绍与使用2.1.4 WebDriver介绍与使用 2.2 WebDriver常…

Gopro hero5运动相机格式化后恢复案例

Gopro运动相机以稳定著称&#xff0c;旗下的Hero系列销售全球。下面我们来看一个Hero5格式化后拍了少量素材的恢复案例。 故障存储:64G MicroSD卡 Exfat文件系统 故障现象: 64G的卡没备份数据时做了格式化操作又拍了一条&#xff0c;发现数据没有备份&#xff0c;客户自行使…

Tomcat安装配置教程

目录 1、安装tomcat1.1、查看JDK版本1.2、 匹配对应的JDK版本1.3、 下载Tomcat1.3.1、 安装包版&#xff08;推荐&#xff0c;不用配环境&#xff09;1.3.2、 压缩包版 2、 运行Tomcat3、 不能运行问题 1、安装tomcat 1.1、查看JDK版本 由于不同版本tomcat对于jdk的版本有要求…