【C++高阶(一)】继承

目录

一、继承的概念

1.继承的基本概念

2.继承的定义和语法

3.继承基类成员访问方式的变化

​编辑 4.总结

二、基类和派生类对象赋值转换

三、继承中的作用域

四、派生类的默认成员函数

1.派生类中的默认构造函数

2.派生类中的拷贝构造函数

3.派生类中的移动构造函数

4.派生类的拷贝赋值运算符

 5.派生类的移动赋值运算符

6.派生类的析构函数

为什么基类析构函数需要virtual关键字修饰?

理由:多态性和正确的析构顺序

问题:非虚析构函数导致的资源泄漏

总结

五、继承和友元

六、继承与静态成员

 七、复杂的菱形继承和菱形虚拟继承

1.单继承

2.多继承

3.菱形继承

 菱形继承的问题

4.菱形虚拟继承

5.虚拟继承解决数据冗余和二义性的原理

虚基表的工作机制


一、继承的概念

在C++中,继承是一种面向对象编程的重要特性,它允许一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和行为(成员变量和成员函数)。通过继承,派生类不仅可以拥有基类的所有成员,还可以扩展或修改这些成员以提供更具体或特殊的功能。

1.继承的基本概念

  1. 基类(Base Class):提供基础属性和行为的类。
  2. 派生类(Derived Class):从基类继承并扩展或修改其功能的类。
  3. 访问控制(Access Control)
    • Public 继承:基类的public和protected成员在派生类中保持其访问级别不变,public成员依然是public,protected成员依然是protected。
    • Protected 继承:基类的public和protected成员在派生类中都变为protected成员。
    • Private 继承:基类的public和protected成员在派生类中都变为private成员。
  4. 构造函数和析构函数:派生类的构造函数在执行前会先调用基类的构造函数,析构函数的调用顺序则相反,先调用派生类的析构函数,再调用基类的析构函数。
  5. 多重继承(Multiple Inheritance):C++允许一个派生类从多个基类继承。

2.继承的定义和语法

class Base {
public:int baseValue;void baseFunction() {// 基类成员函数}
};class Derived : public Base {
public:int derivedValue;void derivedFunction() {// 派生类成员函数}
};

3.继承基类成员访问方式的变化

 4.总结

  1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管是在类内还是在类外都不能去访问它。
  2. 基类private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是继承才出现的。
  3. 实际上面的表格我们进行一下总结就能发现,基类的私有成员在子类中都是不可见的。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
  5. 在实际运用中一般都是使用public继承,几乎很少使用protected/private继承,也不提倡使用protected/private继承,因为 protected/private继承下来的成员都只能在派生类的类里使用,实际中扩展维护性不强。

二、基类和派生类对象赋值转换

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。
  • 基类对象不能赋值给派生类对象。
  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。

class Base {
public:int baseValue;virtual void display() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:int derivedValue;void display() override {std::cout << "Derived class" << std::endl;}
};Base baseObj;
Derived derivedObj;baseObj = derivedObj;  // 对象切割发生
baseObj.display();     // 输出 "Base class"

在上面的例子中,尽管derivedObj赋值给了baseObj,但baseObj只保留了Base类的部分,派生类的derivedValue被切割掉了,调用display函数时也只会调用基类的版本

此外,C++允许使用基类的指针或引用来指向派生类对象,这可以实现多态性。多态性允许你通过基类接口调用派生类的重载函数。

Base* basePtr = &derivedObj;
basePtr->display();  // 输出 "Derived class"(多态性)Base& baseRef = derivedObj;
baseRef.display();  // 输出 "Derived class"(多态性)

 在上面代码中,basePtr和baseRef都指向Derived对象,并且调用display方法时,会调用派生类Derived中的版本,这是因为display函数被声明为virtual。(virtual关键字我们下面会讲)

另外还有类型转换:static_cast和dynamic_cast,感兴趣的可以去了解下。

三、继承中的作用域

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; // 学号
};
void Test()
{Student s1;s1.Print();
};
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" <<i<<endl;}
};
void Test()
{B b;b.fun(10);
};

