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

【C++进阶篇】像传承家族宝藏一样理解C++继承

文章目录

须知

💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!

👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步! 

《深入剖析C++继承:从基础到进阶的完整指南》

 1. C++继承前言与背景

1.1 前言

C++是一个功能强大的面向对象编程语言,它不仅支持过程式编程,还在此基础上提供了许多用于构建复杂软件系统的面向对象特性。继承是C++中最为核心的概念之一,它允许我们通过现有的类(基类)创建新的类(派生类),从而实现代码的重用和扩展。继承是面向对象编程的三个基本特性之一(另外两个是封装和多态),在设计模式、软件架构和大型系统开发中起着至关重要的作用。

理解和应用C++继承的概念对于编写高效、可维护和可扩展的代码至关重要。C++的继承不仅仅是一个简单的“类与类之间的关系”,它涉及到如何组织和管理对象之间的共享数据、方法以及如何利用多态实现代码的灵活性。因此,C++继承的深入理解对程序员来说是必须的,它能够帮助开发者更好地设计类的层次结构,提升软件系统的复用性和扩展性。

1.2 背景

继承源于面向对象编程的基本思想,即通过创建类的层次结构,模拟现实世界中的事物关系。在现实世界中,物体之间往往存在父子关系、包含关系、继承关系等。C++的继承机制正是通过类与类之间的继承关系来模拟这些现实中的关系。继承使得开发者能够从一个基类派生出多个派生类,从而共享基类的行为,并在需要时对其进行扩展或修改。

在C++中,继承是通过class关键字和访问修饰符(如publicprotectedprivate)来实现的,基类(父类)提供了一些公有和保护成员,派生类(子类)可以继承这些成员。继承还允许派生类重写基类的方法(方法重写),并能够通过虚函数实现运行时多态性,这是C++继承特性的重要组成部分。

C++继承的强大之处在于它不仅仅支持单一继承,还支持多继承,这使得它可以适应更复杂的类关系。通过使用虚拟继承,C++避免了传统多继承中可能出现的“钻石继承”问题。此外,C++继承支持访问控制(如publicprotectedprivate继承),从而为开发者提供了灵活的类设计和组织结构的能力。

然而,C++继承的设计和使用也存在一些挑战,特别是在多继承和虚继承的场景下。理解如何合理使用继承关系,避免继承层次过深,避免继承滥用,是程序员需要掌握的关键技能。

C++继承的关键要点:

  1. 代码重用:继承使得子类能够复用父类的属性和方法,减少重复代码。
  2. 扩展性:通过继承,子类可以扩展或修改父类的行为,从而实现系统的扩展。
  3. 多态性:继承和虚函数的结合使得C++能够实现运行时多态,从而使代码更加灵活和动态。
  4. 多继承与虚继承:C++支持多继承和虚继承,这为开发者提供了强大的功能,但也增加了代码设计的复杂度。
  5. 访问控制:C++提供了不同的继承访问权限(publicprotectedprivate),允许开发者控制基类成员的访问权限,确保程序设计的封装性和安全性。

 2.引言:C++继承的核心意义

继承是面向对象编程的一个关键特性,它能够使得代码更加简洁、可扩展和易维护。在C++中,继承不仅仅是类之间的关系,更是构建复杂系统的基石。通过继承,我们可以在一个类中共享另一个类的功能,而不需要重复编写相同的代码。

在这篇博客中,我们将深入探讨C++中的继承,包括其基础概念、应用场景、常见问题以及一些进阶技巧。通过示例和图示,您将能够更好地理解继承的各个方面,并能够在项目中有效运用。 

3.继承基本概念与定义

3.1 什么是C++继承——从基本概念开始

继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段,它允许程序员在
持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承 呈现了面向对象
程序设计的层次结构 ,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,
承是类设计层次的复用。
3.1.1 示例代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter"; // 姓名int _age = 18;//年龄
};
class Student : public Person
{
protected:int _stuid; // 学号
};
class Teacher : public Person
{
protected:int _jobid; // 工号
};
int main()
{Student s;Teacher t;s.Print();t.Print();return 0;
}

输出:

name:peter
age:18
name:peter
age:18

在上面的代码中,Student类继承了Person类,因此可以访问Person类中的Print()方法。 

 3.2 继承的定义

继承在 C++ 中的定义主要通过以下格式实现:

