当前位置: 首页 > news >正文

继承机制深度解析:从基础到进阶的完整指南

文章目录

  • 1. 继承的概念及定义
    • 1.1 继承的概念:
    • 1.2继承的定义:
      • 1.2.1 定义格式
      • 1.2.2 继承基类成员访问方式的变化:
    • 1.3继续类模板
  • 2. 基类和派生类间的转换
    • 2.1 向上转换(Upcasting)
    • 2.2 向下转换(Downcasting)
  • 3. 继承中的作用域
    • 3.1 隐藏规则:
    • 3.2 考察继承作用域相关选择题
  • 4. 派生类的默认成员函数
    • 4.1 4个常见默认成员函数
    • 4.2 实现⼀个不能被继承的类
  • 5. 继承与友元
  • 6. 继承与静态成员
  • 7. 多继承及其菱形继承问题
    • 7.1 继承模型
    • 7.2 虚继承
  • 8. 继承和组合

继承是面向对象编程(OOP)中最重要的特性之一,通过继承,子类可以复用父类的属性和方法,减少代码重复。继承使得类之间的层次结构清晰,并实现了代码的模块化和重用。

1. 继承的概念及定义

1.1 继承的概念:

继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承呈现了⾯向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复⽤,继承是类设计层次的复⽤

举例:

#include <iostream>using namespace std;// 创建一个基类,用于身份认证
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证void identity(){// ...cout << "身份认证成功" << endl;}protected:string _name;int _age;string _address;string _tel;
};// 学生类继承 Person
class Student : public Person
{
public:// 学习void study(){cout << "学生正在学习" << endl;}protected:int _stuid; // 学号
};// 教师类继承 Person
class Teacher : public Person
{
public:// 授课void teaching(){cout << "教师正在授课" << endl;}protected:string _title; // 职称
};int main()
{Student s;s.identity();s.study();Teacher t;t.identity();t.teaching();return 0;
}

1.2继承的定义:

1.2.1 定义格式

下⾯我们看到Person是基类,也称作⽗类。Student是派⽣类,也称作⼦类。(因为翻译的原因,所以
既叫基类/派⽣类,也叫⽗类/⼦类)
在这里插入图片描述

1.2.2 继承基类成员访问方式的变化:

讨论了继承后成员的访问权限。例如,公有继承(public)、保护继承(protected)和私有继承(private)会影响基类成员在派生类中的访问权限。这部分内容有助于理解不同的继承方式对代码结构的影响。
在这里插入图片描述
1. 公有继承(public)
基类的 public 成员在子类中仍然是 public。
基类的 protected 成员在子类中仍然是 protected。
基类的 private 成员不可直接访问,但子类可以通过基类的 public 或 protected 成员函数间接访问。
2. 受保护继承(protected)
基类的 public 成员在子类中变为 protected。
基类的 protected 成员在子类中保持 protected。
基类的 private 成员不可直接访问,子类也不能直接访问。
3. 私有继承(private)
基类的 public 成员在子类中变为 private。
基类的 protected 成员在子类中变为 private。
基类的 private 成员不可直接访问,子类也不能直接访问。

上面三点可以总结成下面的这么一个表格

类成员/继承函数public继承protected继承private继承
基类的public成员派生类的pulic成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类不可见在派生类不可见在派生类不可见

总结:
实际上⾯的表格我们进⾏⼀下总结会发现,基类的私有成员在派⽣类都是不可⻅。基类的其他成员在派⽣类的访问⽅式 == Min(成员在基类的访问限定符,继承⽅式),public>protected >private。
需要注意的是:

  1. 使⽤关键字class时默认的继承⽅式是private,使⽤struct时默认的继承⽅式是public,不过最好显示的写出继承⽅式。
  2. 在实际运⽤中⼀般使⽤都是public继承,几乎很少使⽤protetced/private继承,也不提倡使⽤protetced/private继承,因为protetced/private继承下来的成员都只能在派⽣类的类⾥⾯使用,实际中扩展维护性不强。
