C++(十五)继承 part1

news2024/9/21 23:24:03

一、继承的概念

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


        下面我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。

        他们一些不同的成员变量和函数,比如老师独有成员变量是职称,学生的独有成员变量是学号;学生的独有成员函数是学习,老师的独有成员函数是授课。

为了解决此类代码冗余的问题,C++引入了继承

二、继承的定义格式

基类(Base Class):这是一个现有的类,它的属性(成员变量)和方法(成员函数)可以被其他类继承。

派生类(Derived Class):这是一个从基类继承而来的类。

下面我们看到Person是父类也称作基类。Student是子类,也称作派生类。(因为翻译的原因,所以既叫父类/子类,也叫基类/派生类)

继承定义特点概述:

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

示例:


#include <iostream>
#include <string>
using namespace std;

// 基类:Person
class Person {
protected:
    string name;
    string address;
    string phoneNumber;

public:
    Person(const string& name, const string& address, const string& phoneNumber)
        : name(name), address(address), phoneNumber(phoneNumber) {}

    // 基类成员函数
    void identity() const {
            //... ...
    }
};

// 派生类:Student
class Student : public Person     //此处注意继承的写法
{
private:
    string studentNumber;

public:
    Student(const string& name, const string& address, const string& phoneNumber, const string& studentID)
        : Person(name, address, phoneNumber), studentNumber(studentID) {}

    // 派生类独有成员函数
    void study() const {
        cout << name << " is studying." << endl;
    }
};

// 派生类:Teacher
class Teacher : public Person     //此处注意继承的写法
{
private:
    string title; //职称

public:
    Teacher(const string& name, const string& address, const string& phoneNumber, const string& title)
        : Person(name, address, phoneNumber), title(title) {}

    // 派生类独有成员函数
    void teach() const {
        cout << name << " is teaching." << endl;
    }
};

可以把上面代码改成现在这样:

创建一个基类 Person 来避免冗余,然后让 StudentTeacher 继承这个基类。基类 Person 包含共有的成员变量和函数,而 StudentTeacher 则包含各自特有的成员变量和函数。 

这里我们将学习第三种访问限定符

  1. public 公有
  2. protected 保护 (结合继承规则介绍)
  3. private 私有

继承规则如下: 

接下来的分析将围绕这张表,逐步学习继承的规则。

先看什么叫继承方式,和3种不同的基类成员呢?

基类成员

表中的类成员指的就是在基类(父类)中的方法和属性,根据访问限定符,来规定上下文中的可见性和可访问性,和以往学习的一样,只是多了protected(保护)的使用。

依然用我们上面的代码来举例说明(为观察效果作出了小改动):

继承方式

  • 什么叫继承方式呢?

        继承方式(Inheritance Type)指的是在 C++ 中派生类(子类)继承基类(父类)时指定的访问级别。这种访问级别会影响基类成员在派生类中的可见性和访问权限。

继续使用刚刚的代码来说明:

 刚刚我们使用的就是公有继承(public)

对应的当然有保护继承私有继承

继承规则

知道各种基类成员和继承方式的基本概念之后,我们就可以学习具体的代码怎么写呢?

其实非常简单,大家先看完3个示例,结合下图,就能一一理解清楚了。

示例1:

公有继承使用:

  • 基类的 public 成员在派生类中仍然是 public 的。
  • 基类的 protected 成员在派生类中仍然是 protected 的。
  • 基类的 private 成员仍然无法在派生类中访问
//基类
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};

//派生类
class Derived : public Base {
public:
    void accessBaseMembers() {
        publicMember = 10;    // 可以访问
        protectedMember = 20; // 可以访问
        // privateMember = 30; // 无法访问,编译错误
    }
};

公有继承是最常用的继承方式,因为它保持了基类成员的访问控制,同时允许派生类公开访问基类的公有接口。 

示例2: 

保护继承使用:

  • 基类的 public 成员在派生类中变成 protected 的。
  • 基类的 protected 成员在派生类中仍然是 protected 的。
  • 基类的 private 成员仍然无法在派生类中访问。
//基类
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};
//派生类
class Derived : protected Base {
public:
    void accessBaseMembers() {
        publicMember = 10;    // 可以访问
        protectedMember = 20; // 可以访问
        // privateMember = 30; // 无法访问,编译错误
    }
};
int main() {
    Derived d;
    // d.publicMember = 10;     // 无法访问,编译错误
}

