C++语言学习(八)—— 继承、派生与多态(二)

news2024/11/14 2:13:36

目录

一、多继承

1.1 多继承中的二义性问题

1.2 虚基类

二、多态

2.1 静态绑定与静态多态

2.2 动态绑定与动态多态

三、运算符重载

3.1 重载++、- -运算符

3.2 重载赋值运算符

3.3 重载输出流<<、输入流>>运算符

3.3.1 重载输出流(<<)运算符

3.3.2 重载输入流(>>)运算符

四、赋值兼容规则

五、虚函数

5.1 虚函数的定义

5.2 虚析构函数

5.3 纯虚函数及抽象类


一、多继承

在C++中,多继承是一种面向对象的编程技术,允许一个类从多个父类派生而来,继承多个父类的属性和方法。

在C++中,可以使用逗号分隔的方式来指定多个父类。语法如下:

class DerivedClass : public BaseClass1, public BaseClass2, ... {
   // 类的定义
};

在多继承中,派生类继承了所有父类的成员,包括数据成员和成员函数。如果多个父类中有同名的成员,派生类需要通过作用域限定符来指定使用哪个父类的成员。

多继承为程序带来了灵活性和代码复用的好处,但也需要小心处理一些问题,比如菱形继承问题菱形继承是指派生类同时从两个父类继承某个公共的父类,可能导致代码复杂性和二义性问题。在C++中,可以使用虚继承来解决菱形继承的问题。

1.1 多继承中的二义性问题

多继承中的二义性问题是指在派生类中存在两个或多个父类,这些父类拥有同名的成员(包括成员函数和数据成员),从而导致在派生类中无法判断要使用哪个父类的成员。

当多个父类拥有同名成员时,如果派生类直接访问同名成员,会导致编译错误,编译器无法确定要使用哪个父类的成员。 这种情况下,需要在派生类中通过作用域限定符来明确指定使用哪个父类的成员。例如:

// BaseClass1 和 BaseClass2 都有同名的成员函数 foo()
class DerivedClass : public BaseClass1, public BaseClass2 {
   public:
      void callFoo() {
         // 使用作用域限定符来指定使用哪个父类的成员函数
         BaseClass1::foo();
      }
};

另一种避免二义性问题的方法是使用虚继承虚继承是指通过使用关键字virtual在基类之间建立虚继承关系,这样可以确保只有一份共享的基类子对象。例如:

class BaseClass {
   // 类定义
};

// 使用虚继承
class DerivedClass1 : virtual public BaseClass {
   // 类定义
};

// 使用虚继承
class DerivedClass2 : virtual public BaseClass {
   // 类定义
};

// 派生类同时从 DerivedClass1 和 DerivedClass2 继承
class DerivedClass3 : public DerivedClass1, public DerivedClass2 {
   // 类定义
};

在使用虚继承时,由于只有一份共享的基类子对象,因此二义性问题会被解决。

1.2 虚基类

虚基类是在C++中使用虚继承(virtual inheritance)实现的。虚基类解决了多继承中的二义性问题。

当一个派生类从多个基类继承时,如果这些基类中有一个公共基类作为虚基类,那么虚基类将被派生类共享。这意味着虚基类在派生类中只会有一份实例,避免了二义性问题。

虚继承的语法是通过在派生类的继承列表中使用关键字virtual来声明虚继承。例如:

class BaseClass {
   // 类定义
};

// 虚继承
class DerivedClass : virtual public BaseClass {
   // 类定义
};

当派生类通过虚继承从多个基类继承时,如果这些基类中有一个公共基类,那么这个公共基类将在派生类中只有一份实例。这样,在派生类中访问公共基类的成员时就不会产生二义性。

二、多态

C++中的多态是面向对象编程的一个重要概念,它允许使用基类的指针或引用来调用派生类的方法,实现了动态绑定(dynamic binding)。

多态性在面向对象编程中非常有用,它允许以一种通用的方式操作不同类型的对象,提高了代码的可扩展性和可维护性。

2.1 静态绑定与静态多态

在C++中,静态绑定(static binding)是指在编译时确定函数调用的具体实现;而静态多态(static polymorphism)是指在编译时根据函数的参数类型来选择合适的函数实现。

静态绑定是指在编译时,根据调用对象的静态类型确定调用的函数。也就是说,在编译时,编译器会根据指针或引用的静态类型来确定调用的函数,而不会考虑指针或引用所指向的对象的实际类型。