// 实例演⽰三种继承关系下基类成员的各类型成员访问关系的变化
class Person
{
public:void Print(){cout << _name << endl;}
protected:string _name; // 姓名
private:int _age; // 年龄
};
//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected:int _stunum; // 学号
};

1.3继续类模板

在C++中,可以对模板类进行继承。模板类继承在泛型编程中非常有用,允许派生类在不同数据类型上复用模板基类的功能。这部分会涉及模板继承的具体语法和一些例子,以展示如何通过模板实现通用功能。
举例:

#include <iostream>
using namespace std;// 基类定义
class Animal {
public:// 公有成员函数void sound() {cout << "This is an animal sound." << endl;}// 虚函数,子类可以重写virtual void eat() {cout << "Animal is eating." << endl;}protected:int age; // 受保护成员,可以被子类访问private:int weight; // 私有成员,子类无法直接访问
};// 派生类定义,继承自 Animal 类
class Dog : public Animal {
public:// 重写基类的虚函数void eat() override {cout << "Dog is eating." << endl;}// 新增子类特有的方法void bark() {cout << "Dog barks." << endl;}
};int main() {Animal animal;Dog dog;// 调用基类方法animal.sound();animal.eat();// 调用子类方法dog.sound();          // 继承自基类的方法dog.eat();            // 调用子类重写后的方法dog.bark();           // 调用子类特有的方法return 0;
}

2. 基类和派生类间的转换

在继承体系中,基类和派生类之间可以相互转换。尤其是在多态环境中,这种转换变得尤为重要。
在 C++ 中,基类和派生类之间的转换(类型转换)主要体现在指针和引用的转换。这种转换可以实现多态性,但需要遵循一些规则。这里介绍向上转换(将派生类转换为基类)和向下转换(将基类转换为派生类),并说明它们的用途及注意事项。

  • 向上转型(Upcasting):派生类的对象可以被视为基类的对象。这种转换是安全的,不会导致数据丢失,因为派生类包含了基类的所有成员。
  • 向下转型(Downcasting):将基类指针或引用转换为派生类。这种转换需要谨慎,通常使用动态类型识别(如C++中的dynamic_cast)来确保类型安全。

2.1 向上转换(Upcasting)

向上转换是指将派生类的指针或引用转换为基类的指针或引用。C++ 中,向上转换是隐式的,并且总是安全的,因为派生类包含基类的所有属性和方法。

#include <iostream>
using namespace std;class Animal {
public:virtual void sound() {cout << "Animal sound" << endl;}
};class Dog : public Animal {
public:void sound() override {cout << "Dog barks" << endl;}void bark() {cout << "Dog-specific behavior" << endl;}
};int main() {Dog myDog;Animal* animalPtr = &myDog;  // 隐式向上转换animalPtr->sound();          // 调用的是 Dog 类的 sound,因为 sound 是虚函数// animalPtr->bark();        // 错误:基类指针不能访问派生类特有的成员return 0;
}

2.2 向下转换(Downcasting)

向下转换是指将基类的指针或引用转换为派生类的指针或引用。向下转换不如向上转换安全,因为基类并不一定包含派生类的所有信息。为了避免运行时错误,向下转换通常需要使用 C++ 的 dynamic_cast。

#include <iostream>
using namespace std;class Animal {
public:virtual void sound() {cout << "Animal sound" << endl;}
};class Dog : public Animal {
public:void sound() override {cout << "Dog barks" << endl;}void bark() {cout << "Dog-specific behavior" << endl;}
};int main() {Animal* animalPtr = new Dog; // 向上转换Dog* dogPtr = dynamic_cast<Dog*>(animalPtr); // 向下转换if (dogPtr) {dogPtr->bark(); // 安全访问 Dog 类特有的成员} else {cout << "Conversion failed" << endl;}delete animalPtr;return 0;
}
  • 向上转换:隐式且安全,常用于多态场景,子类对象可以被赋值给基类指针或引用。
  • 向下转换:需要显式进行,通常通过 dynamic_cast 或 static_cast,用来从基类指针访问派生类特有的功能。