保护继承限制了派生类中对基类成员的访问范围,使得基类的公有成员在派生类中不再是公有的,而是只能在派生类内部和进一步派生的子类中访问。 

  • 访问限定符protected :在类外面不能访问 ——代码种main函数无法访问protected 的成员 

 通过画图让大家更具体的理解继承里面发生了什么:

【注意】继承做了这些事情,但是实际代码并不能这样写,只是为了形象直观地给大家呈现效果

示例3:

私有继承的使用 

  • 基类的 publicprotected 成员在派生类中都变成 private 的。
  • 基类的 private 成员仍然无法在派生类中访问。
class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};

class Derived : protected Base {
public:
    void accessBaseMembers() {
        publicMember = 10;    // 可以访问
        protectedMember = 20; // 可以访问
        // privateMember = 30; // 无法访问,编译错误
    }
};

int main() {
    Derived d;
    // d.publicMember = 10; // 无法访问,编译错误
}

图示: 

通过3个例子很直观的观察到继承方式的不同会如何作用于派生类了。

最后注意一在基类中的private成员

  • 不可访问性:基类的 private 成员(包括数据成员和成员函数)在派生类中是不可访问的。派生类不能直接访问或修改基类的 private 成员,即使是通过派生类的成员函数。

  • 继承但不可访问:虽然 private 成员被继承到了派生类中,但它们只能通过基类自己的成员函数访问。派生类无法直接访问这些成员,但可以通过基类的 publicprotected 成员函数间接访问。

  • 控制实现细节:使用 private 访问权限的一个主要目的是隐藏类的实现细节。这样,派生类和外部代码都无法依赖或修改这些私有细节,这增强了类的封装性和安全性。

  • 不可重写性:基类的 private 成员函数不能在派生类中被重写。因为它们在派生类中是不可见的,因此派生类无法定义与基类中 private 成员函数同名的函数。

示例: 

#include <iostream>
#include <string>
using namespace std;
class Base {
private:
    int privateData;

    void privateFun() {
        // 基类的私有成员函数
    }

public:
    Base() : privateData(0) {}

    int getPrivateData() const {
        return privateData; // 基类的公共函数可以访问私有成员
    }
};

class Derived : public Base {
public:
    void Derived_Fun() {
        // privateData = 10; // 错误:无法访问基类的私有成员
        // privateFun(); // 错误:无法调用基类的私有成员函数
    }
};
int main()
{
    Derived der;
    der.getPrivateData(); //合法

    return 0;
}

private总结:private成员无论何种继承方式在子类中都是不可见的。

使用总结:

 三、父类与子类间赋值

        在C++中,父类与子类之间的赋值涉及一些规则和注意事项,特别是在对象赋值、对象间的指针或引用赋值,以及赋值运算符重载的情况下。子类对象可以赋值给父类对象,但父类对象赋值给子类对象则需要满足特定条件,否则会出现问题。

