C++ 继承和派生 万字长文超详解

news2024/9/8 23:00:36

本文章内容来源于C++课堂上的听课笔记

继承和派生基础

继承是一种概念,它允许一个新创建的类(称为子类或派生类)获取另一个已经存在的类(称为父类或基类)的属性和行为。这就好比是子类继承了父类的特征。想象一下,如果有一个“动物”类,它有一些通用的特征和行为,然后我们有一个“猫”类,它可以继承“动物”类的这些通用特征,如四条腿、呼吸等。这样,“猫”类就继承了“动物”类

派生是指从已有类创建新类的过程。在这个过程中,新类继承了已有类的属性和方法。派生类可以添加新的属性或方法,也可以覆盖或修改继承的属性和方法。这就允许我们基于现有的代码构建和扩展新的功能。在上面的例子中,“猫”类就是通过派生(或继承)从“动物”类创建出来的

继承分为单继承和多重继承,如果一个派生类只有一个基类,就是单继承,否则是多重继承

派生类是基类的具体化,基类是派生类的抽象

假设已经声明了一个基类A,想要声明派生类B的一般形式为:

举个例子:

#include <iostream>
using namespace std;

// 定义基类 Animal
class Animal {
public:
    void makeSound() {
        cout << "Some generic sound...\n";
    }
};

// 定义派生类 Dog,它继承了 Animal 类
class Dog : public Animal {
public:
    void bark() {
        cout << "Woof! Woof!\n";
    }
};

int main() {
    // 创建一个 Dog 对象
    Dog myDog;

    // 调用继承自 Animal 类的方法
    myDog.makeSound();

    // 调用派生类 Dog 自己的方法
    myDog.bark();

    return 0;
}

对于继承方式,一般有三种,分别是public,protected,private,如果不写继承方式,默认是private

派生类中的成员包括从基类继承的成员和新增的成员两大部分

构造派生类时,会涉及下面的一些事件:

1.从基类接收成员。派生类把基类全部的成员接收过来