静态多态是指通过函数重载实现的多态。在C++中,函数重载允许定义多个同名函数,但它们的参数列表必须不同。在编译时,根据调用对象的静态类型和函数的参数列表来确定调用的函数。

下面是一个简单的示例,展示了静态绑定和静态多态的使用:

class Shape {
public:
    void draw() {
        cout << "Drawing a shape." << endl;
    }

    void draw(int width) {
        cout << "Drawing a shape with width: " << width << endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() {
        cout << "Drawing a rectangle." << endl;
    }
};

int main() {
    Shape* shape1 = new Shape();
    shape1->draw(); // 输出:Drawing a shape.
    shape1->draw(10); // 输出:Drawing a shape with width: 10

    Shape* shape2 = new Rectangle();
    shape2->draw(); // 输出:Drawing a rectangle.
    shape2->draw(20); // 输出:Drawing a shape with width: 20

    delete shape1;
    delete shape2;

    return 0;
}

在上面的示例中,Shape中定义了重载draw()函数,一个是不带参数的,一个是带一个int类型参数的。Rectangle继承Shape,并重写draw()函数。

在main()函数中,通过Shape指针shape1来调用draw()函数,编译器根据shape1的类型为Shape,所以会调用Shape类中定义的draw()函数。

而通过Shape指针shape2来调用draw()函数时,编译器根据shape2的类型为Shape,但实际指向的对象是Rectangle的对象,所以会调用Rectangle类中重写的draw()函数。同样,根据shape2的类型和函数参数列表,调用的是Shape中定义的带一个int类型参数的draw()函数。

2.2 动态绑定与动态多态

在C++中,动态绑定(dynamic binding)是指在运行时根据对象的实际类型来确定函数调用的具体实现;动态多态(dynamic polymorphism)是指通过运行时多态性实现的多态特性。

动态绑定是指通过使用虚函数实现的动态多态性。当使用基类的指针或引用来调用一个虚函数时,编译器会在运行时根据实际对象的类型来确定调用的函数。这就意味着,虚函数的具体实现是在运行时动态绑定的,而不是在编译时静态绑定的。

动态多态是指通过继承和虚函数实现的多态性。在C++中,可以将一个函数声明为虚函数,使用virtual关键字来标识。派生类可以覆盖基类的虚函数,实现自己的特定行为。在运行时,如果通过基类的指针或引用调用虚函数,实际执行的是对象的实际类型对应的函数。

下面是一个简单的示例,展示了动态绑定和动态多态的使用:

class Shape {
public:
    virtual void draw() {
        cout << "Drawing a shape." << endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() {
        cout << "Drawing a rectangle." << endl;
    }
};

int main() {
    Shape* shape1 = new Shape();
    shape1->draw(); // 输出:Drawing a shape.

    Shape* shape2 = new Rectangle();
    shape2->draw(); // 输出:Drawing a rectangle.

    delete shape1;
    delete shape2;

    return 0;
}

在上面的示例中,Shape中的draw()函数被声明为虚函数Rectangle继承自Shape,并重写了draw()函数。

在main()函数中,通过Shape指针shape1来调用draw()函数。由于draw()函数是虚函数,在运行时会根据shape1指向的对象的实际类型来确定调用的函数,因此会调用Shape中定义的draw()函数。

通过Shape指针shape2来调用draw()函数时,它指向的对象是Rectangle的对象,所以在运行时会调用Rectangle中重写的draw()函数。

三、运算符重载

运算符重载:是指在编程语言中,为自定义的类类型或用户自定义的数据类型添加一些额外的操作符和功能。通过重载操作符,可以使得自定义的类类型对象能够像内置类型一样使用操作符进行相应的操作。

在C++中,运算符可以使用关键字operator重载,例如:

class MyClass {
public:
    int value;

    MyClass(int val) : value(val) {}

    MyClass operator+(const MyClass& other) const {
        return MyClass(value + other.value);
    }
};

在上面的例子中,我们重载加法运算符+,使得两个MyClass对象可以进行相加操作并返回一个新的MyClass对象

注意:

  1. 运算符只能重载已有的运算符,不能创建新的运算符;
  2. 运算符重载的函数必须是成员函数或友元函数;
  3. 运算符重载函数的参数个数和类型应该与原始运算符相匹配;
  4. 运算符重载函数一般需要返回一个新的对象,而不是修改原始对象。

3.1 重载++、- -运算符

重载++和--运算符是常见的运算符重载操作之一,用于实现对象的自增和自减操作。

在C++中,++和--运算符可以分为前置运算和后置运算两种形式。前置运算符表示在变量之前进行自增或自减操作,后置运算符表示在变量之后进行自增或自减操作。

下面是对++和--运算符的重载示例:

class MyClass {
public:
    int value;