3. 继承中的作用域

3.1 隐藏规则:

  1. 在继承体系中基类和派⽣类都有独⽴的作⽤域。
  2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。(在派生类成员函数中,可以使用基类::基类成员显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  4. 注意在实际中在继承体系⾥⾯最好不要定义同名的成员。
// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆 
class Person
{
protected :string _name = "⼩李⼦"; // 姓名 int _num = 111; // ⾝份证号 
};
class Student : public Person
{
public:void Print(){cout<<" 姓名:"<<_name<< endl;cout<<" ⾝份证号:"<<Person::_num<< endl;cout<<" 学号:"<<_num<<endl;}
protected:int _num = 999; // 学号 
};
int main()
{Student s1;s1.Print();return 0;
};

3.2 考察继承作用域相关选择题

这部分包含一些选择题,通过考察不同情况下的代码表现帮助理解作用域规则。
A和B类中的两个func构成什么关系()
A. 重载 B. 隐藏 C.没关系
下面程序的编译运⾏结果是什么()
A. 编译报错 B. 运⾏报错 C. 正常运⾏

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};
int main()
{B b;b.fun(10);b.fun();return 0;
};

在这个代码示例中:
class A 定义了一个无参的 fun 函数。
class B 继承自 A,并定义了一个有参的 fun(int i) 函数。
在 C++ 中,如果派生类(B)中定义了与基类(A)同名但参数不同的函数,则该派生类中的函数会隐藏基类中的同名函数。这称为函数隐藏,而不是函数重载。

因此,第一题的正确答案是:B. 隐藏

在 main 函数中,b.fun(10); 调用的是 class B 中的 fun(int i) 函数,因此会输出 func(int i)10。

接着 b.fun(); 试图调用无参的 fun 函数。然而,由于 class B 中的 fun(int i) 隐藏了 class A 中的 fun(),编译器在 class B 中找不到无参的 fun 函数,从而导致编译错误。

因此,第二题的正确答案是:A. 编译报错

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

4.1 4个常见默认成员函数

6个默认成员函数,默认的意思就是指我们不写,编译器会变我们⾃动⽣成⼀个,那么在派⽣类中,这几个成员函数是如何⽣成的呢?

  1. 派⽣类的构造函数必须调⽤基类的构造函数初始化基类的那⼀部分成员。如果基类没有默认的构造函数,则必须在派⽣类构造函数的初始化列表阶段显⽰调⽤。
  2. 派⽣类的拷⻉构造函数必须调⽤基类的拷⻉构造完成基类的拷⻉初始化。
  3. 派⽣类的operator=必须要调⽤基类的operator=完成基类的复制。需要注意的是派⽣类的operator=隐藏了基类的operator=,所以显⽰调⽤基类的operator=,需要指定基类作⽤域
  4. 派⽣类的析构函数会在被调⽤完成后⾃动调⽤基类的析构函数清理基类成员。因为这样才能保证派⽣类对象先清理派⽣类成员再清理基类成员的顺序。
  5. 派⽣类对象初始化先调⽤基类构造再调派⽣类构造。
  6. 派⽣类对象析构清理先调⽤派⽣类析构再调基类的析构。
  7. 因为多态中⼀些场景析构函数需要构成重写,重写的条件之⼀是函数名相同那么编译器会对析构函数名进⾏特殊处理,处理成destructor(),所以基类析构函数不virtual的情况下,派生类析构函数和基类析构函数构成隐藏关系。
    在这里插入图片描述
#include<iostream>
using namespace std;
class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const char* name, int num): Person(name), _num(num){cout << "Student()" << endl;}Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){// 构成隐藏,所以需要显⽰调⽤Person::operator =(s);_num = s._num;}return *this;}~Student(){cout << "~Student()" << endl;}
protected:int _num; //学号
};
int main()
{Student s1("jack", 18);Student s2(s1);Student s3("rose", 17);s1 = s3;return 0;
}

