【深度解析C++之运算符重载】

系列文章目录

🌈座右铭🌈:人的一生这么长、你凭什么用短短的几年去衡量自己的一生!

💕个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C++,自顶向下看Java-CSDN博客

❤️相关文章❤️:【深度解析C++之this指针】-CSDN博客文章浏览阅读795次,点赞21次,收藏22次。一、为什么需要this指针this指针是C++当中用于指向当前对象的指针,它是成员函数内的一个隐式参数,指向调用该成员函数的对象的内存地址(this指针存放的内容就是对象的内存地址),this指针的主要用途是在成员函数内部访问对象的成员变量和调用其他的成员函数。在类的成员函数当中,如果存在与类的成员变量同名的局部变量或者函数参数,编译器可能无法区分它们,这个时候我们就需要使用this指针可以明确地指出成员变量属于当前对象。https://blog.csdn.net/weixin_59658448/article/details/135136967


目录

系列文章目录

前言

一、运算符重载是什么?

1、基本概念

2、为什么要引入运算符重载

        1、自然语法:

        2、代码简洁性:

        3、类的抽象性:

        4、标准库兼容

 二、运算符重载的特性

1.参数类型:const T&

        1、效率提升

        2、避免修改输入参数

2.返回值类型:T&

        1、效率提升:

        2、支持连续赋值:

3、防御性编程:

         1、为自身赋值

        2、为自身赋值的后果

        1. 资源泄漏: 

        2. 不一致的状态:

        3. 程序崩溃或不稳定:

 4、返回*this:复合连续赋值的含义

        1、*this的作用:

        2、*this与引用返回

三、前置++与后置++

1、前置++

        1、返回类型为引用:

        2、先递增后返回:

        3、推荐使用前置递增:

2、后置++

        1、回类型为值:

        2、使用参数区分前后置:

        3、先返回后递增:

 总结


前言

        这篇文章主要为大家讲解C++当中的运算符重载的问题,学习这篇文章需要对C++的this指针足够的了解,链接我已经为大家放到了文章的开头如果又需要的话请查收。我将全方位地为大家讲解运算符重载,一次性解决各位初学者的所有疑问。


一、运算符重载是什么?

1、基本概念

        当我们谈到运算符重载时,我们实际上是在讨论如何重新定义 C++ 中的某个运算符,使其适用于用户自定义的类或数据类型。运算符重载通过定义特殊的成员函数来实现,这些成员函数以 operator 关键字开头,后接需要重载的运算符符号。

        下面是一个运算符重载函数的通用格式:

返回值类型 operator运算符(参数列表) {
    // 运算符的实现代码
}

        接下来我来用代码为大家演示一下什么叫做运算符重载:

class MyClass {
private:
	double value;
public:
	MyClass(double value) :value(value) {

	}
	MyClass operator + (const MyClass& other)const {
		return MyClass(value + other.value);
	}
	MyClass operator - (const MyClass& other)const {
		return MyClass(value - other.value);
	}
	MyClass operator * (const MyClass& other)const {
		return MyClass(value * other.value);
	}
	MyClass operator / (const MyClass& other)const {
		if (other.value != 0) {
			return MyClass(value / other.value);
		}
		else {
			cerr << "Error: Division by zero:" << endl;
			return MyClass(0);
		}
	}
	MyClass& operator++(){
		++this->value;
		return *this;
	}
	MyClass& operator++(int) {
		MyClass tmp(*this);
		++this->value;
		return tmp;
	}
	double getValue() {
		return this->value;
	}
};
int main() {
	MyClass obj1(2.0);
	MyClass obj2(4.0);
	MyClass obj3 = obj1 + obj2;
	MyClass obj4 = ++obj1;
	cout << obj3.getValue() << endl;
	cout << obj4.getValue() << endl;
	return 0;
}

        再上面的代码当中我们重载了加法运算符、减法运算符等等,通过这样的方式我们定义了让两个对象当中的值相加的过程,运算符重载是C++当中一种灵活的方式,让用户自定义类型的对象能够使用类似于内置类型的语法进行操作。