2.调整从基类接收的成员。接收基类成员是程序人员不能选择的,但是程序人员可以对这些成员作某些调整。如:  通过指定继承方式,改变基类成员在派生类中的访问属性;  可以在派生类中声明一个与基类成员同名的成员,覆盖基类的同名成员(与重载是不同的,这里叫做重写override

3.在声明派生类时增加的成员

4.在声明派生类时,一般还应当自己定义派生类的构造函数和析构函数,因为构造函数和析构函数是不能从基类继承的

继承方式

上面说过,在声明派生类的时候要加一个名叫“继承方式”的东西,这里就对其进行相关的解释

公有继承

基类的公用成员和保护成员在派生类中仍然保持其公用成员和保护成员的属性; 而基类的私有成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以引用它,而不能被派生类的成员函数引用,因此就成为派生类中的不可访问的成员

#include <iostream>
using namespace std;

// 定义基类 Vehicle
class Vehicle {
public:
    void startEngine() {
        cout << "Engine started.\n";
    }

    void stopEngine() {
        cout << "Engine stopped.\n";
    }
private:
    int m;
};

// 定义派生类 ElectricCar,它公有继承自 Vehicle
class ElectricCar : public Vehicle {
public:
    void chargeBattery() {
        cout << "Battery is charging.\n";
        cout << m << endl;//无法访问
    }
};

int main() {
    // 创建一个 ElectricCar 对象
    ElectricCar myElectricCar;

    // 使用继承自 Vehicle 类的方法
    myElectricCar.startEngine();

    // 使用 ElectricCar 类自己的方法
    myElectricCar.chargeBattery();

    // 使用继承自 Vehicle 类的方法
    myElectricCar.stopEngine();

    return 0;
}

私有继承

私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问它们,在派生类外不能访问它们。 私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以引用它们

对于不需要再往下继承的类的功能可以用私有继承方式把它隐蔽起来,这样,下一层的派生类无法访问它的任何成员

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

// 定义基类 Person
class Person {
private:
    string name;
    int age;

public:
    Person(const string& n, int a) : name(n), age(a) {}

    void displayInfo() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

// 定义派生类 Employee,私有继承自 Person
class Employee : private Person {
private:
    string jobTitle;

public:
    // 在 Employee 构造函数中初始化 Person 的成员
    Employee(const string& n, int a, const string& title)
        : Person(n, a), jobTitle(title) {}

    void displayEmployeeInfo() {
        // 在 Employee 类的成员函数中,可以访问继承的 Person 成员
        displayInfo();
        cout << "Job Title: " << jobTitle << endl;
    }
};

int main() {
    // 创建一个 Employee 对象
    Employee employee("John", 30, "Software Engineer");

    // 无法直接访问 Person 的私有成员
    // employee.displayInfo(); // 这行代码会导致编译错误

    // 但可以通过 Employee 的成员函数间接访问继承的 Person 成员
    employee.displayEmployeeInfo();

    return 0;
}

保护继承

保护成员:保护成员和私有成员类似,都不能直接被外界访问,但唯一不同的是保护成员可以被对应类的派生类的成员函数所访问

保护基类的公用成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有。也就是把基类原有的公用成员也保护起来,不让类外任意访问

三种继承方式总结如下

派生类的构造函数和析构函数

上面说过子类不能继承父类的构造函数和析构函数,但有一种方法是可以让子类去调用父类的构造函数的,这样,就可以同时初始化父类成员和子类成员了

一般形式如下:

派生类构造函数名(总参数表列): 基类构造函数名(参数表列)
  {派生类中新增数据成员初始化语句}

举一个例子:

Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) 
 //派生类构造函数
{    age=a;                        //在函数体中只对派生类新增的数据成员初始化
      addr=ad;
}

其中age,addr是派生类新增成员

也可以在类外实现派生类的构造函数,在类内仅仅声明即可

Student1∷Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
{age=a;     addr=ad;}

在类中对派生类构造函数作声明时,不包括基类构造函数名及其参数表列

不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数,实现对基类数据成员的初始化。也可以在同一个构造函数的定义中同时实现这两种功能。

Student1(int n, string nam,char s,int a, string ad):Student(n,nam,s),age(a),addr(ad){}

在派生类对象释放时,先执行派生类析构函数~Student1( ),再执行其基类析构函数~Student( )

有子对象的构造函数和组合

什么是子对象?简单来说就是一个类的成员中包含一个类对象,这个类对象就是子对象(subobject),这个子对象也是可以直接在构造函数中一起初始化的,非常方便

注意,如果这个子对象是本派生类的基类,还是正常的继承

如果这个子对象是另外一个已定义的类称为类的组合

类的组合和继承一样,是软件重用的重要方式。组合和继承都是有效地利用已有类的资源。但二者的概念和用法不同。继承是纵向的,组合是横向的

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

// 定义基类 Student
class Student {
private:
    string name;
    int age;

public:
    // 基类 Student 的构造函数
    Student(const string& n, int a) : name(n), age(a) {
        cout << "Student constructor called." << endl;
    }

    void displayInfo() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

// 定义子对象 Grade
class Grade {
private:
    char grade;

public:
    // Grade 构造函数
    Grade(char g) : grade(g) {
        cout << "Grade constructor called." << endl;
    }

    void displayGrade() {
        cout << "Grade: " << grade << endl;
    }
};

// 定义派生类 Student1,包含一个子对象 Grade
class Student1 : public Student {
private:
    Grade studentGrade;

public:
    // Student1 构造函数,调用基类 Student 和子对象 Grade 的构造函数
    Student1(const string& n, int a, char g)
        : Student(n, a), studentGrade(g) {
        cout << "Student1 constructor called." << endl;
    }

    // Student1 类的成员函数,可以访问基类 Student 和子对象 Grade 的成员
    void displayStudent1Info() {
        displayInfo();        // 访问基类成员函数
        studentGrade.displayGrade();  // 访问子对象成员函数
    }
};

int main() {
    // 创建一个 Student1 对象
    Student1 student1("Alice", 20, 'A');

    // 调用 Student1 类的成员函数,间接调用基类 Student 和子对象 Grade 的成员函数
    student1.displayStudent1Info();

    return 0;
}

执行派生类构造函数的顺序是: ① 调用基类构造函数,对基类数据成员初始化; ② 调用子对象构造函数,对子对象数据成员初始化; ③ 再执行派生类构造函数本身,对派生类数据成员初始化

基类构造函数和子对象的次序可以是任意的,如果有多个子对象,派生类构造函数的写法依此类推,应列出每一个子对象名及其参数表列。

多级派生时的构造函数

#include <iostream>
#include <string>

using namespace std;

// 基类 Person
class Person {
private:
    string name;

public:
    // 基类构造函数
    Person(const string& n) : name(n) {
        cout << "Person constructor called." << endl;
    }

    void displayName() {
        cout << "Name: " << name << endl;
    }
};

// 派生类 Student,继承自 Person
class Student : public Person {
private:
    int studentID;

public:
    // Student 构造函数,调用基类 Person 构造函数
    Student(const string& n, int id) : Person(n), studentID(id) {
        cout << "Student constructor called." << endl;
    }

    void displayStudentInfo() {
        displayName(); // 访问基类成员函数
        cout << "Student ID: " << studentID << endl;
    }
};

// 派生类 GraduateStudent,继承自 Student
class GraduateStudent : public Student {
private:
    string researchTopic;

public:
    // GraduateStudent 构造函数,调用直接基类 Student 构造函数
    GraduateStudent(const string& n, int id, const string& topic)
        : Student(n, id), researchTopic(topic) {
        cout << "GraduateStudent constructor called." << endl;
    }

    void displayGraduateStudentInfo() {
        displayStudentInfo(); // 访问直接基类 Student 的成员函数
        cout << "Research Topic: " << researchTopic << endl;
    }
};

int main() {
    // 创建一个 GraduateStudent 对象
    GraduateStudent gradStudent("John Doe", 12345, "Machine Learning");

    // 调用 GraduateStudent 类的成员函数,间接调用基类 Person 和直接基类 Student 的成员函数
    gradStudent.displayGraduateStudentInfo();

    return 0;
}

main 函数中创建 GraduateStudent 对象时,首先调用 Person 类的构造函数,然后调用 Student 类的构造函数,最后调用 GraduateStudent 类的构造函数

派生类构造函数和析构函数注意点

1.当不需要对派生类新增的成员进行任何初始化操作时,派生类构造函数的函数体可以为空,即构造函数是空函数

2.如果在基类中没有定义构造函数,或定义了没有参数的构造函数,那么在定义派生类构造函数时可不写基类构造函数

3.如果在基类和子对象类型的声明中都没有定义带参数的构造函数,而且也不需对派生类自己的数据成员初始化,则可以不必显式地定义派生类构造函数

4.如果在基类或子对象类型的声明中定义了带参数的构造函数,那么就必须显式地定义派生类构造函数

5.如果在基类中既定义无参的构造函数,又定义了有参的构造函数(构造函数重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数

6.在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理

7.调用的顺序与构造函数正好相反: 先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理

多重继承

注意派生类中的构造函数怎么写的

#include <iostream>
#include <string>

using namespace std;

// 基类 Shape(形状)
class Shape {
protected:
    int width;
    int height;

public:
    // Shape 类构造函数
    Shape(int w, int h) : width(w), height(h) {
        cout << "Shape constructor called." << endl;
    }

    void displayShapeInfo() {
        cout << "Shape - Width: " << width << ", Height: " << height << endl;
    }
};

// 基类 Color(颜色)
class Color {
protected:
    string color;

public:
    // Color 类构造函数
    Color(const string& c) : color(c) {
        cout << "Color constructor called." << endl;
    }

    void displayColor() {
        cout << "Color: " << color << endl;
    }
};

// 派生类 ColoredShape(有颜色的形状),继承自 Shape 和 Color
class ColoredShape : public Shape, public Color {
public:
    // ColoredShape 类构造函数,显式调用基类构造函数
    ColoredShape(int w, int h, const string& c,int id) : Shape(w, h), Color(c) id(id){
        cout << "ColoredShape constructor called." << endl;
    }

    void displayColoredShapeInfo() {
        displayShapeInfo(); // 访问 Shape 类的成员函数
        displayColor();     // 访问 Color 类的成员函数
    }
    int id;
};

int main() {
    // 创建一个 ColoredShape 对象
    ColoredShape coloredShape(10, 5, "Red");

    // 调用 ColoredShape 类的成员函数,间接调用基类 Shape 和 Color 的成员函数
    coloredShape.displayColoredShapeInfo();

    return 0;
}

虚基类

如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员

C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员

虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。 声明虚基类的一般形式为

class 派生类名: virtual 继承方式 基类名

#include <iostream>

// 基类:动物
class Animal {
public:
    Animal(const std::string& name) : name(name) {}

    void eat() const {
        std::cout << name << " is eating." << std::endl;
    }

    void sleep() const {
        std::cout << name << " is sleeping." << std::endl;
    }

private:
    std::string name;
};

// 虚基类:飞行动物
class FlyingAnimal : virtual public Animal {
public:
    FlyingAnimal(const std::string& name) : Animal(name) {}

    void fly() const {
        std::cout << Animal::getName() << " is flying." << std::endl;
    }
};

// 派生类:哺乳动物
class Mammal : virtual public Animal {
public:
    Mammal(const std::string& name) : Animal(name) {}

    void giveBirth() const {
        std::cout << Animal::getName() << " is giving birth." << std::endl;
    }
};

// 派生类:既是哺乳动物又可以飞的动物
class Bat : public FlyingAnimal, public Mammal {
public:
    Bat(const std::string& name) : Animal(name), FlyingAnimal(name), Mammal(name) {}
};

int main() {
    Bat bat("Batman");

    // 通过虚基类,避免了二义性问题
    bat.eat();
    bat.sleep();
    bat.fly();
    bat.giveBirth();

    return 0;
}

基类和派生类的转换

在C++中,基类和派生类之间的转换主要涉及两种类型:向上转型(Upcasting)和向下转型(Downcasting)。这些转型可能涉及到指针和引用。

向上转型(Upcasting):

向上转型是将派生类的指针或引用转换为基类的指针或引用。这是一个安全的操作,因为派生类对象包含基类的部分。这样的转换通常是自动进行的,不需要显式的转换操作

#include <iostream>

class Base {
public:
    virtual void display() const {
        std::cout << "Base class display" << std::endl;
    }
};

class Derived : public Base {
public:
    void display() const override {
        std::cout << "Derived class display" << std::endl;
    }

    void additionalFunction() const {
        std::cout << "Additional function in the derived class" << std::endl;
    }
};

int main() {
    Derived derivedObj;
    Base* basePtr = &derivedObj; // 向上转型

    // 调用的是 Derived 类的 display 方法
    basePtr->display();

    // 无法直接调用 Derived 类的 additionalFunction
    // basePtr->additionalFunction(); // 这行代码会产生编译错误

    return 0;
}

向下转型(Downcasting):

向下转型是将基类的指针或引用转换为派生类的指针或引用。这是一个潜在的危险操作,因为基类可能无法包含派生类的所有信息。因此,向下转型时通常需要进行显式的类型转换,并且应该在确保安全性的情况下进行

class Base {
    //...
};

class Derived : public Base {
    //...
};

int main() {
    Base* basePtr = new Derived();
    
    // 向下转型,需要显式的类型转换
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    if (derivedPtr) {
        // 转换成功,可以使用 derivedPtr 操作 Derived 类的成员
    } else {
        // 转换失败,basePtr 实际上不指向 Derived 类的对象
    }

    delete basePtr;
    return 0;
}

在上述代码中,使用了 dynamic_cast 运算符进行向下转型。这个运算符在运行时检查转型的有效性,如果转型合法,则返回指向派生类对象的指针;否则,返回空指针。

总的来说,向上转型是安全的,而向下转型需要谨慎并使用适当的手段进行检查,以避免潜在的运行时错误。在进行向下转型时,通常使用 dynamic_cast 运算符进行类型检查,或者在一些情况下,可以使用 static_cast 运算符,但要确保转型是安全的。

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

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

相关文章

拼多多官方开放平台接口app商品详情接口获取实时商品详情数据演示

拼多多开放平台提供了一种名为“商品详情接口”的API接口&#xff0c;它允许卖家从自己的系统中快速获取商品信息&#xff0c;如商品标题、描述、价格、库存等&#xff0c;并将这些信息展示在自己的店铺中。通过该接口&#xff0c;卖家可以更好地管理自己的商品库存和销售&…

【前端学java】复习巩固-Java中的对象比较(14)

往期回顾&#xff1a; 【前端学java】JAVA开发的依赖安装与环境配置 &#xff08;0&#xff09;【前端学 java】java的基础语法&#xff08;1&#xff09;【前端学java】JAVA中的packge与import&#xff08;2&#xff09;【前端学java】面向对象编程基础-类的使用 &#xff08…

9.docker镜像Tag为none的原因

1.现象 使用docker images命令查看镜像列表&#xff0c;会发现存在许多标签为none的镜像&#xff1a; 2. 原因 docker镜像标签为none的原因如下&#xff1a; &#xff08;1&#xff09;构建或重新拉取同名同Tag的新镜像&#xff1a;构建或重新拉取同名同Tag的新镜像后&…

SpringSecurity6 | 问题答疑

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

计算机专业毕业后的选择有哪些?

这就得看你自己的职业规划啦&#xff0c;现在考公考研也很卷&#xff0c;就业环境也不太乐观&#xff0c;既然这样&#xff0c;做选择就遵守自己的内心&#xff0c;从自己的职业规划和兴趣来选吧。 下面我们就先对这三条路的职业方向来做分析&#xff1a; 1.考研 考研也有两…

mongodb——概念介绍(文档,集合,固定集合,元数据,常用数据类型)

mongodb 层级结构 实例&#xff1a;系统上运行的进程及节点集&#xff0c;一个实例可以有多个库&#xff0c;默认端口 27017。 库&#xff1a;多个集合组成数据库&#xff0c;每个数据库都是独立的&#xff0c;有自己的用户、权限信息&#xff0c;独立的存储文件集 合。 集合&…

选择java商城开发商需要注意哪些方面?

Java商城开发是一项庞大而复杂的任务&#xff0c;选择一家合适的开发商至关重要。那么&#xff0c;我们在选择Java商城开发商时&#xff0c;需要注意哪些方面呢&#xff1f; 1、专业经验 选择具有丰富经验的开发商是至关重要的。开发商应该拥有多年的Java开发经验&#xff0c;…

Thales安全解决方案:国家网络安全的关键

随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。在这个背景下&#xff0c;Thales安全解决方案正成为提高国家网络安全的关键。本文将探讨Thales安全解决方案如何为国家网络安全保驾护航。 一、Thales安全解决方案概述 Thales安全解决方案是一种全方位的网络安全防护…

武汉凯迪正大KDHG-220P互感器综合测试仪

主要特点 武汉凯迪正大KDHG-220P互感器综合测试仪&#xff0c;仅需进行简单的数字设定&#xff1a;设定互感器的额定参数。仪器将全过程自动记录数据&#xff0c;并自动将变比极性、伏安特性曲线等计算并显示出来&#xff0c;省去换线、手动调压、人工记录、整理、描曲线等烦琐…

Spring Boot 与 Gzip 压缩

响应压缩是 Web 应用一种常见的优化手段&#xff0c;通过压缩算法减小传输数据的体积&#xff0c;提高传输效率、节约带宽。客户端接收到数据后&#xff0c;使用相同的算法对数据进行解压从而获取到原始数据。 客户端和服务器需要通过 Header 来协商双方支持的压缩算法。 Acc…

我了解的3D游戏引擎和图形开发框架

如果你像我一样&#xff0c;没有什么比编写或设计软件更让人兴奋的了。 当我编写代码时&#xff0c;我所获得的巨大快乐促使我开发了跨越许多软件领域的项目。 这些领域之一是为本机应用程序、桌面展示或 Web 创建 3D 图形。 我从未创建过任何 3D 游戏&#xff0c;但很多时候我…

【Rust】快速教程——一直在单行显示打印、输入、文件读写

前言 恨不过是七情六欲的一种&#xff0c;再强大的恨也没法独占整颗心&#xff0c;总有其它情感隐藏在心底深处&#xff0c;说不定在什么时候就会掀起滔天巨浪。——《死人经》 图中是Starship扔掉下面的燃料罐&#xff0c;再扔掉头顶的翅膀后&#xff0c;再翻转过来着陆火星的…

SASS/SCSS精华干货教程

目录 介绍 基本说明 特点 sass语法格式sass的语法格式一共有两种&#xff0c;一种是以".scss"作为拓展名&#xff0c;一种是以".sass"作为拓展名&#xff0c;这里我们只讲拓展名&#xff1a; 编译环境安装 Vscode安装编译插件 简单使用 sass语法扩张…

15.ORACLE11g的归档方式和日志文件的相关操作

ORACLE11g的归档方式和日志文件的相关操作 一、什么是日志文件1、在线日志文件2、归档日志文件 二、Oracle 11g 归档方式&#xff1a;1、归档方式状态2、归档日志方式下数据库的工作原理3、配置归档日志方式3.1 开启归档模式3.2 日志文件相关操作&#xff1a; 4、oracle11g联机…

API接口测试工具为什么尤其重要

在现代软件开发中&#xff0c;API接口测试工具扮演着关键的角色&#xff0c;连接不同的软件组件&#xff0c;实现数据传递和功能调用。为确保API的可靠性、安全性和性能&#xff0c;此工具成为不可或缺的一部分。本文将介绍API接口测试工具的重要性! 1. 自动化测试的效率 API接…

Java集合大总结——Set的简单使用

Set的简单介绍 Set接口是Collection的子接口&#xff0c;Set接口相较于Collection接口没有提供额外的方法。Set 集合不允许包含相同的元素&#xff0c;如果试把两个相同的元素加入同一个 Set 集合中&#xff0c;则添加操作失败。Set集合支持的遍历方式和Collection集合一样&am…

【前端学java】Java中的异常处理(15)完结

往期回顾&#xff1a; 【前端学java】JAVA开发的依赖安装与环境配置 &#xff08;0&#xff09;【前端学java】java的基础语法&#xff08;1&#xff09;【前端学java】JAVA中的packge与import&#xff08;2&#xff09;【前端学java】面向对象编程基础-类的使用 &#xff08;…

Universal adversarial perturbations(2017 CVPR)

Universal adversarial perturbations----《普遍对抗扰动》 通俗UAP算法步骤理解&#xff1a;对于 x i ∈ X {x_i} \in X xi​∈X 的每个采样数据点&#xff0c;比较 k ^ ( x i v ) \hat k({x_i} v) k^(xi​v) 与 k ^ ( x i ) \hat k({x_i}) k^(xi​) &#xff0c;如果 k…

电脑监控软件都有哪些,哪款好用丨全网盘点

电脑监控软件是一种用于监视和控制计算机的软件工具&#xff0c;可以帮助企业和个人了解计算机的使用情况&#xff0c;保护数据安全&#xff0c;提高工作效率等。 电脑监控软件都有哪些&#xff1a; 1、域之盾软件 这是一款功能强大的电脑监控软件&#xff0c;可以实时监控电脑…

算法之回溯

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…