    MyClass(int val) : value(val) {}

    // 前置自增运算符重载
    MyClass& operator++() {
        ++value;
        return *this;
    }

    // 前置自减运算符重载
    MyClass& operator--() {
        --value;
        return *this;
    }

    // 后置自增运算符重载
    MyClass operator++(int) {
        MyClass copy(*this);
        ++value;
        return copy;
    }

    // 后置自减运算符重载
    MyClass operator--(int) {
        MyClass copy(*this);
        --value;
        return copy;
    }
};

在上面的示例中,我们重载了前置和后置的自增和自减运算符。对于前置运算符,我们直接对value进行自增自减操作,并返回修改后的对象自身的引用。对于后置运算符,我们先创建一个当前对象的副本,然后对value进行自增和自减操作,并返回这个副本。

使用示例:

MyClass obj(10);
++obj;     // 前置自增
obj++;     // 后置自增

--obj;     // 前置自减
obj--;     // 后置自减
 

3.2 重载赋值运算符

重载赋值运算符(=)是一种特殊的运算符重载,用于在对象间进行赋值操作。通过重载赋值运算符,我们可以自定义对象之间的赋值行为。

在C++中,赋值运算符的重载函数以特殊的成员函数形式存在,其名称为operator=。它接受一个参数,这个参数表示要赋值给对象的值。

下面是一个重载赋值运算符的示例:

class MyClass {
public:
    int value;

    MyClass(int val) : value(val) {}

    // 赋值运算符重载
    MyClass& operator=(const MyClass& other) {
        value = other.value;
        return *this;
    }
};

在上面的示例中,我们重载了赋值运算符,参数为类型为const MyClass&other对象。我们将other对象的value成员赋值给当前对象的value成员,并返回当前对象的引用。

使用示例:

MyClass obj1(10);
MyClass obj2(20);

obj1 = obj2;   // 使用赋值运算符将obj2的值赋给obj1
 

3.3 重载输出流<<、输入流>>运算符

重载输出流(<<)和输入流(>>)运算符是一种重载C++标准库中的流插入(output)和流提取(input)操作符,使得我们可以以自定义的方式向流中输出数据或者从流中提取数据。

3.3.1 重载输出流(<<)运算符

重载输出流运算符(<<)的函数通常被定义为友元函数,它以个参数的形式存在,第一个参数是ostream类型输出流对象,第二个参数是要输出的自定义类型对象。函数体中实现了将自定义类型的对象输出到流中的逻辑。函数的返回值通常是ostream对象引用,以便连续输出。

下面是一个重载输出流运算符的示例:

class MyData {
public:
    int value1;
    int value2;

    MyData(int val1, int val2) : value1(val1), value2(val2) {}

    // 输出流运算符重载
    friend std::ostream& operator<<(std::ostream& os, const MyData& data) {
        os << "Value 1: " << data.value1 << ", Value 2: " << data.value2;
        return os;
    }
};

在上面的示例中,我们重载了输出流运算符,将MyData对象的成员变量值输出到流中,并返回输出流对象的引用。

使用示例:

MyData data(10, 20);
std::cout << data << std::endl;   // 使用重载的输出流运算符将data对象的值输出到控制台

3.3.2 重载输入流(>>)运算符

重载输入流运算符(>>)的函数也通常被定义为友元函数,它以个参数的形式存在,第一个参数是istream类型的输入流对象,第二个参数是要输出的自定义类型的对象引用。函数体中实现了从流中提取数据并赋值给自定义类型的对象的逻辑。

下面是一个重载输入流运算符的示例:

class MyData {
public:
    int value1;
    int value2;

    // 输入流运算符重载
    friend std::istream& operator>>(std::istream& is, MyData& data) {
        is >> data.value1 >> data.value2;
        return is;
    }
};

使用示例:

MyData data;
std::cin >> data;   // 使用重载的输入流运算符从控制台输入数据并赋值给data对象

四、赋值兼容规则

赋值兼容规则是指在进行赋值操作时,要求赋值号两边的操作数类型能够相互转换,并且满足特定的规则。

在C++中,赋值兼容规则分为以下两种情况:

  1. 基本数据类型的赋值兼容规则:

    • 相同类型的变量可以直接赋值给对应类型的变量。
    • 较小类型的变量可以赋值给较大类型的变量,这种情况下会自动进行类型转换,不会丢失数据。
    • 浮点数可以赋值给整数类型,但会发生截断。