2、为什么要引入运算符重载

        1、自然语法:

        运算符重载使得用户自定义的类型能够使用类似于内置类型的语法进行操作。例如,通过重载加法运算符,你可以使用object1 + object2的形式进行对象相加,这样的语法更接近我们日常的数学表达方式,使代码更易读。

        2、代码简洁性:

        运算符重载可以简化代码,使其更紧凑而易于理解。通过自定义运算符的行为,你可以隐藏底层实现细节,使代码更具表达力。

        3、类的抽象性:

        运算符重载有助于创建更抽象的类,使其更符合问题领域的模型。例如,通过重载比较运算符,你可以定义自定义类对象之间的比较规则,使得类在各种情境下都能够直观地比较。

        4、标准库兼容

        运算符重载使得用户自定义类型能够与标准库当中的算法和容器协同工作,例如如果你的类支持小于运算符的重载、那么对象就可以用于STL当中的排序算法当中,这就是标准库兼容,虽然运算符重载有着很多的优势,但是如果过度使用也会导致代码的混乱和不容易理解,因此在进行运算符重载的时候建议谨慎选择。


 二、运算符重载的特性

1.参数类型:const T&

        参数类型为 const T& 表示传递的参数是一个对常量类型 T 的引用。这有两个主要好处

        1、效率提升

        避免拷贝构造函数调用:通过使用引用而不是直接传递对象,可以避免不必要的拷贝构造函数的调用。如果使用非引用的方式传递参数,会导致传递的对象被复制一份,调用拷贝构造函数,增加了额外的开销,特别是对于大型对象或者自定义类型来说,这样的开销是不必要的。通过传递引用,可以直接操作原始对象,提高了传参的效率。

        2、避免修改输入参数

        使用 const 修饰:参数类型中的 `const` 关键字确保在函数内部不能修改传递的对象。这是通过将对象声明为常量引用来实现的。如果在函数内部尝试修改这个引用所引用的对象,编译器会报错。这样的设计有助于保护传递的对象不被意外地修改,提高了代码的健壮性和可维护性。

        

void processData(const std::string& input) {
    // 不能修改 input,只能读取其中的数据
    // ...
}

        `processData` 函数接受一个 `std::string` 类型的常量引用作为参数。这确保了在函数内部不能修改传递的字符串,而且通过引用的方式传递参数,也避免了不必要的字符串拷贝。

2.返回值类型:T&

        返回类型为 T& 表示返回的是对类型 T 的引用。这也有两个主要优势:

        1、效率提升:

        避免返回时的拷贝:返回引用而不是对象本身避免了在函数返回时发生不必要的拷贝构造函数调用。如果函数返回对象本身而不是引用,那么在返回时需要创建一个副本,调用拷贝构造函数,这可能会导致性能开销。通过返回引用,可以直接返回原始对象,提高了效率。

        2、支持连续赋值:

        允许链式赋值操作:返回引用允许进行连续赋值操作,例如 `a = b = c`。这是因为返回的是对象的引用,而不是对象本身,所以可以在赋值操作中继续引用相同的对象。这种语法糖提高了代码的简洁性和可读性。

class MyClass {
private:
	int value;
public:
	MyClass(int value) :value(value) {

	}
	MyClass& setValue(int num) {
		this->value += num;
		return *this;
	}
	int getValue() {
		return this->value;
	}
};
int main() {
	MyClass obj1(10);
	obj1.setValue(2).setValue(5).setValue(7);
	cout << obj1.getValue() << endl;
	return 0;
}

        程序的运行结果如下:因为当我们返回一个对象的引用的时候,返回的是一个对象的别名我们就可以继续使用这个对象连续的进行同样的操作,这就叫做对象的链式调用,如果有小伙伴关于this指针这部分的内容有不理解的话,可以看我之前写过的一篇this指针的专题文章,那里面有对于this指针的详细介绍。

3、防御性编程:

         1、为自身赋值

        在赋值操作符重载中,检测自己给自己赋值是为了防止资源泄漏或不一致的状态。通常,你会看到类似以下的代码:

MyClass& MyClass::operator=(const MyClass& other) {
    // 检测是否自己给自己赋值
    if (this != &other) {
        // 执行赋值操作
        // ...
    }
    return *this;
}

        在赋值操作符重载中检测自己给自己赋值是一种防御性编程的做法,目的是避免可能导致资源泄漏或不一致状态的情况发生。如果大家对于这段话不理解的话,我用代码来解释,以下是一个常见的检测自赋值的代码模式:

#include <iostream>

class Example {
public:
    int* data;
    size_t size;

    // 构造函数
    Example(size_t s) : size(s) {
        data = new int[size];
    }

    // 析构函数
    ~Example() {
        delete[] data;
    }

    // 赋值操作符重载
    Example& operator=(const Example& other) {
        // 检测自赋值
        if (this != &other) {
            // 进行赋值操作
            delete[] data;  // 释放原有资源
            size = other.size;
            data = new int[size];
            std::copy(other.data, other.data + size, data);  // 复制数据
        }
        return *this;
    }
};