四、派生类的默认成员函数

在之前的学习中, 我们知道类可以自动生成一些默认的成员函数,这些成员函数包括默认构造函数、拷贝构造函数、移动构造函数、拷贝赋值运算符、移动赋值运算符和析构函数。而对于派生类,这些默认成员函数的生成和行为有一些特殊的规则和注意事项,下面我讲详细介绍。

1.派生类中的默认构造函数

默认构造函数在没有用户定义的构造函数时自动生成。对于派生类的默认构造函数,它会调用基类的默认构造函数(如果存在),然后初始化派生类的成员。而如果基类没有默认构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

class Base {
public:Base() {std::cout << "Base default constructor" << std::endl;}
};class Derived : public Base {
public:Derived() {std::cout << "Derived default constructor" << std::endl;}
};int main() {Derived d;  // 输出:Base default constructor//      Derived default constructorreturn 0;
}

2.派生类中的拷贝构造函数

拷贝构造函数在没有用户定义的情况下自动生成,用于创建类的对象副本。派生类的拷贝构造函数会首先调用基类的拷贝构造函数,然后复制派生类的成员。

class Base {
public:Base(const Base&) {std::cout << "Base copy constructor" << std::endl;}
};class Derived : public Base {
public:Derived(const Derived& other) : Base(other) {std::cout << "Derived copy constructor" << std::endl;}
};int main() {Derived d1;Derived d2 = d1;  // 输出:Base copy constructor//      Derived copy constructorreturn 0;
}

3.派生类中的移动构造函数

移动构造函数在没有用户定义的情况下自动生成,用于移动资源所有权。派生类的移动构造函数会首先调用基类的移动构造函数,然后移动派生类的成员。

class Base {
public:Base(Base&&) noexcept {std::cout << "Base move constructor" << std::endl;}
};class Derived : public Base {
public:Derived(Derived&& other) noexcept : Base(std::move(other)) {std::cout << "Derived move constructor" << std::endl;}
};int main() {Derived d1;Derived d2 = std::move(d1);  // 输出:Base move constructor//      Derived move constructorreturn 0;
}

4.派生类的拷贝赋值运算符

拷贝赋值运算符在没有用户定义的情况下自动生成,用于将一个对象的内容赋值给另一个对象。派生类的拷贝赋值运算符会首先调用基类的拷贝赋值运算符,然后赋值派生类的成员。

class Base {
public:Base& operator=(const Base&) {std::cout << "Base copy assignment operator" << std::endl;return *this;}
};class Derived : public Base {
public:Derived& operator=(const Derived& other) {Base::operator=(other);std::cout << "Derived copy assignment operator" << std::endl;return *this;}
};int main() {Derived d1, d2;d1 = d2;  // 输出:Base copy assignment operator//      Derived copy assignment operatorreturn 0;
}

 5.派生类的移动赋值运算符

移动赋值运算符在没有用户定义的情况下自动生成,用于将一个对象的内容移动到另一个对象。派生类的移动赋值运算符会首先调用基类的移动赋值运算符,然后移动派生类的成员。

class Base {
public:Base& operator=(Base&&) noexcept {std::cout << "Base move assignment operator" << std::endl;return *this;}
};class Derived : public Base {
public:Derived& operator=(Derived&& other) noexcept {Base::operator=(std::move(other));std::cout << "Derived move assignment operator" << std::endl;return *this;}
};int main() {Derived d1, d2;d1 = std::move(d2);  // 输出:Base move assignment operator//      Derived move assignment operatorreturn 0;
}

6.派生类的析构函数

析构函数在没有用户定义的情况下自动生成,用于清理对象派生类的析构函数会首先调用派生类的析构函数,然后调用基类的析构函数。

class Base {
public:virtual ~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};int main() {Base* b = new Derived();delete b;  // 输出:Derived destructor//      Base destructorreturn 0;
}

为什么基类析构函数需要virtual关键字修饰?