class 子类名 : 继承方式 基类名 {
    // 子类的成员
}; 

继承语法:

  • 使用publicprivateprotected访问权限来决定继承的可访问性。
    • public继承:基类的公有成员在派生类中仍然是公有的。
    • protected继承:基类的公有成员在派生类中变为受保护的。
    • private继承:基类的公有成员在派生类中变为私有的。

 

示例代码:

class Teacher : public Person {
protected:int _jobid;  // 工号
};int main() {Student s;Teacher t;s.Print();t.Print();return 0;
}

派生类StudentTeacher都继承基类(父类)Person类的成员方法函数Print(),通过s.Print()t.Print()输出 Student 和 Teacher 对象的姓名和年龄。

4. 继承中的访问权限

4.1 基类成员在派生类中的访问权限

基类的 publicprotected 和 private 成员在派生类中的访问权限取决于继承方式。下面是不同继承方式下的访问权限表:

 从表中可以看出基类的private成员在派生类(子类)始终不可见,而基类的public成员和protected成员的是否能被访问取决于本身成员的访问权限与继承方式,两个取继承方式最坎坷的一个。

注意:1-> 如果需要基类的某个成员在派生类中可访问但不希望类外部访问,则可以将其设置为 protected,这样可以更好地控制访问权限。

2-> 使用关键字 class 时默认的继承方式是 private ,使用 struct 时默认的继承方式是 public 不过
最好显示的写出继承方式

 

4.2  基类和派生类对象赋值转换

在C++中,基类和派生类对象的赋值转换是一个比较常见的操作场景。通常情况下,派生类对象可以赋值给基类对象,或者通过基类的指针或引用来操作派生类对象。这种转换机制使得C++在继承结构中实现了多态和代码复用。但需要注意的是,基类对象不能直接赋值给派生类对象。 

4.2.1 派生类对象赋值给基类对象 

 派生类对象包含了基类的成员,因此派生类对象赋值给基类对象时将属于基类对象的那一部分赋值给基类对象。这里有个形象的说法叫切片(切割)。寓意把派生类中父类那部分切来赋值过去。

示例代码:

#include<iostream>
#include<string>
using namespace std;class Person
{
protected:string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};
class Student : public Person
{
public:int _No; // 学号
};
void Test()
{Student sobj;// 1.子类对象可以赋值给父类对象/指针/引用Person pobj = sobj;Person* pp = &sobj;Person& rp = sobj;//2.基类对象不能赋值给派生类对象sobj = pobj;//error// 3.基类的指针可以通过强制类型转换赋值给派生类的指针pp = &sobj;Student * ps1 = (Student*)pp; // 这种情况转换时可以的。ps1->_No = 10;pp = &pobj;Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题ps2->_No = 10;
}
 4.2.2 基类指针与引用转换

派生类对象可以赋值给基类的指针或引用,这是实现多态的重要前提条件。通过基类指针或引用,程序可以在运行时动态绑定到派生类的成员函数。这种方式允许我们在不需要修改代码的情况下扩展程序的功能。

 示例代码:
 

#include<iostream>
#include<string>
using namespace std;class Person
{
public:virtual void Print()//具有二义性,所以添加了Virtual{cout << "Person: " << _name << endl;}
protected:string _name="Jack";
};class Student:public Person
{
public:void Print()override{cout << "Student: " << _age <<" name:"<<_name << endl;}
private:int _age=20;
};void PrintPersonInfo(Person& p) {p.Print();  // 基类引用调用虚函数,实现多态
}int main() {Student s;PrintPersonInfo(s);  // 输出 "Student: 20, name: Jack"return 0;
}

在这个例子中,我们通过基类 Person 的引用调用 Student 类中的 Print() 函数,实现了运行时多态。派生类对象 s 被传递给基类引用 p,并正确调用了 Student 类的重写函数 Print()

 4.2.3 强制类型转换

在某些特殊情况下,基类指针或引用可能需要转换为派生类的指针或引用。C++ 提供了 dynamic_caststatic_cast 等多种类型转换方式。在继承关系中,使用 dynamic_cast 进行安全的类型转换尤为重要,特别是在处理多态时。