int main() {
    Example obj1(3);
    Example obj2(5);

    obj1 = obj2;  // 赋值操作

    return 0;
}

        大家尤其注意一下这段代码:

 // 赋值操作符重载
    Example& operator=(const Example& other) {
        // 检测自赋值
        if (this != &other) {
            // 进行赋值操作
            delete[] data;  // 释放原有资源
            size = other.size;
            data = new int[size];
            std::copy(other.data, other.data + size, data);  // 复制数据
        }
        return *this;
    }

        大家可以想象一下如果我在主函数当中进行了这样的操作:

int main() {
	MyClass obj1(20);
	obj1 = obj1;
	return 0;
}

        如果我进行了这样的操作,而且这个对象单中含有data这样的指针成员,我不进行任何的判断就执行了这样的代码:

delete[] data;  // 释放原有资源
            size = other.size;
            data = new int[size];
            std::copy(other.data, other.data + size, data);  // 复制数据

        大家可以想象一下会有什么样的后果,一个对象因为自己给自己赋值然后不分青红皂白地就把自己的数据给删除了,这样会导致非常严重的后果,这个对象的数据会丢失,资源造成泄漏,所以我们在进行运算符重载的时候一定要判断一下,不能够直接删除数据。

        2、为自身赋值的后果

        在上述例子中,`operator=` 被重载以处理 `Example` 类型对象的赋值操作。在赋值之前,首先检测了是否是自赋值,即 `this != &other`。如果是自赋值,就不进行释放和拷贝的操作,以避免释放正在使用的资源,并保持对象的一致性。 这种检测自赋值的做法在处理动态分配的资源(比如堆内存)时尤为重要。如果不进行自赋值检测,可能导致在释放原有资源之前就将其覆盖,从而导致资源泄漏或者出现不一致的状态。通过检测自赋值,可以确保赋值操作的安全性和一致性。 如果在赋值操作符重载中不进行自赋值检测,可能会导致以下危害:

        1. 资源泄漏: 

        假设你有一个包含动态分配内存的类,如果没有检测自赋值并在赋值前释放资源,那么在自赋值的情况下就会导致原有的资源丢失,无法释放,从而发生内存泄漏。

        2. 不一致的状态:

        如果在进行自赋值时不检测,可能会导致对象的状态处于不一致的状态。例如,在拷贝数据之前删除原有数据,这样会导致拷贝时访问无效的内存,导致未定义行为。

        3. 程序崩溃或不稳定:

        不进行自赋值检测可能导致程序崩溃或不稳定的行为。在自赋值情况下,如果不小心释放了正在使用的资源,可能导致悬挂指针或无效内存访问,最终导致程序崩溃。

#include <iostream>

class Example {
public:
    int* data;
    size_t size;

    Example(size_t s) : size(s) {
        data = new int[size];
    }

    ~Example() {
        delete[] data;
    }

    Example& operator=(const Example& other) {
        // 没有自赋值检测
        // 可能导致资源泄漏和不一致的状态
        delete[] data;  // 错误:没有检测自赋值
        size = other.size;
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        return *this;
    }
};

int main() {
    Example obj(3);
    obj = obj;  // 自赋值

    return 0;
}

        代码我为大家放到了这里,有兴趣的话可以自己去验证一下会发生什么。编译虽然不会报错但是程序已经出现了巨大的安全问题。

 4、返回*this:复合连续赋值的含义

        1、*this的作用:

        return *this 的目的是支持连续赋值。在连续赋值中,每个赋值表达式的返回值都是被赋值的对象的引用。例如,a = b = c,首先 b = c 返回 b 的引用,然后 a = b 返回 a 的引用。这种返回自身引用的方式允许多个赋值操作可以串联在一起。

        我不知道大家是否还记得我刚刚在对象的链式调用当中提过这个知识点,代码如下,在实现链式调用的过程当中一方面返回值是引用而且return后面的语句就是*this。