4.2 实现⼀个不能被继承的类

  • ⽅法1:基类的构造函数私有,派⽣类的构成必须调⽤基类的构造函数,但是基类的构成函数私有化以后,派⽣类看不⻅就不能调⽤了,那么派⽣类就⽆法实例化出对象。
  • ⽅法2:C++11新增了⼀个final关键字,final修改基类,派⽣类就不能继承了。
// C++11的⽅法
class Base final
{
public:void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
private:// C++98的⽅法/*Base(){}*/
};
class Derive :public Base
{void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};
int main()
{Base b;Derive d;return 0;
}

5. 继承与友元

友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员 。

友元函数或友元类可以访问类的私有成员。在继承关系中,友元允许某些特殊的函数或类访问基类和派生类的内部细节。友元的合理使用能够提高类之间的协作效率,但不当使用可能破坏封装性。

class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
int main()
{Person p;Student s;// 编译报错:error C2248: “Student::_stuNum”: ⽆法访问 protected 成员// 解决⽅案:Display也变成Student 的友元即可Display(p, s);return 0;
}

6. 继承与静态成员

基类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员。⽆论派⽣出多少个派⽣类,都只有⼀个static成员实例。

class Person
{
public:string _name;static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:int _stuNum;
};
int main()
{Person p;Student s;// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的// 说明派⽣类继承下来了,基派⽣类对象各有⼀份cout << &p._name << endl;cout << &s._name << endl;// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的// 说明派⽣类和基类共⽤同⼀份静态成员cout << &p._count << endl;cout << &s._count << endl;// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员cout << Person::_count << endl;cout << Student::_count << endl;return 0;
}

7. 多继承及其菱形继承问题

7.1 继承模型

单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承
多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。
菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。
我们常见的cout以及cin就是istream以及ostream两个的库通过菱形继承组成的
在这里插入图片描述

