【C++】继承与模板

继承

1.继承的概念

概念:继承(inheritace)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称之为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承是类设计层次的复用

//	   派生类  继承方式 基类
class Student:public Person{   
public:int _name;int _sex;
}
  • 代码复用:通过继承,派生类可以复用基类的代码,减少重复编写相同功能的需要。
  • 扩展性:派生类可以在基类的基础上添加新的成员或方法,扩展功能。
  • 层次结构:继承建立了类之间的“is-a”关系,形成了一个类层次结构,有助于组织和理解代码。

2.继承方式和访问限定符

特征public继承protected继承private继承
基类的public成员变成派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员变成派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员变成只能通过基类接口访问,派生类中不可见只能通过基类接口访问,派生类中不可见只能通过基类接口访问,派生类中不可见
能否隐式向上转换是(但只能在派生类中)

注意:

  • 基类的private成员派生类不可见(无法直接允许访问),但可以使用基类publicprotected成员函数间接访问。
  • 使用关键字class时默认继承方式是private;使用struct时默认继承方式是public最好显示的写出继承方式,以提高代码的可读性和可维护性。
  • public > protected > private,继承方式权限只能缩小不能放大,如基类的public成员的遇到protected继承方式就成了派生类的protected成员。
  • 访问限定符在基类中没有体现,在派生类中才产生区别,这也就是访问限定符产生的原因。