    例如:

    int a = 10;
    double b = 3.14;
    a = b;  // 自动进行类型转换,b的值3.14会被截断为整数3,赋值给a。
    

  2. 类类型的赋值兼容规则:

    • 类类型之间的赋值必须通过拷贝构造函数或者赋值运算符来完成。
    • 如果类定义了拷贝构造函数或者赋值运算符,那么可以将一个对象赋值给另一个对象,这会调用拷贝构造函数或者赋值运算符进行对象间的成员变量的赋值操作。
    • 如果没有定义拷贝构造函数或者赋值运算符,则无法直接进行对象之间的赋值操作。

    例如:

    class MyClass {
    public:
        int value;
    
        MyClass(int val) : value(val) {}
    };
    
    MyClass obj1(10);
    MyClass obj2 = obj1;  // 调用拷贝构造函数,将obj1的value赋值给obj2的value。
    

五、虚函数

5.1 虚函数的定义

C++中的虚函数是一种特殊的成员函数,它允许在派生类中重写基类的同名函数。通过使用虚函数,可以实现多态性,即在运行时根据对象的实际类型调用相应的函数。

要将成员函数声明虚函数,只需要在基类中将函数声明为虚函数,使用关键字"virtual"即可。例如:

class Base {
public:
    virtual void print() {
        cout << "This is Base class." << endl;
    }
};

class Derived : public Base {
public:
    void print() override {
        cout << "This is Derived class." << endl;
    }
};

在上面的例子中,print()函数被声明为虚函数。在派生类Derived中,通过重写print()函数,可以实现不同的行为。当使用基类指针或引用调用print()函数时,会根据对象的实际类型来确定调用函数。

虚函数在派生类中可以被重写,也可以在派生类中使用override关键字来标记重写,以增强代码的可读性。同时,可以使用virtual关键字来显式地标记派生类中的虚函数(非必需)。

虚函数是多态性的基础,在使用多态性时,通常会通过基类指针或引用来操作派生类对象。例如:

Base* b = new Derived();
b->print(); // 调用Derived类的print()函数

在上面的代码中,使用基类指针b指向派生类对象Derived,并调用print()函数。由于print()函数被声明为虚函数,因此会根据对象的实际类型调用Derived的版本。

5.2 虚析构函数

C++中的虚析构函数用于实现基类和派生类之间的多态析构,以确保在删除派生类对象时,能正确调用派生类和基类的析构函数。

在C++中,如果一个类拥有虚函数,那么最好将其析构函数也声明为虚函数。当使用基类指针删除一个派生类对象时,如果基类的析构函数不是虚函数,只会调用基类的析构函数,而不会调用派生类的析构函数。这可能导致派生类中的资源没有被正确释放,造成内存泄漏。

以下是一个使用虚析构函数的示例:

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

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

int main() {
    Base* basePtr = new Derived();
    delete basePtr;
    
    return 0;
}

在上面的例子中,Base的析构函数被声明为虚函数,Derived继承自Base重写了析构函数。在main函数中,我们创建了一个Derived对象的指针,并将其赋值给Base类指针。当我们使用delete删除basePtr时,会触发析构函数的调用。由于Base类的析构函数是虚函数,因此会先调用Derived的析构函数,再调用Base的析构函数。

输出结果:

Derived destructor
Base destructor

5.3 纯虚函数及抽象类

纯虚函数是在基类中通过在函数声明前加上关键字"virtual"并在函数后面加上"= 0"来声明的一种特殊的虚函数纯虚函数没有函数体,其目的是为了在基类中只定义接口而不实现具体的功能。

抽象类是包含至少一个纯虚函数的类,它不能被实例化,只能作为基类来派生新的类。抽象类的主要目的是提供一种接口或者规范,它定义了派生类必须实现的纯虚函数。

纯虚函数和抽象类常常一起使用,通过在基类中声明纯虚函数,可以强制派生类实现这些函数,从而实现多态性。派生类需要实现基类中的纯虚函数才能被编译通过。

以下是一个简单的示例代码:

class Animal {
public:
    virtual void sound() const = 0; // 纯虚函数
};

class Cat : public Animal {
public:
    void sound() const override {
        cout << "喵喵喵" << endl;
    }
};

class Dog : public Animal {
public:
    void sound() const override {
        cout << "汪汪汪" << endl;
    }
};

int main() {
    Animal* animPtr = new Cat();
    animPtr->sound(); // 输出: 喵喵喵
    
    animPtr = new Dog();
    animPtr->sound(); // 输出: 汪汪汪
    
    delete animPtr;
    
    return 0;
}

在上述示例中,Animal是一个抽象类,其sound()函数被声明为纯虚函数,因此Animal不能被实例化。通过派生类CatDog分别实现了sound()函数,使它们成为了具体的类。在main函数中,使用Animal指针指向不同的派生类对象,调用sound()函数实现了多态性。

纯虚函数和抽象类是C++中实现多态性的关键机制,它们允许基类定义接口并要求派生类实现,从而实现了程序的灵活性和可扩展性。

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

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

相关文章

VirtualBox下安装Centos7.9虚拟机的踩坑记录

目录 0 背景1 安装Centos7.91.1 下载iso镜像1.2 正常安装虚拟机1.3 将用户添加到sudoers并免密1.4 更新yum源1.5 配置静态IP1.6 通过ssh工具传文件1.7 总结 0 背景 最近搞了个便宜的低配台式机用来敲代码&#xff0c;主要是嫌弃笔记本屏幕太小了&#xff0c;想用个大屏。 然后我…

129页《战略推演:获取竞争优势的思维与方法》

知识星球APP搜索【战略咨询文库】&#xff0c;下载700多份资料 一、战略思维 差异化战略 产品或服务差异化&#xff1a;通过提供独特的产品特性、功能、设计或品质&#xff0c;满足特定客户群体的需求&#xff0c;从而与竞争对手区分开来。例如&#xff0c;苹果公司以其创新…

LeetCode 面试经典 150 题回顾

目录 一、数组 / 字符串 1.合并两个有序数组 &#xff08;简单&#xff09; 2.移除元素 &#xff08;简单&#xff09; 3.删除有序数组中的重复项 &#xff08;简单&#xff09; 4.删除有序数组中的重复项 II&#xff08;中等&#xff09; 5.多数元素&#xff08;简单&am…

数据结构与算法的实现与优化

目录 引言 数据结构与算法的重要性 实现与优化的基本原则 线性表的实现 顺序表与链表的实现 静态链表与动态链表的对比 栈与队列的实现 顺序栈与链栈的实现 顺序队列、链队列、循环队列与双端队列的实现 高级树结构的实现 二叉树、平衡树与哈夫曼树的实现 图的实现…

SSRF——redis(未授权访问)①

本文介绍如何复现并利用Redis的未授权访问漏洞&#xff0c;以达到远程代码执行和获取系统权限的目的。Redis版本&#xff08;2.8&#xff09;&#xff0c;于Ubuntu环境下进行。 SSRF&#xff08;Server-Side Request Forgery&#xff09;是一种网络安全漏洞&#xff0c;允许攻击…

前端实现两张图片合成,图片换背景,简单p图程序

前言 最近在自己做一个图文工具网站&#xff0c;图片背景替换功能&#xff0c;后台通过opencv的AI算法抠出了图片主元素&#xff0c;现在需要把抠出来的元素换一个背景色&#xff0c;或者合成到一张背景图片中&#xff0c;如何操作呢&#xff1f; 实现方式Demo 我考虑的是…

linux系统使用 docker 来部署运行 mysql5.7 并配置 docker-compose-mysql.yml 文件

Docker是一个开源的容器化平台&#xff0c;旨在简化应用程序的创建、部署和管理。它基于OS-level虚拟化技术&#xff0c;通过将应用程序和其依赖项打包到一个称为容器的标准化单元中&#xff0c;使得应用程序可以在任何环境中快速、可靠地运行。 Docker的优势有以下几个方面&a…

【解决问题】linux系统运行程序可以打印信息到屏幕,重定向到一个文件,文件中没有信息

在Linux系统中运行程序时&#xff0c;如果你发现程序打印的信息无法通过重定向写入到文件中&#xff0c;那么很可能是因为程序将信息打印到了标准错误流&#xff08;stderr&#xff09;&#xff0c;而不是标准输出流&#xff08;stdout&#xff09;。重定向操作通常只对标准输出…

20240826 每日AI必读资讯

突发&#xff01;IBM中国研发岗访问权限一夜关闭&#xff0c;千人或被裁 - IBM中国区一夜关闭了研发测试岗员工的内部权限&#xff0c;波及1000多名员工。 - 今年3月&#xff0c;IBM大中华区董事长陈旭东在媒体及分析师沟通会上表示&#xff0c;公司在IBM大中华区的战略将聚焦…

《重生到现代之从零开始的C语言生活》—— 指针4

字符指针变量 在指针类型中我们知道有一种指针类型为字符指针char* 如果我们想用他 int main() {char p w;char *d &p;return 0; }他可以存放字符变量的地址 也可以这么使用 int main() {char *d "hello"return 0 ; }其本质是把字符串"hello"的…

动态链接库——深入探讨C++程序中.so技术细节和实现方式及C程序中动静态库的区别(+工程分步骤编译库)

在开发中&#xff0c;动态链接库&#xff08;DLL&#xff09;和共享对象&#xff08;Shared Object&#xff09;.so文件的使用成为提升程序灵活性和重用性的关键手段。如下相关工具&#xff0c;GNU Libtool: 一种用于创建可移植共享库的工具。Dynamic Linker: 关于动态链接器的…

VSCode - 终端使用 code 命令

1、Command Shift P 调出 输入 shell 选择 安装命令 2、授权 3、使用 % code --version 1.92.1 eaa41d57266683296de7d214f574d0c2652e1fc4 arm64% code ~/.zshrc 伊织 2024-08-21&#xff08;三&#xff09;

6.InnoDB引擎

InnoDB引擎 1.逻辑存储结构2.架构2.1内存架构2.2 磁盘结构 3.事务原理3.1 事务3.2 redo log3.3undo log 4.MVCC4.1MVCC 基本概率14.2 实现原理 1.逻辑存储结构 2.架构 2.1内存架构 2.2 磁盘结构 create tablespace mytest add datafile mytest.idb engineinnodb;后台线程 mys…

Ubuntu 24.04 上安装和配置 Zabbix Agent

Zabbix 是一个强大的开源监控工具&#xff0c;可以帮助您跟踪服务器&#xff0c;网络和应用程序。在主机环境中配置了 Zabbix Server 之后&#xff0c;下一步是添加用于监视的远程主机。Zabbix Agent 从您的服务器收集数据并将其发送到 Zabbix 服务器进行监控。 本指南将向您展…

CUDA-BEVFusion(1): 环境安装

文章目录 1. 查看ubantu配置2. 环境安装2.1 安装包下载2.1.1 tensorRT 下载2.1.2 CUDA 下载2.1.3 cuDNN 下载2.2 安装2.2.1 cuda 安装2.2.2 cuDNN 安装2.2.3 tensorRT安装3. 安装包下载1. 查看ubantu配置 查看GPU的版本sudo apt-get install pciutilslspci | grep VGA查看linux…

Tabby 终端工具推荐

前言:Tabby 是一个现代化的、跨平台的终端模拟器&#xff0c;旨在提供增强的用户体验和定制功能。Tabby&#xff08;以前称为 Terminus&#xff09;被设计为比传统终端更加灵活和美观 Eugeny/tabby: A terminal for a more modern age (github.com)https://github.com/Eugeny/…

网络安全售前入门02——产品了解

目录 1.前言 2.WEB应用防火墙介绍 2.1产品架构功能 2.2应用场景 2.3部署形式 2.4产品价值 2.5选型依据 3.上网行为审计 3.1产品架构功能 3.2应用场景 3.3部署形式 3.4产品价值 3.5选型依据 后续 1.前言 为方便初接触网络安全售前工作的小伙伴了解网安行业情况,我…

BUUCTF PWN wp--ciscn_2019_n_1

第一步 checksec&#xff0c;并检查该题的保护机制 该题的保护机制如下&#xff1a; Arch (架构): amd64-64-little 这意味着这个二进制文件是为64位AMD64架构编译的&#xff0c;使用小端序&#xff08;little-endian&#xff09;。RELRO (重定位-read-only): Partial RELRO P…

工具技巧:如何使用AutoDL算力云

AutoDL算力云可以快速构建编程环境&#xff0c;价格也很实惠 模型运行已知需要显存少&#xff0c;可以考虑选择4090&#xff0c;有24G&#xff0c;具体选择哪种类型&#xff0c;可以看看重点看看这两方面**&#xff1a;数据盘能否扩容&#xff0c;CUDA版本是否够高** 根据自身…

虚拟化技术VirtualBox

虚拟化技术是当今云计算领域中的重要技术之一&#xff0c;而VirtualBox作为一款开源的虚拟化软件&#xff0c;在Linux系统中发挥着重要作用。本文将从VirtualBox的基本概念入手&#xff0c;介绍其在Linux系统中的应用和高级云计算技术&#xff0c;包括其原理、特点、优势、使用…