基类的析构函数需要加virtual关键字是为了确保在删除派生类对象时能够正确调用析构函数。这是一个非常重要的概念,尤其是在使用多态性和通过基类指针或引用操作派生类对象时。

理由:多态性和正确的析构顺序

class Base {
public:virtual ~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};

当基类的析构函数是虚函数时,通过基类指针删除派生类对象时,C++会首先调用派生类的析构函数,然后再调用基类的析构函数。这确保了派生类中分配的资源可以先被正确释放,再释放基类中分配的资源。

int main() {Base* b = new Derived();delete b;  // 输出顺序:Derived destructor//           Base destructorreturn 0;
}

问题:非虚析构函数导致的资源泄漏

如果基类的析构函数不是虚函数,则通过基类指针删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类中的资源没有被正确释放,造成资源泄漏。

class Base {
public:~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};int main() {Base* b = new Derived();delete b;  // 只输出:Base destructorreturn 0;
}

在上述代码中,由于 Base 类的析构函数不是虚函数,删除 b 时只调用了 Base 的析构函数,Derived 类的析构函数没有被调用,这会导致 Derived 类中的资源没有被正确释放。

总结

为了确保在使用多态性时派生类对象可以被正确地销毁,避免资源泄漏,基类的析构函数应该声明为虚函数。这一做法可以保证删除派生类对象时,派生类和基类的析构函数都能被正确调用。以下是总结的要点:

  1. 多态性支持:使用基类指针或引用操作派生类对象时,确保正确调用派生类的析构函数。
  2. 正确的析构顺序:先调用派生类的析构函数,再调用基类的析构函数,确保资源正确释放。
  3. 避免资源泄漏:防止派生类中的资源没有被释放,导致内存泄漏或其他资源泄漏。

五、继承和友元

友元关系是单向的和局部的,友元关系不能继承!也就是说基类友元不能访问子类私有和保护成员。

#include <iostream>// 基类
class Base {
private:int basePrivateVar;
protected:int baseProtectedVar;
public:int basePublicVar;Base() : basePrivateVar(1), baseProtectedVar(2), basePublicVar(3) {}friend void baseFriendFunction(Base &obj);
};// 基类的友元函数
void baseFriendFunction(Base &obj) {std::cout << "Base Private Var: " << obj.basePrivateVar << std::endl;std::cout << "Base Protected Var: " << obj.baseProtectedVar << std::endl;
}// 派生类
class Derived : public Base {
private:int derivedPrivateVar;
protected:int derivedProtectedVar;
public:int derivedPublicVar;Derived() : derivedPrivateVar(4), derivedProtectedVar(5), derivedPublicVar(6) {}friend void derivedFriendFunction(Derived &obj);
};// 派生类的友元函数
void derivedFriendFunction(Derived &obj) {// 基类的友元不能访问派生类的私有或保护成员// std::cout << "Derived Private Var: " << obj.derivedPrivateVar << std::endl; // 错误// std::cout << "Derived Protected Var: " << obj.derivedProtectedVar << std::endl; // 错误std::cout << "Derived Public Var: " << obj.derivedPublicVar << std::endl;
}int main() {Base baseObj;Derived derivedObj;baseFriendFunction(baseObj); // 可以访问Base类的私有和保护成员derivedFriendFunction(derivedObj); // 可以访问Derived类的公共成员// 基类的友元函数不能访问派生类的私有和保护成员// baseFriendFunction(derivedObj); // 错误return 0;
}

六、继承与静态成员

基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。

静态成员变量需要在类外进行定义和初始化。静态成员函数则不需要在类外定义。

#include <iostream>// 基类
class Base {
public:static int staticVar;  // 声明静态成员变量static void staticFunction() {  // 声明并定义静态成员函数std::cout << "Static Function in Base" << std::endl;}
};// 定义静态成员变量
int Base::staticVar = 10;// 派生类
class Derived : public Base {
public:void display() {std::cout << "Base staticVar: " << staticVar << std::endl;  // 访问基类的静态成员变量staticFunction();  // 调用基类的静态成员函数}
};int main() {Derived obj;obj.display();// 静态成员可以通过类名直接访问Base::staticVar = 20;Derived::staticVar = 30;std::cout << "Base staticVar after modification: " << Base::staticVar << std::endl;std::cout << "Derived staticVar after modification: " << Derived::staticVar << std::endl;return 0;
}

静态成员的特点