class MyClass {
private:
	int value;
public:
	MyClass(int value) :value(value) {

	}
	MyClass& setValue(int num) {
		this->value += num;
		return *this;
	}
	int getValue() {
		return this->value;
	}
};
int main() {
	MyClass obj1(10);
	obj1.setValue(2).setValue(5).setValue(7);
	cout << obj1.getValue() << endl;
	return 0;
}

        this指针指向的是这个对象本身,可以说是对象在内存当中的地址,那么*this就是对这个对象进行解引用,返回的也就是这个对象本身。和链式调用相互配合。

        2、*this与引用返回

        各位小伙伴这部分的内容非常重要,也困扰了我很长的时间,今天我一次性给大家讲清楚。

  1. 新对象 vs. 原对象:如果返回新创建的对象而不是引用,那么每次调用函数时都会生成一个新的对象。这意味着每次操作都会创建新的对象,而不是在原对象上进行修改。这样的设计可能更适合不希望改变原始对象状态的情况。
  2. 复制成本:返回对象可能涉及到复制构造函数的调用,这可能导致一些额外的开销。如果你的对象比较大或者复制构造函数比较昂贵,这种设计可能会影响性能。
  3. 不支持链式调用:如果不返回引用,就不能支持链式调用,无法在一行代码中连续调用多个该类的成员函数。
     

        如果文字大家还是不能够明白的话请看这样的一段代码:
 

class MyClass {
private:
	int value;
public:
	MyClass(int value) :value(value) {

	}
	MyClass setValue(int num) {
		this->value += num;
		return *this;
	}
	int getValue() {
		return this->value;
	}
};
int main() {
	MyClass obj(5);
	obj.setValue(10);
	cout << obj.getValue() << endl;
	obj.setValue(1);
	cout << obj.getValue() << endl;
	obj.setValue(5).setValue(7).setValue(5);
	cout << obj.getValue() << endl;
	return 0;
}

        代码的运行结果如下:当我不适用引用返回的时候并且这个时候还调用了对象的链式调用是没有用的,因为*this是对象本身,如果不返回引用的话返回的是对象本身,每一次调用函数返回的都是一个新的对象,无法在原来的对象上进行操作,无法实现函数的连续调用也就是对象的链式调用。

        可能到了这里小伙伴会有一个疑问,为什么*this就是对象本身的意思,可是使用引用返回就能够返回对象的引用呢?this指针指向对象,对这个指针解引用应该就是对象本身啊,难道就因为返回值的类型是引用*this就能返回引用吗?

        答案很简单因为编译器做了优化,给*this对象本身临时绑定了一个引用,所以链式调用的时候不管我们执行多少次返回的都是同一个对象的引用,所以可以利用链式调用对同一个对象进行多次重复的操作。


三、前置++与后置++

1、前置++

        重载前置和后置递增运算符 ++ 是面向对象编程中的一项常见任务。这两者之间有一些细微的差异,前置递增运算符也就是 ++i:

T& operator++();  // 返回引用

        1、返回类型为引用:

        前置递增运算符返回引用,允许对同一对象进行连续递增操作,因为返回的是原始对象的引用。前置++是对对象本身的值进行了修改所以返回的也必须是对象本身。

        2、先递增后返回:

        首先对对象进行递增操作,然后返回递增后的对象的引用。

        3、推荐使用前置递增:

        在性能上,前置递增通常比后置递增更高效,因为前置递增直接对原始对象进行操作,而后置递增需要创建一个副本,增加了额外的开销。

2、后置++

        后置++与前置++的区别就是在参数列表当中写一个int,这个int没有任何的意义,就是为了区分前后的。

T operator++(int);  // 参数int用于区分前置和后置递增

        1、回类型为值:

        后置递增运算符返回一个值,而不是引用。这是因为后置递增要返回递增前的原始值,而不是递增后的对象。

        2、使用参数区分前后置:

        后置递增运算符的参数是一个(通常是未使用的)整数,用于在函数签名上区分前置和后置版本。

        3、先返回后递增:

        首先返回递增前的原始值,然后再对对象进行递增操作。

#include <iostream>

class Counter {
private:
    int count;

public:
    Counter() : count(0) {}

    // 前置递增运算符
    Counter& operator++() {
        ++count;
        return *this;
    }

    // 后置递增运算符
    Counter operator++(int) {
        Counter temp(*this);  // 保存递增前的值
        ++count;  // 对对象进行递增
        return temp;  // 返回递增前的值
    }

    int getCount() const {
        return count;
    }
};