struct BaseStruct {
public:int publicMember;
protected:int protectedMember;
private:int privateMember;
};class DerivedClass : BaseStruct { // 默认 private 继承
public:void accessMembers() {publicMember = 1;      // private 继承,基类的 public 成员变为 privateprotectedMember = 2;   // private 继承,基类的 protected 成员变为 private// privateMember = 3;  // 无法访问}
};class DerivedPublic : public BaseStruct { // public 继承
public:void accessMembers() {publicMember = 1;      // 保持 publicprotectedMember = 2;   // 保持 protected// privateMember = 3;  // 无法访问}
};

3.基类和派生类对象赋值转换

3.1 赋值兼容规则:

  • 子类对象可以赋值给父类对象、指针或引用(称为”切割“或”切片“)。在这种赋值过程中,派生类特有的成员将被忽略,只保留基类部分。
  • 基类对象不能直接赋值给派生对象,因为基类对象不包含派生类新增的成员。
class Person{
public:string _name;int _sex;int _age;
};
class Student : public Person{
public:int _No;
};
int main(){Student s;s.name = "Alice";s.age = 20;s._No = 12345;Person p;p = s; // 切割,只复制基类部分std::cout << p.name << ", " << p.age << std::endl; // 输出:Alice, 20Person* ptr = &s; // 多态,ptr 指向 Student 对象Person& ref = s;  // 引用,ref 引用 Student 对象  return 0;
}

image-20240715143458274

3.2 指针和引用的转换:

  • 指针转换

    • 向上转型(Upcasting):将派生类指针转换为基类指针,是隐式且安全的。

    • 向下转型(Downcasting):将基类指针转换为派生类指针,需要使用 dynamic_cast 进行类型检查,确保转换的安全性。

  • 引用转换

  • 类似于指针转换,向上转型是隐式的,而向下转型需要显式的类型转换。

int main() {Student s;s.name = "Bob";s.age = 22;s.studentID = 67890;Person* basePtr = &s; // 向上转型,隐式转换basePtr->introduce(); // 调用基类方法// 向下转型,需要使用 dynamic_castStudent* derivedPtr = dynamic_cast<Student*>(basePtr);if (derivedPtr) {derivedPtr->study(); // 调用派生类方法}return 0;
}

4.继承作用域与成员隐藏

  • 作用域独立:继承中的基类和派生类都有各自独立的作用域,成员隐藏仅在派生类作用域内有效。
  • 成员隐藏只要成员名称相同,无论类型或参数列表,派生类成员都会重定义(隐藏)基类的成员。

解决函数的隐藏与重载

class A {
public:virtual void fun() {cout << "A::fun()" << endl;}
};class B : public A {
public:void fun(int i) { // 重载,不隐藏基类的 fun()cout << "B::fun(int): " << i << endl;}void callBaseFun() {A::fun(); // 显式调用基类的 fun()}void fun() override { // 重定义cout << "B::fun()" << endl;}
};int main() {B b;b.fun();          // 调用 B::fun()b.fun(10);        // 调用 B::fun(int)b.callBaseFun();  // 调用 A::fun()return 0;
}
  • B::fun(int i):重载了 A::fun(),但不隐藏基类的 fun(),在同一作用域内(派生类B中)。
  • B::fun():重定义了 A::fun(),提供了新的实现。

也可以通过using A::fun引入基类的fun(),从而调用A类的fun方法。

重载、重写和重定义的区别

特性重载(Overloading)重写(Overriding)重定义(Hiding)
作用域同一类内派生类和基类之间派生类和基类之间
是否需要继承
关键字virtualoverride
参数列表必须不同必须相同可以不同
返回类型可以不同必须相同(或协变)可以不同
调用时间编译时决定运行时决定编译时决定
用途提供同名函数的不同版本实现多态性派生类中隐藏基类同名函数

5.派生类的默认成员函数

当创建派生类时,编译器会自动为其生成一些默认的成员函数,包括构造函数、拷贝构造函数、赋值运算符和析构函数。这些默认成员函数在大多数情况下是足够的,但在特定需求下,程序员可以显式地定义或删除它们。

默认成员函数的行为

  1. 构造函数
    • 默认构造函数:派生类的构造函数必须调用基类的构造函数以初始化基类部分。如果基类没有默认构造函数,派生类构造函数必须在初始化列表中显式调用基类的其他构造函数。
    • 拷贝构造函数:自动调用基类的拷贝构造函数,完成基类部分的拷贝初始化。
  2. 赋值运算符(operator=
    • 自动调用基类的赋值运算符,完成基类部分的赋值。
  3. 析构函数
    • 派生类的析构函数在执行完自己的清理工作后,自动调用基类的析构函数,确保基类部分的资源得到正确释放。
    • 虚析构函数:如果基类的析构函数是虚函数(virtual),可以确保通过基类指针删除派生类对象时,派生类的析构函数被正确调用,避免资源泄漏。

image-20241027160711419

QQ_1721094131676

#include <iostream>
using namespace std;class Base {
public:Base() { cout << "Base Constructor" << endl; }Base(const Base&) { cout << "Base Copy Constructor" << endl; }Base& operator=(const Base&) { cout << "Base Assignment Operator" << endl; return *this; }virtual ~Base() { cout << "Base Destructor" << endl; }
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor" << endl; }Derived(const Derived& d) : Base(d) { cout << "Derived Copy Constructor" << endl; }Derived& operator=(const Derived& d) { Base::operator=(d); cout << "Derived Assignment Operator" << endl; return *this; }~Derived() { cout << "Derived Destructor" << endl; }
};int main() {Derived d1;Derived d2 = d1; // 拷贝构造d2 = d1;         // 赋值运算符return 0;
}/*
输出:
Base Constructor
Derived Constructor
Base Copy Constructor
Derived Copy Constructor
Base Assignment Operator
Derived Assignment Operator
Derived Destructor
Base Destructor
Derived Destructor
Base Destructor
*/

6.友元与静态成员

  • 友元关系不能继承:基类的友元在派生类中不是友元,派生类需要重新声明友元关系。
class Base {
private:int secret;friend class FriendClass;
};class Derived : public Base {// FriendClass 不是 Derived 的友元
};
  • 静态成员:基类定义了static静态成员,则整个继承体系里面只有一个这样的成员,所有派生类共享这一个静态成员。
class Base {
public:static int staticValue;
};int Base::staticValue = 0;class Derived1 : public Base {};
class Derived2 : public Base {};int main() {Derived1::staticValue = 5;cout << Derived2::staticValue << endl; // 输出:5return 0;
}

7.多继承

优点

  • 功能整合:允许派生类同时拥有多个基类的功能,适用于需要结合多种特性的复杂类。
  • 灵活性:提供了更大的设计灵活性,适用于多维度的类层次结构。

缺点

  • 复杂性增加:类层次结构更加复杂,增加了理解和维护的难度。
  • 名称冲突:多个基类中可能存在同名成员,导致名称冲突和二义性。
  • 菱形继承问题:多个基类继承自同一个祖先类,导致数据冗余和二义性。
7.1 单继承与多继承
  • 单继承:一个子类只有一个直接父类时这个继承关系为单继承。
class Person
class Student:public Person
class PostGraduate:public Student
  • 多继承:一个子类有两个或以上的直接父类时成为多继承。
class Teacher
class Student
class Assistant:public Student,public Teacher
7.2 菱形继承

菱形继承:菱形继承是多集成的一种特殊情况。

class Person
class Teacher:public Person
class Student:public Person
class Assistant:public Student,public Teacher

菱形继承的问题

  • 数据冗余:派生类中存在多个基类的副本,导致数据冗余。例如,Assistant 类中会有两份 Person 的成员。
  • 二义性:当访问祖先类的成员时,编译器无法确定访问哪一个基类的成员,导致二义性错误。

img

class Person {
public:string _name; 	// 姓名
};
class Student : public Person {
protected:int _num; 	//学号
};
class Teacher : public Person {int _id;	//职工号   
};
class Assistant : public Student, public Teacher {
protected:string _majorCourse;	// 主修课程
};
void Test() {// 这样会有二义性无法明确知道访问的是哪一个Assistant a;// 编译错误:二义性,无法确定是 Student::Person::name 还是 Teacher::Person::name//a._name = "peter";// 需要显示指定访问那个父类的成员可以解决二义性问题// 但是数据冗余问题无法解决a.Student::_name = "XXX";a.Teacher::_name = "YYY";
}

8.虚拟继承

为了解决菱形继承带来的数据冗余和二义性问题,C++ 引入了虚继承(Virtual Inheritance)。通过虚继承,派生类共享基类的唯一实例,消除数据冗余和二义性。

如在上面Student和Teacher在继承Person时使用虚继承即可解决问题:

class Person {
public:std::string name;
};class Student : virtual public Person {
protected:int studentID;
};class Teacher : virtual public Person {
protected:int employeeID;
};class Assistant : public Student, public Teacher {
protected:std::string majorCourse;
};int main() {Assistant a;a.name = "Charlie"; // 唯一的 Person::name 成员return 0;
}

下面是一个直接继承的例子:

注:由于x86指针采用4字节对齐方式,x64采用8字节对齐方式。方便起见以下全用x86为例

class A {
public:int _a;
};
class B : public A {
public:int _b;
};
class C : public A {
public:int _c;
};
class D : public B, public C {
public:int _d;
};
void Test(){D d;// sizeof(B)=8; sizeof(C)=8// sizeof(D) = sizeof(B)+sizeof(C)+sizeof(_d) = 20cout << "sizeof(d)=" << sizeof(d) << endl;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;// 编译报错:_a 不明确,需要指明是那个父类下连带的属性,如上// d._a = 6;
}

image-20240716161623437

从图中可以见得数据中存在两个_a,造成了数据的冗余和二义性;

下面看一下虚继承的示例:

class A {
public:int _a;
};
class B : virtual public A {
public:int _b;
};
class C : virtual public A {
public:int _c;
};
class D : public B, public C {
public:int _d;
};
void Test(){D d;cout << "sizeof(d)=" << sizeof(d) << endl;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;d._a = 6;
}

image-20240716170952184

8.1 虚拟继承工作原理
  • 虚基表指针(VBPtr):编译器为每个类对象添加一个虚基表指针,用于指向虚基类的信息。这与虚函数表指针(Vptr)不同,用于虚函数的多态性。
  • 虚基表(VBTable):虚基表记录虚基类的位置信息和偏移量,确保在派生类中正确访问共享基类成员。
虚拟指针的“三板斧”

由上面例子我们能够发现:但虚拟继承时,原本的父类成员会被替换为一个虚基表指针,这个指针指向一张虚基表,虚基表里存放虚基类与虚基表指针的偏移量。要注意和虚函数表区分开来。

正常继承:(下图中展示的是x64环境下的)

正常继承

虚拟继承:

img

编译器处理虚继承的方法是:编译器在处理虚继承(虚函数)时,会给每个派生类添加一个隐藏成员类似:

class A{
public:void *vptr;// 续集表指针 --> 4个字节...
}

同时在编译器会给向派生类的构造函数中安插一条赋值语句 来为vptr赋值,类似:

A(){vptr = &A::vftable;
}

虚基类存储在最后继承它的派生类

9.继承与组合

继承和组合都是实现类复用和构建复杂对象的手段,但它们在设计哲学和应用场景上有所不同。

9.1 继承(Inheritance)
  • 关系:表示“is-a”关系,即派生类是基类的一种。例如,StudentPerson
  • 复用方式:派生类通过继承基类的成员,实现代码的复用和扩展。
  • 继承是一种白箱复用,父类对子类基本是透明的,但是它一定程度破坏了父类的封装
  • 优点:
    • 简单直接,适用于明确的类型层次结构。
    • 允许派生类直接访问基类的 publicprotected 成员。
  • 缺点:
    • 高耦合,派生类依赖基类的实现细节。
    • 破坏基类的封装性,基类的改变可能影响派生类。
8.2 组合(Composition)
  • 关系:表示“has-a”关系,即一个类包含另一个类的对象。例如,Car 有一个 Engine
  • 复用方式:通过在类中包含成员对象,实现功能的复用和扩展。
  • 组合式一种黑箱复用,C对D是不透明的,C保持着他的封装。
  • 优点:
    • 低耦合,类之间的依赖关系较弱。
    • 保持了各自类的封装性和独立性。
    • 更加灵活,可以在运行时动态组合不同的组件。
  • 缺点:
    • 需要通过成员对象的接口间接访问功能,可能增加代码复杂性。

示例

  • 继承示例:
// 继承示例
class Engine {
public:void start() {std::cout << "Engine started." << std::endl;}
};class Car : public Engine { // Car is-a Engine(不符合实际逻辑,仅为示例)
public:void drive() {start(); // 直接访问 Engine 的成员std::cout << "Car is driving." << std::endl;}
};int main() {Car car;car.start(); // 直接调用 Engine 的方法car.drive();return 0;
}
/*
输出:
Engine started.
Engine started.
Car is driving.
*/
  • 组合示例:
// 组合示例
class Engine {
public:void start() {std::cout << "Engine started." << std::endl;}
};class Car {
private:Engine engine; // Car has-a Engine
public:void drive() {engine.start(); // 通过 Engine 的接口访问std::cout << "Car is driving." << std::endl;}
};int main() {Car car;// car.start(); // 错误,Engine 的 start() 是私有的car.drive();return 0;
}
/*
输出:
Engine started.
Car is driving.
*/

组合的类耦合度更低,而继承的类是一种高耦合。

最佳实践

  • 优先使用组合:当类之间的关系不明确或“has-a”关系更符合实际需求时,优先选择组合。
  • 谨慎使用继承:仅在明确需要“is-a”关系并且派生类确实需要基类的功能时,才使用继承。

面试题

C++的缺陷有哪些?
  1. 复杂性
    • C++ 的语法和特性非常丰富,这使得学习曲线陡峭,容易出错。
    • 复杂的模板和泛型编程可能导致编译错误难以理解。
  2. 内存管理
    • 手动管理内存(如使用 newdelete)容易导致内存泄漏和野指针问题。
    • 虽然 C++11 引入了智能指针(如 unique_ptrshared_ptr),但仍然需要开发者谨慎使用。
  3. 多继承
    • 多继承可能导致复杂的对象模型和潜在的二义性问题。
    • 菱形继承问题是一个典型的多继承问题,需要使用虚继承来解决。
  4. 性能和效率
    • 虽然 C++ 通常被认为是高性能的语言,但某些高级特性(如虚函数、RTTI)可能会引入额外的开销。
    • 优化代码需要深入理解编译器和硬件特性。
  5. 缺乏内置的垃圾回收机制
    • C++ 没有内置的垃圾回收机制,需要手动管理内存,增加了开发复杂度。
  6. 标准库的局限性
    • 标准库虽然强大,但某些领域(如网络编程、GUI 开发)的支持相对薄弱。
    • 第三方库的质量和兼容性参差不齐。
什么是菱形继承?菱形继承的问题是什么?如何解决?虚继承的原理是什么?
  1. 什么是菱形继承?

    菱形继承是一种多继承的情况,其中派生类从两个基类派生,而这两个基类又共同派生自同一个基类。这种继承结构形成了一个菱形的形状。

class A {
public:int value;
};
class B : public A {};
class C : public A {};
class D : public B, public C {};
  1. 菱形继承的问题是什么?

    (1) 二义性:

    • 由于 DBC 继承,而 BC 都有一个 A 的子对象,D 会有两个 A 的子对象。
    • 当尝试访问 A 的成员时,编译器无法确定应该使用哪个 A 的子对象,导致二义性问题。

    (2) 对象模型复杂:

    • 菱形继承会导致对象模型变得复杂,增加内存开销和管理难度。
  2. 如何解决菱形继承的问题?

    使用虚继承可以解决菱形继承带来的二义性和对象模型复杂的问题。

class A {
public:int value;
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
  1. 虚继承的原理?

    (1) 单一子对象:

    • 虚继承确保派生类中只有一个基类的子对象,而不是多个。
    • 在上述例子中,D 只有一个 A 的子对象,而不是两个。

    (2) 初始化顺序:

    • 虚基类的构造函数会在最派生类的构造函数中被调用,而不是在中间派生类的构造函数中被调用。
    • 这确保了虚基类的子对象在所有派生类的子对象之前被初始化。

    (3) 内存布局:

    • 虚继承会导致对象的内存布局更加复杂,因为编译器需要在对象中添加额外的指针来管理虚基类的子对象。
    • 这可能会引入一定的性能开销。

模版

1.概念和工作原理

1.1 概念:

模版(Template)是C++中实现泛型变成的核心机制。它运行程序员编写与类型无关的代码,通过实例化时指定具体类型,从而实现代码的复用和灵活性。模版主要分为函数模版类模版。模版的设计旨在在不牺牲性能的前提下,实现代码的高度复用。通过在编译时生成针对不同类型的代码,模版避免了运行时的多态开销,同时保持了类型安全。这种编译时多态性与运行时多态性(如虚函数)形成鲜明对比,各有优劣。

1.2 工作原理:

模版在C++中通过实例化机制工作。当编译器遇到模板的使用时,会根据提供的类型参数生成具体的函数或类。这一过程发生在编译期,确保了生成的代码在类型上是正确的。

编译器在编译阶段根据模板参数生成响应的代码,这意味着每个不同的模板参数组合都会生成独立的代码实例。这种机制带了以下优点:

  • 类型安全: 所有类型检查在编译期完成,避免了运行时错误。
  • 性能优化:生成的代码针对特定类型进行了优化,消除了不必要的抽象层。

但同时也存在一些缺点:

  • 编译时间增加:大量的模版实例化可能导致编译时间显著增加。
  • 代码膨胀:每个模版实例化都会生成独立的代码,可能导致可执行文件体积增大。

2.函数模板

2.1 定义与使用

函数模板允许编写与类型无关的函数,通过模板参数在调用时指定具体类型。函数模板的语法以template关键字开头,紧随其后的是模板参数列表。

// 函数模版
template <typename T>
T getMax(T a, T b) {return (a > b) ? a : b;
}int main() {cout << getMax<int>(3, 7) << endl;         // 输出:7cout << getMax<double>(3.5, 2.5) << endl;  // 输出:3.5cout << getMax<char>('g', 'e') << endl;    // 输出:greturn 0;
}
2.2 模版参数推导

在大多数情况下编译期能够根据函数参数自动推导处模板类型,此时可以不用显式的指定类型。,因此上面对模版的使用还可以这样

int main() {cout << getMax(3, 7) << endl;          // 自动推导为 getMax<int>cout << getMax(3.5, 2.5) << endl;      // 自动推导为 getMax<double>cout << getMax('g', 'e') << endl;      // 自动推导为 getMax<char>return 0;
}

注意事项:

  • 如果模版参数无法从函数参数中推导出来,必须显式指定类型。
  • 模版参数推导在函数重载解析中起重要作用,可能影响函数的选择。
2.3 多参数模板

函数模板可以接收多个类型参数,以处理不同类型的参数组合。这为函数的通用性提供了更大的灵活性:

// 多参数函数模版
template <typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {// 根据传入值a、b相加的结果推导类型return a + b;
}int main() {cout << add(3, 4.5) << endl;         // 输出:7.5cout << add(2.3f, 4) << endl;        // 输出:6.3return 0;
}

拓展分析

decltype关键字:是C++11新增的一个关键字,和auto的功能一样,用来编译时期进行自动类型推导,确保返回类型与操作符的实际结果类型一致。它的引入很好的弥补了auto不适用或跟不无法使用的场景。

decltype基本语法:

int a = 10;
decltype(a) b = a; // b的类型与a相同,即int

3.类模板

3.1 定义与使用
#include <iostream>
#include <string>
using namespace std;// 类模版
template <typename T>
class MyContainer {
private:T element;
public:MyContainer(T elem) : element(elem) {}void display() const {cout << "Element: " << element << endl;}
};int main() {MyContainer<int> intObj(42);intObj.display();  // 输出:Element: 42MyContainer<string> strObj("Hello");strObj.display();  // 输出:Element: Helloreturn 0;
}
3.2 模板类的默认参数

类模板可以为模板参数指定默认类型,简化实例化时的类型指定。允许在需要时覆盖默认类型,保持代码的通用性。

// 类模版,默认类型为int
template <typename T = int>
class MyContainer {
private:T element;
public:MyContainer(T elem) : element(elem) {}void display() const {cout << "Element: " << element << endl;}
};int main() {MyContainer<> defaultObj(100); // 默认类型为intdefaultObj.display();           // 输出:Element: 100MyContainer<double> doubleObj(99.99);doubleObj.display();            // 输出:Element: 99.99return 0;
}

4.模板特化

模板特化允许为特定类型或类型组合提供专门的实现,以满足特殊需求。模板特化分为全特化偏特化

4.1 全特化

全特化为模板所有参数指定具体类型,提供专门的实现。这在处理特定类型时非常有用,例如针对指针类型或某些自定义类型提供不同的行为。

#include <iostream>
using namespace std;// 原始类模版
template <typename T>
class MyContainer {
private:T element;
public:MyContainer(T elem) : element(elem) {}void display() const {cout << "Generic Element: " << element << endl;}
};// 全特化,针对char*类型
template <>
class MyContainer<char*> {
private:char* element;
public:MyContainer(char* elem) : element(elem) {}void display() const {cout << "Specialized Element: " << element << endl;}
};int main() {MyContainer<int> intObj(10);intObj.display();  // 输出:Generic Element: 10char msg[] = "Hello, World!";MyContainer<char*> charPtrObj(msg);charPtrObj.display();  // 输出:Specialized Element: Hello, World!return 0;
}
  • 限制: 全特化允许部分模板参数进行特化,适用于部分类型参数的特殊实现。
4.2 偏特化

偏特化允许部分模板参数进行特化,实现更加灵活和细粒度的模板行为。适用于部分类型的特殊实现。偏特化主要用于类模板,函数模板不支持偏特化。

#include <iostream>
#include <string>
using namespace std;// 原始类模版
template <typename T1, typename T2>
class Pair {
private:T1 first;T2 second;
public:Pair(T1 a, T2 b) : first(a), second(b) {}void display() const {cout << "Pair: (" << first << ", " << second << ")" << endl;}
};// 偏特化,当T2为char*时
template <typename T1>
class Pair<T1, char*> {
private:T1 first;char* second;
public:Pair(T1 a, char* b) : first(a), second(b) {}void display() const {cout << "Specialized Pair: (" << first << ", " << second << ")" << endl;}
};int main() {Pair<int, double> p1(1, 3.14);p1.display();  // 输出:Pair: (1, 3.14)char msg[] = "C++ Templates";Pair<string, char*> p2("Topic", msg);p2.display();  // 输出:Specialized Pair: (Topic, C++ Templates)return 0;
}

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

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

相关文章

信息安全工程师(68)可信计算技术与应用

前言 可信计算技术是一种计算机安全体系结构&#xff0c;旨在提高计算机系统在面临各种攻击和威胁时的安全性和保密性。 一、可信计算技术的定义与原理 可信计算技术通过包括硬件加密、受限访问以及计算机系统本身的完整性验证等技术手段&#xff0c;确保计算机系统在各种攻击和…

融合DevOps打造企业高效流程体系的实践与探索

一、引言 转眼间&#xff0c;我已毕业十多年&#xff0c;在IT领域深耕不辍&#xff0c;曾涉足全栈研发、大数据研发、架构设计与项目管理等多个岗位&#xff0c;更主导过公司从市场到交付再到运营的全链条流程建设。在这漫长的职业生涯中&#xff0c;一个问题始终萦绕在我心头&…

Linux安装部署数据库:PostgreSQL14

Linux安装部署数据库&#xff1a;PostgreSQL14 一、安装环境1、虚拟机环境2、下载安装包 二、安装步骤1、在线安装 PGSQL2、离线安装 PGSQL3、源码安装 PGSQL 三、基本操作1、初始化配置2、数据库登录3、常用命令项 四、常见问题1、对用户 "postgres" 的对等认证失败…

App测试环境部署

一.JDK安装 参考以下AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 二.SDK安装 安装地址&#xff1a;https://www.androiddevtools.cn/ 解压 环境变量配置 变量名&#xff1a;ANDROID_SDK_HOME 参考步骤&#xff1a; A…

3GPP协议解读_NTN系列(一)_38.811_非地面网络(NTN)的背景、应用场景和信道建模

非地面网络 1. Scope4. 非地面网络背景介绍4.1 5G中的非地面网络4.2 非地面网络在5G中的用例4.3 卫星和空中接入网的架构4.4 卫星和空中接入网终端的特点4.5 空气/星载飞行器特性4.6 NTN的覆盖模式4.7 NTN网络架构选项4.8 频谱 5. 非地面网络应用场景5.1 应用场景概览5.2 属性介…

CHAPTER 14 Nonlinearity and Mismatc

CHAPTER 14 Nonlinearity and Mismatch 第6,7章我们介绍了两种非理想: 频率响应和噪声. 这一章我们介绍另外两种非理想现象: 非线性和失配. 我们首先定量化nonlinearity, 学习差分电路和反馈系统的非线性, 以及线性化技术. 然后学习差分电路中的失配和dc offset. 最后学习一些…

51单片机之蜂鸣器驱动

1.简介 蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于计算机、打印机、 复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。   压电式蜂鸣器主要…

【C++】vector<string>-动态数组存储多个string

#1024程序员节 | 征文# //demo #include <iostream> #include <vector> #include <string>using namespace std; int main() {// 创建一个存储字符串的向量vector<string> Record;// 向向量中添加字符串Record.push_back("example");Record…

Node-RED的面板的认识及操作

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 文章引言 &#x1f4df; 面板…

深入探索:深度学习在时间序列预测中的强大应用与实现

引言&#xff1a; 时间序列分析是数据科学和机器学习中一个重要的研究领域&#xff0c;广泛应用于金融市场、天气预报、能源管理、交通预测、健康监控等多个领域。时间序列数据具有顺序相关性&#xff0c;通常展示出时间上较强的依赖性&#xff0c;因此简单的传统回归模型往往…

论文略读:Less is More: on the Over-Globalizing Problem in Graph Transformers

2024 ICML 主要观点&#xff1a;Graph Transformer 中的过全局化问题 (Over-Globalizing Problem) 当前 Graph Transformer 的注意力机制过度关注那些远端节点&#xff0c;而实际上包含了大部分有用信息的近端节点则被相对忽视了——>提出了一种新的采用协同训练的两级全局…

【Ubuntu】服务器系统重装SSHxrdpcuda

本文作者&#xff1a; slience_me Ubuntu系统重装操作合集 文章目录 Ubuntu系统重装操作合集1.1 系统安装&#xff1a;1.2 安装openssh-server更新系统包安装OpenSSH服务器检查SSH服务的状态配置防火墙以允许SSH测试SSH连接配置SSH&#xff08;可选&#xff09; 1.3 安装远程连…

力扣之612.平面上的最近距离

文章目录 1. 612.平面上的最近距离1.1 题目说明1.2 准备数据1.3 解法1.4 结果截图 1. 612.平面上的最近距离 1.1 题目说明 Point2D 表&#xff1a; ----------------- | Column Name | Type | ----------------- | x | int | | y | int | ----------------- (x, y) 是该表的…

微信小程序——消息订阅

首先用到的就是wx.requestSubscribeMessage接口。 注意&#xff1a;用户发生点击行为或者发起支付回调后&#xff0c;才可以调起订阅消息界面 requestSubscribeMessage() {uni.requestSubscribeMessage({tmplIds: [],//需要订阅的消息模板的id的集合&#xff0c;一次调用最多可…

阿里云用STS上传oss的完整程序执行流程图 和前端需要哪些参数uniapp

H5 微信小程序可用的前端直传阿里云OSS(STS临时凭证前端签名)直接下载插件 阿里云sts使用官方文档 下面是原理说明&#xff1a; 前端上传文件到阿里云OSS需要携带的具体参数&#xff1a; 从服务器获取的 STS 凭证&#xff1a; // 这些参数需要从你的后端服务器获取 {acc…

66Analytics 汉化版,网站统计分析源码,汉化前台后台

66Analytics 汉化版,网站统计分析源码,汉化前台后台 本源码汉化前台后台&#xff0c;非其他只汉化前台版 网络分析变得容易。自托管、友好、一体化的网络分析工具。轻量级跟踪、会话回放、热图、用户旅程等 简单、好看、友好-大多数网络分析解决方案做得太多了&#xff0c;在大…

Linux 重启命令全解析:深入理解与应用指南

Linux 重启命令全解析&#xff1a;深入理解与应用指南 在 Linux 系统中&#xff0c;掌握正确的重启命令是确保系统稳定运行和进行必要维护的关键技能。本文将深入解析 Linux 中常见的重启命令&#xff0c;包括功能、用法、适用场景及注意事项。 一、reboot 命令 功能简介 re…

商家转账到零钱功能:便捷高效的资金流转新方式

在当今数字化时代&#xff0c;线上支付已成为商业活动中不可或缺的一部分。为了满足商家与消费者之间日益增长的多样化需求&#xff0c;各大支付平台纷纷推出了创新的支付解决方案。其中&#xff0c;“商家转账到零钱”功能便是一项备受瞩目的创新服务&#xff0c;它不仅极大地…

Yii2 init 初始化脚本分析

脚本目的&#xff1a; init 脚本主要的作用是&#xff1a;从 environments 目录中复制配置文件&#xff0c;确保应用适配不同环境&#xff08;例如开发、生产环境等&#xff09;。 工作流程&#xff1a; 获取 $_SERVER 的 argv 参数 加载 environments/index.php 文件&#…

OpenTelemetry 实际应用

介绍 OpenTelemetry“动手”指南适用于想要开始使用 OpenTelemetry 的人。 如果您是 OpenTelemetry 的新手&#xff0c;那么我建议您从OpenTelemetry 启动和运行帖子开始&#xff0c;我在其中详细介绍了 OpenTelemetry。 OpenTelemetry开始改变可观察性格局&#xff0c;它提供…