  1. 类共享性:所有类的对象共享同一个静态成员变量。
  2. 类作用域:静态成员变量和静态成员函数在类作用域内,但可以通过类名直接访问。
  3. 内存管理:静态成员变量在程序启动时分配内存,程序结束时释放内存。

注意:

  • 静态成员函数:静态成员函数不能访问非静态成员变量和非静态成员函数,因为它们属于类本身,而不是类的某个对象。但是静态成员函数可以访问静态成员变量和其他静态成员函数。

 七、复杂的菱形继承和菱形虚拟继承

1.单继承

一个子类只有一个直接父类时称这个继承关系为单继承。

2.多继承

一个子类有两个或以上直接父类时称这个继承关系为多继承

3.菱形继承

菱形继承(也称钻石继承)是指一种特殊的多继承情况,其中一个类从两个基类继承,而这两个基类又继承自同一个祖先类。这种继承关系形成了一个菱形结构。

 菱形继承的问题

 从上面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。

#include <iostream>// 祖先类
class A {
public:int value;A() : value(0) {}
};// 两个派生类继承自 A
class B : public A {};
class C : public A {};// 派生类 D 同时继承自 B 和 C
class D : public B, public C {};int main() {D obj;// obj.value; // 错误:二义性问题,不知道是从 B 继承的 A 还是从 C 继承的 A// 解决方法之一是明确指定路径obj.B::value = 1;obj.C::value = 2;std::cout << "obj.B::value: " << obj.B::value << std::endl;std::cout << "obj.C::value: " << obj.C::value << std::endl;return 0;
}

4.菱形虚拟继承

为了解决上面菱形继承所带来的问题,我们可以使用虚拟继承。虚拟继承确保在菱形继承结构中只存在一个基类的实例。

如在上面的代码中,我们可以在B和C继承A的时候使用虚拟继承,即

class B : virtual public A {};
class C : virtual public A {};

需要注意的是,虚拟继承不要在其他地方去使用。

5.虚拟继承解决数据冗余和二义性的原理

class A
{
public:int _a;
};
// class B : public A
class B : virtual public A
{
public:int _b;
};
// class C : public A
class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

下面是菱形继承的内存对象成员模型:这里可以看到数据冗余

下面是菱形虚拟继承的内存对象成员模型:

 

这里可以分析出D对象中将A放到了D对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?

这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存储的是偏移量,通过偏移量就能找到A 。

总结:

虚拟继承通过确保每个虚拟基类在派生类中只有一个共享实例,从而避免了重复实例化和二义性问题。为了实现这一点,编译器会使用虚基表来跟踪和管理虚拟基类的实例。

虚基表的工作机制

  1. 虚基表的引入: 每个使用虚拟继承的类会包含一个虚基表指针。这个指针指向一个虚基表,该表包含虚拟基类的指针。

  2. 共享基类实例: 在派生类(如 D)的对象中,虚基表指针确保所有虚拟基类实例都指向同一个实际基类实例。这意味着 D 中只有一个 A 类的实例。

