【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.rhkb.cn/news/458554.html

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

相关文章

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解 目录 时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (创新独家)TTNRBO-VMD改进牛顿-拉夫逊优化算优化变分模态分解TTNRBO–VMD 优化VMD分解层数K和…

设计模式-单例模型(单件模式、Singleton)

单例模式是一种创建型设计模式&#xff0c; 让你能够保证一个类只有一个实例&#xff0c; 并提供一个访问该实例的全局节点。 单例模式同时解决了两个问题&#xff0c; 所以违反了单一职责原则&#xff1a; 保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例…

【Java并发编程】信号量Semaphore详解

一、简介 Semaphore&#xff08;信号量&#xff09;&#xff1a;是用来控制同时访问特定资源的线程数量&#xff0c;它通过协调各个线程&#xff0c;以保证合理的使用公共资源。 Semaphore 一般用于流量的控制&#xff0c;特别是公共资源有限的应用场景。例如数据库的连接&am…

谈谈对函数式编程的理解及rxjs的使用

背景 函数式编程可以说是非常古老的编程方式&#xff0c;但是近几年变成了一个非常热门的话题。不管是Google力推的Go、学术派的Scala与Haskell&#xff0c;还是Lisp的新语言Clojure&#xff0c;这些新的函数式编程语言越来越受到人们的关注。函数式编程思想对前端的影响很大&…

C语言基础题(大合集2)

1. 时间转换 给定秒数 --> 输出秒数 转化成 时/分/秒 //时间转换 //给定秒数 --> 转换成 小时/分/秒 int main() {//输入int seconds 0;int h 0;//小时int m 0;//分钟int s 0;//秒scanf("%d", &seconds);//计算h seconds / 60 / 60;m seconds / 60…

详解varint,zigzag编码, 以及在Go标准库中的实现

文章目录 为啥需要varint编码为啥需要zigzag编码varint编码解码 zigzag编码解码 局限性 为啥需要varint编码 当我们用定长数字类型int32来表示整数时&#xff0c;为了传输一个整数1&#xff0c;我们需要传输00000000 00000000 00000000 00000001 32 个 bits&#xff0c;而有价…

【C++】STL初识

【C】STL初识 文章目录 【C】STL初识前言一、STL基本概念二、STL六大组件简介三、STL三大组件四、初识STL总结 前言 本篇文章将讲到STL基本概念&#xff0c;STL六大组件简介&#xff0c;STL三大组件&#xff0c;初识STL。 一、STL基本概念 STL(Standard Template Library,标准…

QT建立工程时出现了:Reading Project

QT建立工程时出现了&#xff1a;Reading Project 打开建立的工程发现&#xff0c;缺少build文件 从别的工程中复制一个build&#xff0c;点击.pro就可以打开了

【CSS3】css开篇基础(4)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

Spring Boot实现的动态化酒店住宿管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理酒店客房管理系统的相关信息成为必然。开发…

图文详解ChatGPT-o1完成论文写作的全流程

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 本月中旬OpenAI发布了OpenAI o1系列新的AI模型。 据OpenAI介绍&#xff0c;这些模型旨在花更多时间思考后再做出反应&#xff0c;就像人一样。通过训练&#xff0c;它们学会改进思维过…

深度学习(六)CNN:图像处理的强大工具(6/10)

一、CNN 的概述 卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;是深度学习的代表算法之一&#xff0c;在深度学习中占据着重要地位。 CNN 的发展历程可追溯至 20 世纪 80 至 90 年代&#xff0c;时间延迟网络和 LeNet - 5 是最早出现的卷…

conda虚拟环境中安装cuda方法、遇到的问题

conda虚拟环境中安装cuda方法、遇到的问题 文章目录 conda虚拟环境中安装cuda方法、遇到的问题conda虚拟环境中安装cudacuda.h和cuda_runtime.hpytorch运行时的CUDA版本其他问题检查包冲突nvcc -V和nvidia-smi显示的版本不一致cuda路径 conda虚拟环境中安装cuda 参考文章&…

【AIGC】从CoT到BoT:AGI推理能力提升24%的技术变革如何驱动ChatGPT未来发展

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;迈向AGI的新跨越&#x1f4af;BoT与CoT的技术对比技术原理差异推理性能提升应用范围和通用性从错误中学习的能力总结 &#x1f4af;BoT的工作流程和机制初始化过程生成推…

layaair获取组件里的脚本

获取脚本用getComponents方法&#xff0c;但是这个方法里的参数不是脚本的名称。而是组件类型。如果你需要获取脚本&#xff0c;则类型为Laya.Script。挺坑的。我在官网找都没找到这个是这么用的。我猜测的。没想到试了一下成功了。 property(Laya.Node)public img1: Laya.Node…

碰一碰支付系统搭建怎么做?头部公司源码大测评!

随着碰一碰支付dai li骗局的曝光&#xff0c;越来越多的人开始选择将目光转向碰一碰支付系统搭建这一入局方式&#xff0c;连带着与之相关的多个话题&#xff0c;如碰一碰支付系统搭建怎么做等也成为了当前的一大热点。 毕竟&#xff0c;相较于dai li 模式的与第三方公司合作、…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26 前言 本期相关论文可以从“下载” 资源中获取&#xff0c;如果有感兴趣的问题&#xff0c;欢迎交流探讨&#xff01; 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-26前言目…

【C++进阶】C++11(上)

【C进阶】C11(上) &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;C&#x1f96d; &#x1f33c;文章目录&#x1f33c; 1. C11的发展史 2. 列表初始化 2.1 C98的传统{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用…

Kaggle竞赛——灾难推文分类(Disaster Tweets)

目录 1. 准备工作2. 资源导入3. 数据处理4. 绘制词云图5. 数据可视化5.1 词数和字符数可视化5.2 元特征可视化5.3 类别可视化 6. 词元分析6.1 一元语法统计6.2 多元语法统计 7. 命名实体识别8. 推文主题提取9. 构建模型9.1 数据划分与封装9.2 模型训练与验证 10. 模型评估11. 测…