int main() {
    Counter c1;
    std::cout << "Original Count: " << c1.getCount() << std::endl;

    // 前置递增
    ++c1;
    std::cout << "After Pre-increment: " << c1.getCount() << std::endl;

    // 后置递增
    Counter c2 = c1++;
    std::cout << "After Post-increment: " << c2.getCount() << std::endl;
    std::cout << "Final Count: " << c1.getCount() << std::endl;

    return 0;
}

        这部分内容简单容易理解,写一段代码为大家演示,这部分内容重点要理解为什么前置++需要引用返回而后置++不需要引用返回,因为前置++是先自增再返回所以这个时候对象本身已经发生了变化,而后置++不同需要先返回自身的值然后再进行自增操作,所以需要引入临时变量,一般我们推荐使用前置++,他的效率更高一些。

        拷贝构造函数当中还有一个很重要的内容就是const成员函数,这个部分我的下一篇文章会为大家详细介绍,const成员不是这篇文章的重点。 


 总结

        这篇文章就为大家介绍到这里,希望我的文章能够帮助到各位小伙伴,今天是2023年的最后一天,这一年发生了很多令我难以忘怀的事情,2023教会了我很多的事情,我的每一份耕耘终于有了让我满意的收获,我现在正满心期待地迎接2024的到来,就用这篇文章致敬2023努力的自己吧!也祝我的每一位粉丝小伙伴新的一年能够诸事顺遂,所得皆所愿,感谢各位的陪伴!😊

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux网络编程学习心得.4

1.epoll工作模式 水平触发 LT 边沿触发 ET 因为设置为水平触发,只要缓存区有数据epoll_wait就会被触发,epoll_wait是一个系统调用,尽量少调用 所以尽量使用边沿触发,边沿出触发数据来一次只触发一次,这个时候要求一次性将数据读完,所以while循环读,读到最后read默认带阻塞…

HarmonyOS应用程序包快速修复

快速修复概述 快速修复是HarmonyOS系统提供给开发者的一种技术手段&#xff0c;支持开发者以远快于应用升级的方式对应用程序包进行缺陷修复。和全量应用升级软件版本相比&#xff0c;快速修复的主要优势在小、快和用户体验好。在较短的时间内不中断正在运行的应用的情况下&am…

【数据结构】第2章线性表(头歌习题)【合集】

文章目录 第1关&#xff1a;实现顺序表各种基本运算的算法任务描述编程要求完整代码 第2关&#xff1a;实现单链表各种基本运算的算法任务描述编程要求完整代码 第3关&#xff1a;移除顺序表中所有值等于x的元素任务描述编程要求完整代码 第4关&#xff1a;逆置顺序表任务描述编…

windows go环境安装 swag

windows 下载依赖包 go get github.com/swaggo/swag/cmd/swag编译swag cd $GOPATH\pkg\mod\github.com\swaggo\swagv1.16.2\cmd\swagps: go env 获取 GOPATH位置 go installps: 此时 $GOPATH\bin下出现了 swag.exe 项目根目录下执行swag 初始化 swag init生成结果

vue中使用echarts实现省市地图绘制,根据数据显示省市天气图标及温度信息

一、实现效果 使用echarts实现省市地图绘制根据数据显示省下市的天气图标根据数据显示省下市的温度信息 二、实现方法 1、安装echarts插件 npm install echarts --save2、获取省市json数据 https://datav.aliyun.com/portal/school/atlas/area_selector 通过 阿里旗下的高…

如何用Python批量计算Word中的算式

一、问题的提出 到了期末&#xff0c;大家都在忙着写总结、改试卷、算工作量&#xff0c;写总结可以借助于ChatGPT&#xff0c;改试卷可以用星火的自动批阅功能&#xff0c;算工作量就是一项比较棘手的问题&#xff0c;因为它涉及很多算式&#xff0c;有时需要老师用计算器算来…

10TB海量JSON数据从OSS迁移至MaxCompute

前提条件 开通MaxCompute。 在DataWorks上完成创建业务流程&#xff0c;本例使用DataWorks简单模式。详情请参见创建业务流程。 将JSON文件重命名为后缀为.txt的文件&#xff0c;并上传至OSS。本文中OSS Bucket地域为华东2&#xff08;上海&#xff09;。示例文件如下。 {&qu…

每日一练(编程题-C/C++)