  3. 成员访问的重定向: 在访问基类成员时,编译器使用虚基表来正确地定位基类成员,确保访问的是唯一的基类实例。

所以,当一个类虚拟继承另一个类时,编译器在对象布局中插入一个虚基表指针(vbptr)。这个指针指向一个虚基表(vbtbl),而虚基表中包含指向虚拟基类的偏移量或地址。通过这种方式,每个派生类能够正确地定位并访问唯一的虚拟基类实例。


上面就是我们对C++继承的全部理解了~

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

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

相关文章

英语学习笔记25——Mrs. Smith‘s kitchen

Mrs. Smith’s kitchen 史密斯太太的厨房 词汇 Vocabulary Mrs. 夫人【已婚】 复习&#xff1a;Mr. 先生 全名 / 姓    Mrs. 夫人 全名 / 丈夫的姓    Miss 小姐&#xff08;未婚&#xff09; 全名 / 姓    Ms. 女士 全名 / 姓 查看婚姻状况&#xff0c;可以观察…

springboot项目中图片上传之后需要重启工程才能看到图片?

需求背景 最近在做一个用户自定义上传头像的小需求&#xff0c;用户上传头像然后需要立马回显。 需求是很常见的、正当的需求。如果不使用到对象存储这类服务&#xff0c;我们把用户头像的图片文件仅存在本地就可以了。我们在开发的过程中为了工程管理方便通常下意识会将图片…

Modbus TCP转Profinet网关测试配置案例

本案例采用XD-ETHPN20网关做为Modbus TCP通信协议设备与Profinet通信协议设备连接的桥梁。Modbus TCP是一种基于TCP/IP协议的工业通信协议&#xff0c;而Profinet则是用于太网通信的协议。Modbus TCP转Profinet网关可实现这两种不同协议之间的数据交换和传输&#xff0c;极大地…

Python高级进阶--dict字典

dict字典⭐⭐ 1. 字典简介 dictionary&#xff08;字典&#xff09; 是 除列表以外 Python 之中 最灵活 的数据类型&#xff0c;类型为dict 字典同样可以用来存储多个数据字典使用键值对存储数据 2. 字典的定义 字典用{}定义键值对之间使用,分隔键和值之间使用:分隔 d {中…

【ECharts】数据可视化

目录 ECharts介绍ECharts 特点Vue2使用EChats步骤安装 ECharts引入 ECharts创建图表容器初始化图表更新图表 示例基本柱状图后台代码vue2代码配置 组件代码运行效果 基本折线图示例代码组件 基础饼图示例代码后台前端配置组件运行效果 其他 ECharts介绍 ECharts 是一个由百度开…

软件设计师备考 | 案例专题之数据库设计 概念与例题

相关概念 关注上图中的两个部分&#xff1a; 概念结构设计 设计E-R图&#xff0c;也即实体-联系图。 工作步骤&#xff1a;选择局部应用、逐一设计分E-R图、E-R图合并。进行合并时&#xff0c;它们之间存在的冲突主要有以下3类&#xff1a; 属性冲突。同一属性可能会存在于…

低功耗蓝牙模块轻松实现智能防丢器

低功耗蓝牙模块&#xff0c;作为集成蓝牙无线技术功能的PCBA板&#xff0c;主要用于短距离无线通讯&#xff0c;已经成为物联网无线传输发展的中坚力量。随着蓝牙技术不断更新换代&#xff0c;越来越多的智能可穿戴设备出现在我们的生活中&#xff0c;智能手环&#xff0c;智能…

电商公司需不需要建数字档案室呢

建立数字档案室对于电商公司来说是非常有必要的。以下是一些原因&#xff1a; 1. 空间节约&#xff1a;数字档案室可以将纸质文件转化为电子文件&#xff0c;节省了大量存储空间。这对于电商公司来说尤为重要&#xff0c;因为他们通常会有大量的订单、客户信息和供应商合同等文…

力扣538. 把二叉搜索树转换为累加树

Problem: 538. 把二叉搜索树转换为累加树 文章目录 题目描述思路复杂度Code 题目描述 思路 利用二叉搜索树中序遍历的特性&#xff0c;**降序遍历&#xff08;此处是想表达先遍历其右子树再遍历其左子树这样遍历的过程中每个节点值得大小排序是降序得&#xff09;**其节点&…

宝塔PHP环境安装配置Xdebug

宝塔PHP环境安装配置Xdebug 安装XdebugVSCode安装插件编辑配置文件编辑配置运行调试断点快捷键其他 安装Xdebug 在宝塔中&#xff0c;找到PHP&#xff0c;打开管理页面&#xff0c;选择xdebug扩展&#xff0c;点击操作栏中的安装按钮&#xff08;这里已经安装过了&#xff0c;…

电商项目之有趣的支付签名算法

文章目录 1 问题背景2 思路3 代码实现 1 问题背景 在发起支付的时候&#xff0c;一般都需要对发送的请求参数进行加密或者签名&#xff0c;下文简称这个过程为“签名”。行业内比较普遍的签发算法有&#xff1a; &#xff08;1&#xff09;按支付渠道给定的字段排序进行拼接&am…

Android Studio添加依赖 新版 和 旧版 的添加方式(Gradle添加依赖)(Java)

旧版的&#xff08;在线添加&#xff09; 1找 文件 在项目的build.gradle文件中添加依赖(在下面的节点中添加库 格式 ’ 组 &#xff1a;名字 &#xff1a; 版本号 ‘ ) dependencies {implementation com.example:library:1.0.0 }implementation 组:名字:版本…

【lambdastreammaven】

lambda 匿名函数 为了简化java中的匿名内部类 事件监听 写一个类 实现 ActionListener 接口 (外部类) | | 内部类 类在其他地方用不到, 索性就把这个类定义在类的内部使用 好处: 1.内部可以使用外部类的成员 …

论文阅读--CLIPasso

让计算机把真实图片抽象成简笔画&#xff0c;这个任务很有挑战性&#xff0c;需要模型捕获最本质的特征 以往的工作是找了素描的数据集&#xff0c;而且抽象程度不够高&#xff0c;笔画是固定好的&#xff0c;素描对象的种类不多&#xff0c;使得最后模型的效果十分受限 之所以…

小米财报:业绩远超预期,汽车推着手机跑!

随着一季度财报陆续出炉&#xff0c;企业间的分化越来越明显。 新环境下&#xff0c;很多公司都陷入停滞时&#xff0c;去讨论“掉队”已经没有多少意义&#xff0c;现在真正值得我们关注的&#xff0c;是那些在逆风情况下&#xff0c;还能“领先”的企业。毫无疑问&#xff0…

Linux修炼之路之冯系结构,操作系统

目录 一&#xff1a;冯诺依曼体系结构 1.五大组件 2.存储器存在的意义 3.几个问题 二&#xff1a;操作系统 接下来的日子会顺顺利利&#xff0c;万事胜意&#xff0c;生活明朗-----------林辞忧 一&#xff1a;冯诺依曼体系结构 我们当代的计算机的基本构成都是由冯诺依曼…

【开源】多语言大型语言模型的革新:百亿参数模型超越千亿参数性能

大型人工智能模型&#xff0c;尤其是那些拥有千亿参数的模型&#xff0c;因其出色的商业应用表现而受到市场的青睐。但是&#xff0c;直接通过API使用这些模型可能会带来数据泄露的风险&#xff0c;尤其是当模型提供商如OpenAI等可能涉及数据隐私问题时。私有部署虽然是一个解决…

PY32F003+RTL8710(AT) 实现获取天气情况

一、RTL8710主要AT指令 1、ATSR&#xff1a;模块重启 2、ATSE1&#xff1a;开启回显 3、ATPW1&#xff1a;station模式 4、ATPNssid,password,,&#xff1a;连接到AP 5、ATPK1&#xff1a;设置自动接收 6、ATPC0,v1.yiketianqi.com,80&#xff1a;与网站建立TCP连接 7、ATPT125…

关于pytorch加载模型报错问题

load_net[“params”] 报keyerror 加载模型后查看对应参数是什么 model2 torch.load(m1_path "xxx.pth") print(model1.keys())若输出如下&#xff1a; 已经有相应参数不需要执行 load_net[“params”]若输出如下 则需要load_net[“params”]

Linux-命令上

at是一次性的任务&#xff0c;crond是循环的定时任务 如果 cron.allow 文件存在&#xff0c;只有在文件中出现其登录名称的用户可以使用 crontab 命令。root 用户的登录名必须出现在 cron.allow 文件中&#xff0c;如果这个文件存在的话。系统管理员可以明确的停止一个用户&am…