Person* pp = new Student();  // 基类指针指向派生类对象
Student* dc = dynamic_cast<Student*>(pp);  // 安全的向下转换
if (dc) 
{
    dc->Print();
}
else {
    cout << "Type conversion failed!" << endl;

dynamic_cast 在运行时进行类型检查,确保转换是安全的。如果转换失败,将返回 nullptr,从而避免越界访问的风险。 

 5. 继承中的作用域与成员访问

5.1 作用域的独立性与同名成员的隐藏

在继承关系中,基类与派生类各自拥有独立的作用域。如果派生类中定义了与基类成员同名的变量或函数,基类的同名成员将被隐藏,这种现象称为隐藏(Hiding)也叫重定义同名成员在派生类中会覆盖基类中的成员,导致基类成员无法被直接访问。

示例代码:

#include<iostream>
#include<string>
using namespace std;// 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 s;s.Print();return 0;}

 输出:

 姓名:小李子
 身份证号:111
 学号:999

 从输出结果可以看出,Student 类中定义了一个 _num 变量,它隐藏了基类 Person 中的同名变量。为了访问基类的 _num,我们使用了 Person::_num 来显式地指定访问基类中的成员。这样可以避免由于成员同名而导致的混淆。

实际开发中不建议写同名的变量名或函数名。

 5.1.1 函数的隐藏

在C++中,函数隐藏指的是子类中定义的一个与父类中已有的成员函数具有相同名称和参数列表的函数,导致父类的函数在子类中被“隐藏”或“遮蔽”的现象。这种情况通常发生在子类中定义了一个与父类中同名的函数时,父类的函数就不再可见或无法被直接调用,除非通过特定方式(如使用作用域解析符::)显式访问。 

示例代码:

class Teacher
{
public:void Print()const{cout << "Teacher:" << _tel << endl;}
private:string _tel="123456678";
};class Student:public Teacher
{
public:void Print()const{cout << "Student:" << _tel<<" age:"<<age<<" _name:"<<_name << endl;}
private:string _tel = "0123456789";int age = 18;string _name = "Mark";
};int main()
{Student s;s.Print();return 0;
}

 输出:

Student:0123456789 age:18 _name:Mark

从结果可以看出: 派生类Student中的_tel="012356789"隐藏父类Teacher中的_tel="123456678",若大家强制想访问父类Teacher,可以使用Teacher::_tel。

与函数重载区别:

重载作用于同一个作用域,而隐藏作用于不同的作用域,因此隐藏不构成重载(Overloading

 构成函数隐藏的条件不是很苛刻,只需要函数名或变量名相同即可。

5.2 派生类的默认成员函数

在 C++ 中,当我们不显式定义类的构造函数、拷贝构造函数、赋值运算符和析构函数时,编译器会自动为我们生成这些函数。这些自动生成的函数在派生类中也会涉及到对基类成员的操作,因此在继承体系中了解这些默认成员函数的调用规则非常重要。

5.2.1 构造函数的调用顺序

 派生类对象构造过程中,基类的对象会首先调用构造函数进行初始化,其次派生类再调用构造函数进行初始化。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

 示例代码:

class Teacher
{
public:Teacher(const string& name):_name(name){cout << "Teacher constructor called!" << endl;}
private:string _name;
};class Student :public Teacher
{
public:Student(const string& name, int id):Teacher(name)//使用匿名构造函数完成初始化,_id(id){cout << "Student constructor called!" << endl;}
private:int _id;
};int main()
{Student s("Bob", 20241128);return 0;
}

输出:

Teacher constructor called!
Student constructor called!

从结果可以看出,先调用父类的构造,然后再调用派生类的构造函数。这种调用顺序确保基类的成员在派生类构造之前就已经被正确初始化。

 5.2.2  拷贝构造函数与赋值运算符的调用

当派生类对象被拷贝时,基类的拷贝构造函数会先被调用,然后才是派生类的拷贝构造函数。同样,赋值运算符的调用顺序也遵循这一规则:基类的赋值运算符会先于派生类的赋值运算符被调用。 

 示例代码:

class Teacher
{
public:Teacher(const string& name):_name(name){}//拷贝构造函数Teacher(const Teacher& t){_name = t._name;cout << "Teacher copy constructor called!" << endl;}//赋值运算符重载Teacher& operator=(const Teacher& t){_name = t._name;cout << "Teacher assignment operator called!" << endl;return *this;}
protected:string _name;
};class Student :public Teacher
{
public:Student(const string& name, int id):Teacher(name)//使用匿名构造函数完成初始化, _id(id){}//拷贝构造函数Student(const Student& s):Teacher(s)//基类没有默认构造函数,则派生类的构造函数必须在初始化列表中显式调用基类的构造函数。{_id = s._id;cout << "Student copy constructor called!" << endl;}//赋值运算符重载Student& operator=(const Student& s){Teacher::operator=(s);//先调用基类的赋值运算符重载_id = s._id;cout << "Student assignment operator called!" << endl;return *this;}protected:int _id;
};int main()
{Student s1("Bob", 20241128);Student s2=s1;//拷贝构造Student s3("Jack", 2345);s1 = s3;//赋值运算符重载return 0;
}

输出:

Teacher copy constructor called!
Student copy constructor called!
Teacher assignment operator called!
Student assignment operator called!

从结果可以看出基类的拷贝构造和赋值运算符重载优先级优于派生类。为了保证派生类对象的完整性,派生类的拷贝构造函数和赋值运算符必须调用基类的相应函数,确保基类成员正确处理。

5.2.3 析构函数调用顺序

 与构造函数的调用顺序相反,析构函数的调用顺序是先调用派生类的析构函数,然后再调用基类的析构函数。这确保了派生类的资源先被释放,然后基类的资源才能安全地释放。

示例代码:

class Person
{
public:Person(const int& age) : _age(age) {}~Person(){cout << "Person destructor called!" << endl;}
protected:int _age;
};class Student:public Person
{
public:Student(const int& age, const string& name):Person(age), _name(name){}~Student(){cout << "Student destructor called!" << endl;}
protected:string _name;
};int main()
{Student s(20, "Mark");return 0;
}

输出:

Student destructor called!
Person destructor called!

从结果可以看出,派生类Student先调用析构函数,进行类对象清理资源,然后再是基类Person调用析构函数,完成类对象资源清理,从而确保所有派生类的资源被正确释放。

5.2.4 虚析构函数

在继承体系中,若希望基类指针指向派生类对象,并通过该指针安全地释放对象,基类的析构函数应当定义为虚函数。否则,仅会调用基类的析构函数,导致派生类资源没有正确释放,从而引发内存泄漏。 

示例代码:

考虑以下示例,展示了没有虚析构函数时会导致资源未释放的情况: 

#include <iostream>
using namespace std;class Base {
public:Base() { cout << "Base Constructor" << endl; }~Base() { cout << "Base Destructor" << endl; }  // 非虚析构函数
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor" << endl; }~Derived() { cout << "Derived Destructor" << endl; }  // 派生类析构函数
};int main() {Base* basePtr = new Derived();delete basePtr;  // 只调用Base的析构函数,没有调用Derived的析构函数return 0;
}

输出:

Base Constructor
Derived Constructor
Base Destructor 

解释:

 如上所示,当delete basePtr被调用时,基类的析构函数Base::~Base()被调用,但派生类的析构函数Derived::~Derived()没有被调用。这样,Derived类中的资源(例如动态分配的内存、文件句柄等)就没有被正确释放,导致内存泄漏。

正确的做法是将基类的析构函数声明为虚函数: 

#include <iostream>
using namespace std;class Base {
public:Base() { cout << "Base Constructor" << endl; }virtual ~Base() { cout << "Base Destructor" << endl; }  // 虚析构函数
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor" << endl; }~Derived() { cout << "Derived Destructor" << endl; }  // 派生类析构函数
};int main() {Base* basePtr = new Derived();delete basePtr;  // 会先调用Derived的析构函数,再调用Base的析构函数return 0;
}

 输出:

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

解释:

 在这个例子中,基类的析构函数被声明为虚函数,因此当delete basePtr被调用时,程序会首先调用派生类的析构函数Derived::~Derived(),然后再调用基类的析构函数Base::~Base(),从而确保派生类资源得到正确释放。

总结:

  • 虚析构函数:在继承体系中,基类的析构函数应当声明为虚函数,以确保派生类的析构函数能够在删除基类指针时被正确调用。
  • 内存泄漏:如果基类的析构函数不是虚函数,那么派生类的析构函数不会被调用,可能会导致资源没有得到正确释放,从而引发内存泄漏。 

最后 

  1. 继承是面向对象编程的基础:继承允许通过基类创建派生类,实现代码重用、扩展和模块化设计,是面向对象编程的核心特性之一。

  2. 增强代码复用与扩展性:通过继承,派生类可以复用基类的代码,同时根据需要扩展或修改功能,提高代码的复用性和系统的灵活性。

  3. 虚继承解决多继承问题:C++支持多继承,但为了避免多继承中出现的“钻石继承”问题,虚继承机制确保基类的成员只会被继承一次,避免二义性和资源冲突。

  4. 虚函数和多态性实现动态行为:通过虚函数和多态性,C++使得基类指针或引用可以动态地调用派生类的实现,提高了代码的灵活性和可扩展性。

  5. 方法重写与函数隐藏的区别:方法重写是通过虚函数实现的多态性,而函数隐藏则是子类中定义的同名函数覆盖了父类中的函数,但不支持多态性。

  6. 析构函数必须为虚函数:当基类指针指向派生类对象时,析构函数必须声明为虚函数,以确保派生类的资源能够被正确释放,避免内存泄漏。

  7. 继承设计的最佳实践:继承应遵循简洁、清晰的设计原则,避免过深的继承层次和滥用多继承,确保类之间的关系符合“里氏替换原则”,从而提高代码的可维护性和可扩展性。

总结而言,C++继承是实现高效、灵活、可扩展的软件系统的核心工具,但继承的设计与使用应遵循一定的原则,避免复杂性和误用,从而提升代码质量和系统的可维护性。

 路虽远,行则将至;事虽难,做则必成

 亲爱的读者们,下一篇文章再会!!!

相关文章:

【C++进阶篇】像传承家族宝藏一样理解C++继承

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…...

Java基础面试题09:Java异常处理完成以后,Exception对象会发生什么变化?

一、Java异常&#xff08;Exception&#xff09;基本概念 什么是异常&#xff1f; 简单来说&#xff0c;异常就是程序运行时发生了意外的“错误”或者“不正常现象”&#xff0c;导致程序中断。异常处理的目标是让程序在出现问题时能稳住&#xff0c;不会直接崩溃。 1.1 异常…...

mysql sql语句 between and 是否边界值

在 MySQL 中&#xff0c;使用 BETWEEN 运算符时&#xff0c;边界值是包括在内的。这意味着 BETWEEN A AND B 查询会返回 A 和 B 之间的所有值&#xff0c;包括 A 和 B 自身。 示例 假设有一个表 employees&#xff0c;其中有一个 salary 列&#xff0c;您可以使用以下查询&am…...

Java接收LocalDateTime、LocalDatee参数

文章目录 引言I java服务端的实现1.1 基于注解规范日期格式1.2 json序列化和反序列化全局配置自动处理日期格式化II 知识扩展: 枚举的转换和序列化III 签名注意事项引言 应用场景举例:根据时间段进行分页查询数据 前后端交互日期字符串统一是yyyy-MM-dd HH:mm:ss 或者yyyy-M…...

方差分析、相关分析、回归分析

第一章&#xff1a;方差分析 1.1 方差分析概述 作用: 找出关键影响因素&#xff0c;并进行对比分析&#xff0c;选择最佳组合方案。影响因素: 控制因素&#xff08;人为可控&#xff09;和随机因素&#xff08;人为难控&#xff09;。控制变量的不同水平: 控制变量的不同取值…...

SQLModel入门

SQLModel 系统性指南 目录 简介 什么是 SQLModel&#xff1f;为什么使用 SQLModel&#xff1f; 安装快速入门 定义模型创建数据库和表 基本 CRUD 操作 创建&#xff08;Create&#xff09;读取&#xff08;Read&#xff09;更新&#xff08;Update&#xff09;删除&#xff0…...

单片机蓝牙手机 APP

目录 一、引言 二、单片机连接蓝牙手机 APP 的方法 1. 所需工具 2. 具体步骤 三、单片机蓝牙手机 APP 的应用案例 1. STM32 蓝牙遥控小车 2. 手机 APP 控制 stm32 单片机待机与唤醒 3. 智能家居系统 4. 智能记忆汽车按摩座椅 四、单片机蓝牙手机 APP 的功能 1. 多种控…...

PostgreSQL在Linux环境下的常用命令总结

标题 登录PgSQL库表基本操作命令新建库表修改库表修改数据库名称&#xff1a;修改表名称修改表字段信息 删除库表pgsql删除正在使用的数据库 须知&#xff1a; 以下所有命令我都在Linux环境中执行验证过&#xff0c;大家放心食用&#xff0c;其中的实际名称换成自己的实际名称即…...

Unity shaderlab 实现LineSDF

实现效果&#xff1a; 实现代码&#xff1a; Shader "Custom/LineSDF" {Properties{}SubShader{Tags { "RenderType""Opaque" }Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{floa…...

Ubuntu中的apt update 和 apt upgrade

apt update 和 apt upgrade 是 Debian 及其衍生发行版&#xff08;如 Ubuntu&#xff09;中常用的两个 APT 包管理命令&#xff0c;它们各自执行不同的任务&#xff1a; apt update: 这个命令用于更新本地软件包列表。当你运行 apt update 时&#xff0c;APT 会从配置的源&…...

Android 中 Swipe、Scroll 和 Fling 的区别

Android 中 Swipe、Scroll 和 Fling 的区别 Swipe&#xff08;滑动&#xff09;Scroll&#xff08;滚动&#xff09;Fling&#xff08;甩动&#xff09;三者之间的区别代码示例 (Fling)总结 在 Android 应用中&#xff0c;Swipe、Scroll 和 Fling 都是用户在触摸屏幕上进行的滑…...

linux基础2

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…...

如何通过智能生成PPT,让演示文稿更高效、更精彩?

在快节奏的工作和生活中&#xff0c;我们总是追求更高效、更精准的解决方案。而在准备演示文稿时&#xff0c;PPT的制作往往成为许多人头疼的问题。如何让这项工作变得轻松且富有创意&#xff1f;答案或许就在于“AI生成PPT”这一智能工具的广泛应用。我们就来聊聊如何通过这些…...

执法记录仪数据自动备份光盘刻录归档系统

派美雅按需研发的执法记录仪数据自动备份光盘刻录归档系统&#xff0c;为用户提供数据自动上传到刻录服务端、数据上传后自动归类&#xff0c;全自动对刻录端视频文件大小进行实时监测&#xff0c;满盘触发刻录&#xff0c;无需人工干预。告别传统刻录存在的痛点&#xff0c;实…...

启动SpringBoot

前言&#xff1a;大家好我是小帅&#xff0c;今天我们来学习SpringBoot 文章目录 1. 环境准备2. Maven2.1 什么是Maven2.2 创建⼀个Maven项⽬2.3 依赖管理2.3.1 依赖配置2.3.2 依赖传递2.3.4 依赖排除2.3.5 Maven Help插件&#xff08;plugin&#xff09; 2.4 Maven 仓库2.6 中…...

重定向操作和不同脚本的互相调用

文章目录 前言重定向操作和不同脚本的互相调用 前言 声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 重定向操作和不同脚本的互相调用 1.不同脚本的互相…...

51单片机教程(九)- 数码管的动态显示

1、项目分析 通过演示数码管动态显示的操作过程。 2、技术准备 1、 数码管动态显示 4个1位数码管和单片机如何连接 a、静态显示的连接方式 优点&#xff1a;不需要动态刷新&#xff1b;缺点&#xff1a;占用IO口线多。 b、动态显示的连接方式 连接&#xff1a;所有位数码…...

golang支持线程安全和自动过期map

在 Golang 中&#xff0c;原生的 map 类型并不支持并发安全&#xff0c;也没有内置的键过期机制。不过&#xff0c;有一些社区提供的库和方案可以满足这两个需求&#xff1a;线程安全和键过期。 1. 使用 sync.Map&#xff08;线程安全&#xff0c;但不支持过期&#xff09; Go…...

机器学习之RLHF(人类反馈强化学习)

RLHF(Reinforcement Learning with Human Feedback,基于人类反馈的强化学习) 是一种结合人类反馈和强化学习(RL)技术的算法,旨在通过人类的评价和偏好优化智能体的行为,使其更符合人类期望。这种方法近年来在大规模语言模型(如 OpenAI 的 GPT 系列)训练中取得了显著成…...

泷羽sec---shell作业

作业一 写计算器 使用bc命令 需要进行安装bc 代码如下&#xff1a; #!/bin/bash echo "-----------------------------------" echo "输入 f 退出" echo "可计算小数和整数" echo "用法如&#xff1a;1.12.2" echo "------…...

合肥高中英语一对一辅导2026指南,突破听说读写全面提升路径

合肥高中英语一对一辅导2026指南&#xff0c;突破听说读写全面提升路径据《2026年中国基础教育课外辅导行业白皮书》数据显示&#xff0c;2026年高中阶段英语学科辅导需求同比增长23%&#xff0c;其中超过65%的学生家长明确表示&#xff0c;传统大班教学已无法满足孩子个性化提…...

Go代码越容易被AI写,Go工程师越值钱

Go代码越容易被AI写&#xff0c;Go工程师越值钱。 这句话听起来矛盾&#xff0c;但它是这个系列的终极结论。 前提是——你的价值不在"写代码"。 这是「AI工程时代三部曲」的收官篇。第一篇我们聊了Agent框架设计为什么比模型选型更重要&#xff0c;第二篇聊了技术债…...

三行六列16车位立体车库mcgs6.2仿真程序

三行六列16车位立体车库mcgs6.2仿真程序立体车库仿真程序最让人上头的就是运动逻辑设计。今天拆解一个三行六列布局的MCGS6.2项目&#xff0c;看看如何用脚本驱动16个车位的升降动画。注意这里的车位排布有点特殊——虽然看起来是3*6的矩阵&#xff0c;但实际有两处隐藏车位被改…...

如何在3个步骤内完成Logisim-Evolution数字电路设计工具的安装配置

如何在3个步骤内完成Logisim-Evolution数字电路设计工具的安装配置 【免费下载链接】logisim-evolution Digital logic design tool and simulator 项目地址: https://gitcode.com/gh_mirrors/lo/logisim-evolution Logisim-Evolution是一款功能强大的数字逻辑电路设计和…...

Anaconda 被误删后抢救手册:零重装、10 分钟极速恢复

引言 作为 Python 开发者、数据分析师、AI 学习者的「必备工具」&#xff0c;Anaconda 凭借便捷的环境管理、海量预安装包&#xff0c;成为入门与进阶的首选。但很多人曾因误操作 —— 比如清理 C 盘时删掉anaconda3文件夹、卸载时选错路径、甚至误删系统环境变量 —— 导致co…...

PyTorch 3.0静态图分布式训练源码分析窗口即将关闭:官方已标记torch.distributed._spmd模块为“实验性冻结”,2024 Q3后将移除调试钩子入口

第一章&#xff1a;PyTorch 3.0静态图分布式训练的演进背景与冻结决策动因PyTorch 3.0正式宣布冻结静态图&#xff08;TorchScript&#xff09;在分布式训练路径中的演进支持&#xff0c;这一决策并非技术倒退&#xff0c;而是基于多年大规模生产实践与生态协同的理性收敛。随着…...

【机器人路径规划】基于6种最新算法(小龙虾优化算法COA、MSA、RTH、NOA、BFO、SWO)求解机器人路径规划研究附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f447; 关注我领取海量matlab电子书和…...

高防服务器怎么选?360CDN 高防性价比分析

作为运维中小站点3年的老站长&#xff0c;前阵子被DDoS攻击搞得焦头烂额&#xff0c;网站频繁卡顿、宕机&#xff0c;损失不少流量。试过普通服务器加防护插件&#xff0c;基本形同虚设&#xff0c;后来陆续测试了360CDN高防以及其他几款主流高防产品&#xff0c;全程实测不吹不…...

ConcurrentHashMap讲解

在 Java 并发编程中&#xff0c;ConcurrentHashMap 是高频使用的线程安全 Map 实现&#xff0c;也是面试中几乎必问的核心知识点。它完美解决了 HashMap 线程不安全、Hashtable 性能极差的痛点&#xff0c;在高并发场景下实现了安全与性能的平衡。本文将从设计背景、JDK1.7/JDK…...

华为防火墙NAT映射选择指南:一对一映射 vs 端口映射

华为防火墙NAT映射技术深度解析&#xff1a;一对一映射与端口映射的实战选择 在当今企业网络架构中&#xff0c;如何安全高效地将内部服务暴露给外部访问是一个永恒的技术挑战。华为防火墙提供的NAT映射功能&#xff0c;特别是一对一映射和端口映射两种核心方案&#xff0c;为不…...