目录 CSDN每日一练1. 2023/2/27- 一维数组的最大子数组和(类型&#xff1a;数组 难度&#xff1a;中等)2. 2023/4/7 - 小艺照镜子(类型&#xff1a;字符串 难度&#xff1a;困难)3. 2023/4/14 - 最近的回文数(难度&#xff1a;中等)4. 2023/2/1-蛇形矩阵(难度&#xff1a;困难)…

算法基础之最短编辑距离

最短编辑距离 核心思想 &#xff1a; 线性dp 集合定义 &#xff1a; f[i][j]为操作方式的最小值 集合计算 : 三种操作 取最小 ① 删除 : 将a[i]删掉 使ab相同 –> f[i-1][j] 1 f[i][j]② 增添 : 在a[i]后加上一个数 使ab相同 –> f[i][j-1] 1 f[i][j]③ 替换 : 将a[…

基于ssm的航空票务推荐系统的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;航班信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

基于Python的新闻爬取和推荐系统实践

基于Python的新闻爬取和推荐系统实践 项目概述数据集来源技术栈功能特点普通用户功能管理员功能需求 创新点 项目概述 在这个全功能的新闻爬取和推荐系统项目中&#xff0c;我们致力于构建一个高效、智能的平台&#xff0c;为用户提供个性化的新闻阅读体验。采用了Python语言&…

oracle执行不了update

oracle数据库select等其他语句执行正常&#xff0c;update语句执行后一直执行不完&#xff0c;原因是产生了记录锁。 &#xff08;1&#xff09;查询锁 SELECT a.sid, a.serial#,a.USERNAME,ao.OBJECT_NAME FROM v$locked_object lo, dba_objects ao, v$session a WHERE ao.o…

C语言易错知识点十(指针(the final))

❀❀❀ 文章由不准备秃的大伟原创 ❀❀❀ ♪♪♪ 若有转载&#xff0c;请联系博主哦~ ♪♪♪ ❤❤❤ 致力学好编程的宝藏博主&#xff0c;代码兴国&#xff01;❤❤❤ 许久不见&#xff0c;甚是想念&#xff0c;真的是时间时间&#xff0c;你慢些吧&#xff0c;不能再让头发变秃…

电子邮件地址填写指南:格式与常见问题解答

一个专业的电子邮件地址是一个你只用于工作目的的通信帐户。当你给收件人发送电子邮件时&#xff0c;这是他们最先看到的细节之一。无论你的职位或行业如何&#xff0c;拥有一个专业的电子邮件地址都可以提高你和所在公司的可信度。 在本文中我们解释了专业的电子邮件地址是什么…

PAT 乙级 1033 旧键盘打字

旧键盘上坏了几个键&#xff0c;于是在敲一段文字的时候&#xff0c;对应的字符就不会出现。现在给出应该输入的一段文字、以及坏掉的那些键&#xff0c;打出的结果文字会是怎样&#xff1f; 输入格式&#xff1a; 输入在 2 行中分别给出坏掉的那些键、以及应该输入的文字。其…

使用Vue3开发学生管理系统模板1

环境搭建 通过解压之前《Vue3开发后台管理系统模板》的代码&#xff0c;我们能够得到用户增删改查的页面&#xff0c;我们基于用户增删改查的页面做进一步的优化。 创建学生增删改查页面 第一步&#xff1a;复制用户增删改查页面&#xff0c;重命名为StudentCRUD.vue <…

java图书管理系统

主要模块&#xff1a; 为用户开通借书服务增加图书信息登记图书借出信息 技术栈&#xff1a; JSPServletTomcat9.0IDEAMysql 前台登录验证使用框架 数据库脚本包括登录用户名和密码已经写在了数据库脚本.sql 中 解压“需要的jar包”添加到项目的dependency中 运行效果&a…

构建基于小红书笔记详情API的内容生态

随着互联网的发展&#xff0c;内容生态的构建已经成为了许多企业和个人的重要任务。小红书作为一家以内容分享为主的社交平台&#xff0c;其API的开放为开发者提供了一种全新的方式来获取用户生成内容&#xff08;UGC&#xff09;。本文将介绍如何从无到有地构建基于小红书笔记…

告别HTTP,拥抱HTTPS!免费SSL证书领取指南

为什么选择HTTPS&#xff1f; HTTP和HTTPS之间的主要区别在于安全性。HTTP是一种不安全的协议&#xff0c;数据在传输过程中是明文的&#xff0c;容易受到中间人攻击。而HTTPS通过SSL&#xff08;Secure Sockets Layer&#xff09;或TLS&#xff08;Transport Layer Security&…

zabbix通过自动发现-配置监控项、触发器(小白教程)

自动发现配置参考链接&#xff08;不小白&#xff0c;不友好&#xff09; zabbix-get介绍 1配置 zabbix server&#xff1a;版本7&#xff08;不影响&#xff09;,IP地址&#xff1a;192.168.0.60zabbix agent&#xff1a;版本agent1&#xff08;不影响&#xff09;&#xff…