class Person
{
public:string _name; // 姓名
};
class Student : public Person
{
protected:int _num; //学号
};
class Teacher : public Person
{
protected:int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
int main()
{// 编译报错:error C2385: 对“_name”的访问不明确Assistant a;a._name = "peter";// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

7.2 虚继承

很多⼈说C++语法复杂,其实多继承就是⼀个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。

class Person
{
public:string _name; // 姓名
};
class Student : public Person
{
protected:int _num; //学号
};
class Teacher : public Person
{
protected:int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
int main()
{// 编译报错:error C2385: 对“_name”的访问不明确Assistant a;a._name = "peter";// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}
class Person
{
public:string _name; // 姓名/*int _tel;* int _age;
string _gender;
string _address;*/
// ...
};
// 使⽤虚继承Person类
class Student : virtual public Person
{
protected:int _num; //学号
};
// 使⽤虚继承Person类
class Teacher : virtual public Person
{
protected:int _id; // 职⼯编号
};
// 教授助理
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
int main()
{// 使⽤虚继承,可以解决数据冗余和⼆义性Assistant a;a._name = "peter";return 0;
}

我们可以设计出多继承,但是不建议设计出菱形继承,因为菱形虚拟继承以后,⽆论是使⽤还是底层都会复杂很多。当然有多继承语法⽀持,就⼀定存在会设计出菱形继承,像Java是不⽀持多继承的,就避开了菱形继承。

7.3 多继承中指针偏移问题?下⾯说法正确的是( )
A:p1 ==p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3

class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{Derive d;Base1* p1 = &d;Base2* p2 = &d;Derive* p3 = &d;return 0;
}

解析:

p1 是 Base1* 类型,指向 d 对象中 Base1 部分的地址。
p2 是 Base2* 类型,指向 d 对象中 Base2 部分的地址。
p3 是 Derive* 类型,指向整个 Derive 对象的起始地址。
由于 Derive 对象的起始地址与 Base1 部分的起始地址相同,因此 p1 和 p3 实际上指向的是同一个地址。这是因为 Base1 是多重继承中第一个继承的基类,因此 Derive 对象的起始位置就是 Base1 子对象的位置。

相对的,p2 指向的是 Base2 的部分,位于 Derive 对象中 Base1 部分之后的位置,因此 p2 的地址与 p1 和 p3 不同。

8. 继承和组合

• public继承是⼀种is-a的关系。也就是说每个派⽣类对象都是⼀个基类对象。
• 组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。
• 继承允许你根据基类的实现来定义派⽣类的实现。这种通过⽣成派⽣类的复⽤通常被称为⽩箱复⽤(white-box reuse)。术语“⽩箱”是相对可视性⽽⾔:在继承⽅式中,基类的内部细节对派⽣类可⻅ 。继承⼀定程度破坏了基类的封装,基类的改变,对派生类有很⼤的影响。派⽣类和基类间的依赖关系很强,耦合度⾼。
• 对象组合是类继承之外的另⼀种复⽤选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接⼝。这种复⽤⻛格被称⿊箱复⽤(black-box reuse),因为对象的内部细节是不可⻅的。对象只以“⿊箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使⽤对象组合有助于你保持每个类被封装。
• 优先使⽤组合,⽽不是继承。实际尽量多去⽤组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,类之间的关系就适合继承(is-a)那就⽤继承,另外要实现多态,也必须要继承。类之间的关系既适合⽤继承(is-a)也适合组合(has-a),就⽤组合。

// Tire(轮胎)和Car(⻋)更符合has-a的关系
class Tire {
protected:string _brand = "Michelin"; // 品牌size_t _size = 17; // 尺⼨
};
class Car {
protected:string _colour = "⽩⾊"; // 颜⾊string _num = "陕ABIT00"; // ⻋牌号Tire _t1; // 轮胎Tire _t2; // 轮胎Tire _t3; // 轮胎Tire _t4; // 轮胎
};
class BMW : public Car {
public:void Drive() { cout << "好开-操控" << endl; }
};
// Car和BMW/Benz更符合is-a的关系
class Benz : public Car {
public:void Drive() { cout << "好坐-舒适" << endl; }
};
template<class T>
class vector
{};
// stack和vector的关系,既符合is-a,也符合has-a
template<class T>
class stack : public vector<T>
{};
template<class T>
class stack
{
public:vector<T> _v;
};
int main()
{return 0;
}

相关文章:

继承机制深度解析:从基础到进阶的完整指南

文章目录 1. 继承的概念及定义1.1 继承的概念&#xff1a;1.2继承的定义&#xff1a;1.2.1 定义格式1.2.2 继承基类成员访问方式的变化&#xff1a; 1.3继续类模板 2. 基类和派生类间的转换2.1 向上转换&#xff08;Upcasting&#xff09;2.2 向下转换&#xff08;Downcasting&…...

8. 数据结构——邻接表、邻接矩阵的基本操作

一、邻接表 1. 内容 2. 实现代码(直接可以复制使用) //邻接表的相关操作 #include<bits/stdc.h> #define MVnum 100 #define OK 1 #define ERROR -1 using namespace std;typedef int Status; typedef char VerTexType; //假设顶点的数据类型为char typedef int ArcT…...

OpenCV Python 版使用教程(二)摄像头调用

文章目录 一、上篇回顾二、使用步骤1. 调用摄像头的 API 介绍2. 代码示例3. 代码分析 三、下篇预告 一、上篇回顾 在上一篇中&#xff0c;简单介绍了如何在 Windows 和 Ubuntu 两个环境下部署和安装 OpenCV&#xff0c;从本篇开始将逐步介绍 OpenCV 中的常见操作。 本篇介绍 …...

基础算法——排序算法(冒泡排序,选择排序,堆排序,插入排序,希尔排序,归并排序,快速排序,计数排序,桶排序,基数排序,Java排序)

1.概述 比较排序算法 算法最好最坏平均空间稳定思想注意事项冒泡O(n)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)Y比较最好情况需要额外判断选择O( n 2 n^2 n2)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)N比较交换次数一般少于冒泡堆O( n l o g n nlogn nlogn)O( n l o g n nlogn nlogn)O( n l…...

几种常见的处理ARP欺骗的方法:静态ARP表和VLAN等

ARP&#xff08;Address Resolution Protocol&#xff09;欺骗是一种常见的网络攻击手段&#xff0c;攻击者通过伪造ARP响应&#xff0c;将网关的MAC地址指向攻击者的MAC地址&#xff0c;从而截获或篡改网络流量。为了应对ARP欺骗攻击&#xff0c;现代网络设备和管理员采取了一…...

突破1200°C高温性能极限!北京科技大学用机器学习合成24种耐火高熵合金,室温延展性极佳

在工程应用中&#xff0c;如燃气轮机、核反应堆和航空推进系统&#xff0c;对具备优异高温机械性能的金属合金需求十分旺盛。由于材料熔点的固有限制&#xff0c;传统镍基 (Ni) 高温合金的耐温能力已接近极限。为满足开发高温结构材料的需求&#xff0c;耐火高熵合金 (RHEAs) 于…...

ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源 或者超时失效

数据治理过程中&#xff0c;有字段长度不够&#xff0c;扩展字段&#xff0c;报&#xff1a;ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源 或者超时失效 ALTER TABLE LAPD_RSJ_CXJMYLBXCBXX MODIFY HKXZ VARCHAR2(10);错误表示当前会话在试图访问的资源&#xff08;通常…...

Python学习笔记-断点操作结合异常处理

在编程中,调试和错误处理是提升代码质量和开发效率的关键环节。调试能帮助识别并修复问题,异常处理则使得程序能在出现错误时有效地管理而不至于崩溃。断点与异常处理的结合应用是高级编程中不可或缺的技巧,能够帮助更高效地定位问题,提高程序的鲁棒性。 本文将通过详细的…...

Java实现JWT登录认证

文章目录 什么是JWT?为什么需要令牌?如何实现?添加依赖&#xff1a;JwtUtils.java&#xff08;生成、解析Token的工具类&#xff09;jwt配置&#xff1a;登录业务逻辑&#xff1a;其他关联代码&#xff1a;测试&#xff1a; 什么是JWT? JWT&#xff08;Json Web Token&…...

「Mac畅玩鸿蒙与硬件20」鸿蒙UI组件篇10 - Canvas 组件自定义绘图

Canvas 组件在鸿蒙应用中用于绘制自定义图形&#xff0c;提供丰富的绘制功能和灵活的定制能力。通过 Canvas&#xff0c;可以创建矩形、圆形、路径、文本等基础图形&#xff0c;为鸿蒙应用增添个性化的视觉效果。本篇将介绍 Canvas 组件的基础操作&#xff0c;涵盖绘制矩形、圆…...

山东路远生态科技有限公司竣工投产仪式暨产品发布会圆满举行

第二十届三中全会于2024年7月15日至18日在北京举行。全会审议通过了《关于进一步全面深化改革、推进中国式现代化的决定》。其中提到,“要健全因地制宜发展新质生产力体制机制”。 新质生产力是由技术革命性突破、生产要素创新性配置、产业深度转型升级而催生的当代先进生产力…...

java: 题目:银行账户管理系统

题目&#xff1a;银行账户管理系统 设计一个简单的银行账户管理系统。要求实现以下功能&#xff1a; 1. 创建一个银行账户 BankAccount 类&#xff0c;该类具有以下属性&#xff1a;accountNumber&#xff08;账户号码&#xff0c;类型为 String&#xff09; balance&#xff…...

PH热榜 | 2024-11-06

DevNow 是一个精简的开源技术博客项目模版&#xff0c;支持 Vercel 一键部署&#xff0c;支持评论、搜索等功能&#xff0c;欢迎大家体验。 Github&#xff1a;https://github.com/LaughingZhu/DevNow 1. MindOne Builder 标语&#xff1a;这是一个“设计优先”的应用构建工具…...

五、Java并发 Java Google Guava 实现

Guava 是托管在 Github.com 上的流行的 Google 开源的 Java 线程池库。 Guava 包含了许多有用的并发类&#xff0c;同时还包含了几个方便的 ExecutorService 实现&#xff0c;但这些实现类都无法通过直接实例化或子类化来创建实例。取而代之的是提供了 MoreExecutors 助手类来…...

ssm公交车信息管理系统+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码看文章最下面 需要定制看文章最下面 目 录 摘要 I Abstract II 第1章 绪 论 1 1.1 研究背景 1 1.2 研究意义 1 1.3 国内外研究现状 …...

如何删除react项目的默认图标,使在浏览器中不显示默认图标favicon.ico

要删除 React 项目的默认图标&#xff0c;使在浏览器中不显示默认图标favicon.ico&#xff0c;其实有两种方法&#xff1a; 方法一 方法要点&#xff1a;删除掉 public 目录下的 favicon.ico 文件&#xff0c;再用浏览器访问时&#xff0c;如果加载不到图标文件&#xff0c;就…...

【React】react-app-env.d.ts 文件

在使用 create-react-app 生成的 TypeScript 项目模板中&#xff0c;react-app-env.d.ts 文件的作用是为 React 应用中的全局变量和类型进行声明。 全局类型声明&#xff1a;react-app-env.d.ts 文件会引入 react-scripts 提供的全局类型定义&#xff0c;这些类型定义扩展了 Ty…...

设计模式讲解01-建造者模式(Builder)

1. 概述 建造者模式也称为&#xff1a;生成器模式 定义&#xff1a;建造者模式是一种创建型设计模式&#xff0c;它允许你将创建复杂对象的步骤与表示方式相分离。 解释&#xff1a;建造者模式就是将复杂对象的创建过程拆分成多个简单对象的创建过程&#xff0c;并将这些简单…...

wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 wflow-web是一个开源的工作流设计器&#xff0c;它支持可视化拖拽表单组件&#xff0c;动态任意层级结构审批节点&#xff0c;以及复杂流程条件的设置…...

Promise 简单介绍及深入挖掘

一、什么是 Promise&#xff1f; 在 JavaScript 中&#xff0c;Promise 是用于处理异步操作的一种方式。它代表了一个 可能 在将来某个时间点完成或失败的操作的结果。Promise 使得我们能够优雅地处理异步代码&#xff0c;避免了回调地狱&#xff08;Callback Hell&#xff09;…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...

背包问题双雄:01 背包与完全背包详解(Java 实现)

一、背包问题概述 背包问题是动态规划领域的经典问题&#xff0c;其核心在于如何在有限容量的背包中选择物品&#xff0c;使得总价值最大化。根据物品选择规则的不同&#xff0c;主要分为两类&#xff1a; 01 背包&#xff1a;每件物品最多选 1 次&#xff08;选或不选&#…...

二叉树-144.二叉树的前序遍历-力扣(LeetCode)

一、题目解析 对于递归方法的前序遍历十分简单&#xff0c;但对于一位合格的程序猿而言&#xff0c;需要掌握将递归转化为非递归的能力&#xff0c;毕竟递归调用的时候会调用大量的栈帧&#xff0c;存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧&#xff0c;而非…...

旋量理论:刚体运动的几何描述与机器人应用

旋量理论为描述刚体在三维空间中的运动提供了强大而优雅的数学框架。与传统的欧拉角或方向余弦矩阵相比&#xff0c;旋量理论通过螺旋运动的概念统一了旋转和平移&#xff0c;在机器人学、计算机图形学和多体动力学领域具有显著优势。这种描述不仅几何直观&#xff0c;而且计算…...

智慧城市项目总体建设方案(Word700页+)

1 背景、现状和必要性 1.1 背景 1.1.1 立项背景情况 1.1.2 立项依据 1.2 现状 1.2.1 党建体系运行现状 1.2.2 政务体系运行现状 1.2.3 社会治理运行现状 1.2.4 安全监管体系现状 1.2.5 环保体系运行现状 1.2.6 城建体系运行现状 1.2.7 社区体系运行现状 1.2.8 园区…...