赋值规则:

  • 子类对象 可以赋值给父类对象 (对象切割下面会详细介绍
  • 子类对象 可以赋值给父类指针或引用(父类指针可以指向子类对象)
  • 父类对象 不能赋值给子类对象

下面结合代码、图形来解释理解一下这部分内容 

3.1 子类对象赋值给父类对象 

        继续沿用上面示例,main函数中我们实例化出 Student 对象,并试图将其赋值给一个 Person 对象。由于 Person 类并不包含 Student 类独有的 studentNumber 成员变量,赋值时将会发生对象切割。 

#include <iostream>
#include <string>
using namespace std;

// 基类:Person
class Person {
protected:
    string name;
    string address;
    string phoneNumber;

public:
    Person(const string& name, const string& address, const string& phoneNumber)
        : name(name), address(address), phoneNumber(phoneNumber) {}

    // 基类成员函数
    void identity() const {
            //... ...
    }
};
// 派生类:Teacher(没用到所以省略了)
// 派生类:Student
class Student : public Person {
private:
    string studentNumber;

public:
    Student(const string& name, const string& address, const string& phoneNumber, const string& studentNumber)
        : Person(name, address, phoneNumber), studentNumber(studentNumber) {}

    // 派生类独有成员函数
    void study() const {
        cout << name << " is studying." << endl;
    }
};

int main() {
    //              姓名     地址         电话号码      学号
    Student student("张三", "上海 xx街", "1356729950", "2024090530");
    Person person = student;  // 对象切割

    person.identity();  // 调用的是 Person 类的 identity() 函数
    // person.study();   // 错误:Person 类没有 study() 函数
}

对象切割(Object Slicing)

        也称作“切片”,对象切割发生在将一个派生类对象赋值给一个基类对象时。在这个过程中,派生类中的特有成员和行为会被“切割”掉,只保留基类部分。这是因为基类对象无法容纳派生类对象的全部信息。

结合图形在上面main函数中发生的具体过程: 

        在这个例子中,person 对象将只包含 nameaddressphoneNumber 成员,而 studentNumberstudy() 函数无法访问。也就是说,person 对象只是 student 对象的 Person 部分。先简单理解成这样,但是成员函数C++中做了特殊处理下面会重点讲解。

希望大家结合图形可以很好的理解对象切割这一概念。 

3.2 指针和引用赋值 

        如果我们使用指针或引用,将 Student 对象赋值给 Person 指针或引用,可以避免对象切割,并且允许通过虚函数表调用子类的方法(假设使用了虚函数:多态内容下一章细讲)。不过在当前类设计中,我们没有使用虚函数,但我们仍然可以通过指针或引用调用基类的成员函数。

// 基类:Person
class Person {
protected:
    string name;
    string address;
    string phoneNumber;

public:
    Person(const string& name, const string& address, const string& phoneNumber)
        : name(name), address(address), phoneNumber(phoneNumber) {}

    // 基类成员函数
    void identity() const {
        cout << name << endl;
        cout << address << endl;
        cout << phoneNumber << endl;
    }
};
// 派生类:Teacher(没用到所以省略了)
// 派生类:Student
class Student : public Person {
private:
    string studentNumber;

public:
    Student(const string& name, const string& address, const string& phoneNumber, const string& studentNumber)
        : Person(name, address, phoneNumber), studentNumber(studentNumber) {}

    // 派生类独有成员函数
    void study() const {
        cout << name << " is studying." << endl;
    }
};

int main() {
    //              姓名     地址         电话号码      学号
    Student student("张三", "上海 xx街", "1356729950", "2024090530");
    Person person = student;        // 对象切割

    Person& personRef = student;    // 使用引用,避免对象切割
    personRef.identity();           // 调用的是 Person 类的 identity() 函数

    Person* personPtr = &student;   // 使用指针,避免对象切割
    personPtr->identity();          // 调用的是 Person 类的 identity() 函数
    //为了观察在identity中打印些信息
}

 运行结果:

张三
上海 xx街
1356729950
张三
上海 xx街
1356729950

集合图形理解: 

        由于 personRefpersonPtr 都指向 student 对象的 Person 部分,调用 identity() 函数时会表现出多态行为(如果该函数是虚函数),但它们无法直接访问 Student 类中的 study() 函数。因为指针和引用的用法是一样的,所以这里只使用一个作图表示。 

 3.3 父类对象赋值给子类对象

父类对象不能直接赋值给子类对象!!!

父类对象不能直接赋值给子类对象!!!

父类对象不能直接赋值给子类对象!!!

父类对象为什么不能直接赋值给子类对象呢?

父类子类我们依旧用上面示例只改main函数做说明:

错误1: 

int main() {
    //             姓名     地址         电话号码      
    Person person("狗剩", "北京 xx区", "1350000000");

    //              姓名     地址         电话号码      学号
    Student student("张三", "上海 xx街", "1356729950", "2024090530");
    //student = person;  // 错误:不能将父类对象赋值给子类对象
    //该操作编译都过不了
}

错误2:  

有小伙伴可以在想:可以通过类型转换再赋值啊?可以通过显式转换来尝试强制赋值。但这种方式非常危险,可能导致程序崩溃或不确定的行为,因为子类特有的成员变量在父类中并不存在

int main() {
    //             姓名     地址         电话号码      
    Person person("狗剩", "北京 xx区", "1350000000");

    Student* studentPtr = (Student*)&person;  // 不安全的转换
    // studentPtr->study();  // 可能导致未定义行为,因为 person 并没有 studentNumber
    //此做法虽然可以过编译,但非常不推荐!!!
}

 小伙伴们可以结合下面图形应该也是不难理解的:

 

四、隐藏

接下来我们将详细学习C++中继承体系中对成员函数的处理机制——隐藏

在往下学习之前我们不能忘记一点:

  • 就算是有继承关系,也不妨碍父类依然可以单独像普通类一样使用,一样可以实例化出自己的对象。

      

4.1 隐藏的概念

        隐藏(hiding),也叫重定义,子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这意味着通过子类对象调用该函数时,默认只会调用子类的版本,而父类的同名函数将无法直接被调用。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

【注意】在实际中在继承体系里面最好不要定义同名的成员。 

4.2 构成隐藏的条件

  • 成员函数的隐藏,只需要函数名相同就构成隐藏

我们先简单看看看隐藏是什么样子的:

4.3 使用


#include <iostream>
#include <string>
using namespace std;
// 基类:Person
class Person {
protected:
    string name;
    string address;
    string phoneNumber;

public:
    Person(const string& name, const string& address, const string& phoneNumber)
        : name(name), address(address), phoneNumber(phoneNumber) {}

    // 基类成员函数
    void identity() const {
        cout << "Person:" << endl;
        cout << name << endl;
        cout << address << endl;
        cout << phoneNumber << endl;
    }
};
// 派生类:Student
class Student : public Person {
private:
    string studentNumber;

public:
    Student(const string& name, const string& address, const string& phoneNumber, const string& studentNumber)
        : Person(name, address, phoneNumber), studentNumber(studentNumber) {}

    // 派生类独有成员函数
    void study() const {
        cout << name << " is studying." << endl;
    }

    //identity()成员函数名相同够成隐藏
    void identity() const {
    cout << "Student:" << endl;
    cout << name << endl;
    cout << address << endl;
    cout << phoneNumber << endl;
    cout << studentNumber << endl;
    }

};
int main() {
    //              姓名     地址         电话号码      学号
    Student student("张三", "上海 xx街", "1356729950", "2024090530");
    student.identity();
}

 运行结果:

Student:
张三
上海 xx街
1356729950
2024090530

        在 Student 类中,定义了一个与基类 Person 中同名的 identity() 函数。这个新的 identity() 函数会隐藏基类中的同名函数。具体来说,当通过 Student 对象调用 identity() 函数时,将调用 Student 中的版本,而不是 Person 中的版本。这就是函数隐藏。 

 


 

当我们对隐藏有了基本的概念,现在来对继承体系成员函数处理机制 做进一步探讨

  •  继承体系中基类和派生类都有独立的作用域。 

 

在示例代码中:

int main() {
    //              姓名     地址         电话号码      学号
    Student student("张三", "上海 xx街", "1356729950", "2024090530");
    student.identity();
}

 Student类的对象调用identity(),派生类的作用域中查找某个名称时,优先查找派生类自己定义的成员

基类和派生类的作用域是独立的,即基类中的名称查找不会受到派生类中同名成员的影响。


关于隐藏我们再看最后一个问题:下面这段代码调用的是父类还是子类的 identity() 呢?

// 基类:Person
#include <iostream>
#include <string>
using namespace std;
class Person {
protected:
    string name;
    string address;
    string phoneNumber;

public:
    Person(const string& name, const string& address, const string& phoneNumber)
        : name(name), address(address), phoneNumber(phoneNumber) {}

    // 基类成员函数
    void identity() const {
        cout << "Person:" << endl;
        cout << name << endl;
        cout << address << endl;
        cout << phoneNumber << endl;
    }
};
// 派生类:Student
class Student : public Person {
private:
    string studentNumber;

public:
    Student(const string& name, const string& address, const string& phoneNumber, const string& studentNumber)
        : Person(name, address, phoneNumber), studentNumber(studentNumber) {}

    // 派生类独有成员函数
    void study() const {
        cout << name << " is studying." << endl;
    }
    //identity成员函数名相同够成隐藏
    void identity() const {
    cout << "Student:" << endl;
    cout << name << endl;
    cout << address << endl;
    cout << phoneNumber << endl;
    cout << studentNumber << endl;
    }

};
int main() {
    //              姓名     地址         电话号码      学号
    Student student("张三", "上海 xx街", "1356729950", "2024090530");

    Person& personRef = student;  
    personRef.identity();          

    Person* personPtr = &student;   
    personPtr->identity();          
}

运行结果:

Person:
张三
上海 xx街
1356729950
Person:
张三
上海 xx街
1356729950

是的运行的都是Person中的identity(),详解:

4.3.1储存

编译器为每个类的成员函数生成一份代码,通常存储在程序的代码段(也称为文本段)中。父类和子类的 identity() 函数各自有独立的实现,分别存储在不同的位置

  • 父类 Person::identity() 函数:编译器会为 Person 类的 identity() 函数生成一份代码,并将其存储在代码段中。
  • 子类 Student::identity() 函数:同样,编译器也会为 Student 类的 identity() 函数生成一份代码,并将其存储在代码段中。

在程序的运行过程中,这两个函数的代码是独立存在的,并且不会因为它们的同名而发生冲突

4.3.2. 函数隐藏的调用机制

在编译时,编译器已经确定了调用哪个函数,这个决定依赖于调用者的静态类型,即声明时的类型——Person

  • 父类指针/引用调用: 如果你通过父类 Person 的指针或引用调用 identity() 函数,例如 personPtr->identity();personRef.identity();,编译器会在编译时将调用绑定到 Person::identity() 的实现。

  • 子类对象/指针/引用调用: 如果你通过子类 Student 的对象或引用调用 identity() 函数,例如 student.identity();,编译器会将调用绑定到 Student::identity() 的实现。

 

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

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

相关文章

Restful风格接口开发

一、项目搭建 安装nestjs脚手架 // 安装nestjs脚手架 npm i nestjs/cli// 新建 nest new [名字]//选择要用的工具 npm / yarn / pnpm 文件信息&#xff1a; 【main.ts】&#xff1a; 入口文件&#xff0c;通过NestFactory&#xff08;由nestjs/core库抛出的对象&#x…

【深度学习】softmax 回归的从零开始实现与简洁实现

前言 小时候听过一个小孩练琴的故事&#xff0c;老师让他先弹最简单的第一小节&#xff0c;小孩练了两天后弹不出。接着&#xff0c;老师让他直接去练更难的第二小节&#xff0c;小孩练习了几天后还是弹不出&#xff0c;开始感觉到挫败和烦躁了。 小孩以为老师之后会让他从简…

数据链路层认识以太网

我们前面学习到的传输层&#xff0c;网络层&#xff0c;传输层是保证数据可靠传输。而网络层是实现在复杂的网络环境中确定一个合适的路径。我们接下来所说的数据链路层其实就是用于两个设备(同一种数据链路节点)之间进行传递。其实也就是如数次的局域网中设备之间的转发过程。…

Pytest-@pytest.fixture夹具篇(一)

一、定义 在Python的pytest测试框架中&#xff0c;pytest.fixture是一个&#xff08;不是唯一&#xff09;装饰器&#xff0c;用于定义一个测试夹具。 二、简单实例 使用参数autouserTrue pytest.fixture(autouseTrue) def my_fixture():print("Setup: 准备测试环境&q…

华为软件测试笔试真题,抓紧收藏不然就看不到了

软件测试工程师笔试题目 一&#xff0e;填空 1、 系统测试使用&#xff08; C &#xff09;技术, 主要测试被测应用的高级互操作性需求, 而无需考虑被测试应用的内部结构。 A、 单元测试 B、 集成测试 C、 黑盒测试 D、白盒测试 2、单元测试主要的测试技术不包括&#xff08…

11. GIS三维建模工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

CACTER直播预告:畅联海外,高效通邮——解锁海外通邮新路径

在全球化的今天&#xff0c;企业通信的无障碍至关重要。然而&#xff0c;随着企业业务的不断拓展&#xff0c;海外通信的挑战也随之增加。跨国邮件传输的延迟、丢件、甚至安全问题&#xff0c;都可能成为企业国际化道路上的绊脚石。如何确保邮件在全球范围内的高效、安全传输&a…

设计模式1:C#开发中使用创建型的工厂模式和行为型的策略模式

一、接口设计的好处 三大好处&#xff1a;解耦、可复用、可扩展。 二、简单工厂模式 【三要素】能创建具体产品的工厂、抽象产品&#xff08;接口&#xff09;、具体产品 【基本用法】字符串>创建对象>调用其方法 // 产品接口 public interface IProduct {void Opera…

【计组易混淆概念梳理助记】机器字长、指令字长、存储字长、数据通路宽度...各种字长大杂炖,应该如何区分?谁又应该等于谁?

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 所有学过计组的人几乎都有一个烦恼 就是里面的概念无法记住 经典“转头忘” 那究竟是为什么&#xff1f;很显然 因为名词之间没形成结构化 点与点之间无法建立联系 在…

学AI绘画必知!文生图与图生图的基本认知

​ 在AI绘画的学习与使用中&#xff0c;无论是入门小白还是进阶高手&#xff0c;都绕不开两个核心概念&#xff1a;文生图和图生图。 这是所有AI绘画工具的根本操作方法。掌握这两者的基本原理&#xff0c;你便能轻松驾驭大多数AI工具&#xff0c;无论是MidJourney、Stable Dif…

创建makefile来运行其他目录下的makefile

上述makefile运行后&#xff0c;会报错产生结果&#xff1a; 需要一个伪目标all。

街机 SNK NeoGeo 中英文名字与驱动对照表

Part.I 简介 本文列举了街机 NeoGeo 中游戏的中英文名字与其驱动的对照&#xff0c;以帮助诸位更快地找到自己想玩的游戏。 注意&#xff1a;汉化版的街机模拟器 Kawaks 中游戏的中文名字是根据英文直译的&#xff0c;并不是习惯性的中文叫法。比如『三国志』英文名为『Warrio…

【案例67】Npart批量启动服务卡顿严重分析过程

问题现象 通过Npart启动NC服务&#xff0c;发现只启动一个&#xff0c;大概3min左右即可启动成功。但是批量启动服务需要几十分钟才可以把服务启动成功&#xff0c;启动卡在获取“wenjian”图标处。 绕过Npart直接写脚本并行启动相关服务&#xff0c;发现也需要30min 问题分析…

嵌入式OpenHarmony系统的一些特点

大家好,今天主要给大家分享一下,如何使用OpenHarmony,它的一些基本特点。 第一:为5G通讯做准备 OpenHarmony诞生与5G时代,5G不仅可以实现人与人,以及人与物,物与物之间的高速通讯,软件处理速度更快,可以操作的硬件也更多,可以说OpenHarmony是新一代万物互联的操作系…

【2024数模国赛赛题思路公开】国赛D题第二套思路丨附可运行代码丨无偿自提

2024年国赛D题解题第二套思路 【问题分析】 D题为投弹命中概率分析问题&#xff0c;有三种情况可以实现成功命中&#xff0c;并且可以发现这三种情况是互不重叠的&#xff0c;故实际投弹命中率可以表示为三种情况概率的累加&#xff0c;通过最后分析概率函数对投弹落点平面坐…

微信小程序rpx和px关系

微信小程序中使用了自定义的尺寸单位 rpx&#xff08;responsive pixel&#xff09;&#xff0c;这是一种相对单位&#xff0c;用于适配不同屏幕尺寸的设备。rpx 的设计目的是为了让开发者能够更容易地写出跨屏幕尺寸的应用&#xff0c;而不需要为每个屏幕尺寸单独写一套样式。…

为什么这么多物联网项目都失败了?

甚至超过 50% 的智慧布署&#xff0c;在概念阶段停滞不前&#xff0c;这让一些真正有前途的物联网项目都失败了。 这些严峻的数字可能令人费解&#xff0c;尤其是因为它们仍然没有阻止行业参与物联网技术&#xff0c;因为投资者越来越意识到&#xff0c;物联网提供了最激烈的竞…

Origin2024中如何添加误差带?直观查看数据的变化范围

误差线是通常用于统计或科学绘图中&#xff0c;本期给大家分享Origin中绘制带填充区的误差带图&#xff0c;可以直观显示数据的变化范围&#xff0c;填充区域也可以增加视觉效果和美观性 操作步骤&#xff1a; 1、打开Origin2024软件&#xff0c;然后在Book1中输入如下示例数…

[PS]Photoshop 2024下载安装教程附软件包百度网盘分享链接地址

Photoshop 2024下载安装教程&#xff1a; Photoshop是一款修图软件&#xff0c;可以是目前位置最全的修图软件。是图片设计师必备的技能。 如何安装PS? 可以通过网盘链接获取&#xff1a;https://pan.baidu.com/s/1er557ksNcOOqj7zfpi5Lhg?pwdrzo0 提取码&#xff1a;rzo0…

Vue3.0项目实战(三)——大事件管理系统首页 layout 架子与文章分类的实现

目录 1. 首页 layout 架子 [element-plus 菜单] 1.1 基本架子拆解 2. 登录访问拦截 2.1 需求 2.2 vue3 和 vue2 中的 Vue-Router 区别 3. 用户基本信息获取&渲染 4. 退出功能 [element-plus 确认框] 5. 文章分类页面 - [element-plus 表格] 5.1 基本架子 - PageCo…