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

news2025/1/17 17:06:14

系列文章目录

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

💕个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析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.coloradmin.cn/o/1348493.html

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

相关文章

【Android Gradle 插件】Android Plugin DSL Reference 离线文档下载 ( GitHub 下载文档 | 查看文档 )

一、Android Plugin DSL Reference 文档下载 二、Android Plugin DSL Reference 文档查看 一、Android Plugin DSL Reference 文档下载 在之前的博客 【Android Gradle 插件】Android Plugin DSL Reference 文档介绍 ( 1.2 ~ 3.4 版本文档地址 | 4.1 ~ 7.1 版本文档地址 ) 中…

【Spring】AOP的AspectJ开发

AOP基础不了解可以阅读&#xff1a;【Spring】AOP原来如此-CSDN博客 AspectJ是一个居于JAVA开发的AOP框架 基于XML的声明式AspectJ 基于XML的声明式AspectJ是通过XML文件来定义切面&#xff0c;切入点及通知&#xff0c;所有的切面、切入点和通知必须定义在内&#xff0c; 元…

Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射

系列文章 一、逆向工程 Sketchup 逆向工程&#xff08;一&#xff09;破解.skp文件数据结构 Sketchup 逆向工程&#xff08;二&#xff09;分析三维模型数据结构 Sketchup 逆向工程&#xff08;三&#xff09;软件逆向工程从何处入手 Sketchup 逆向工程&#xff08;四&#xf…

NXP实战笔记(二):S32K3xx基于RTD-SDK在S32DS上配置PIT与STM中断并反转IO

目录 1、PIT 1.1、PIT概述 1.2、PIT的配置 1.3、Dio配置 1.4、中断配置 1.5、测试代码 1.6、测试结果 2、STM 2.1、STM概述 2.2、STM的配置 2.3、测试代码 2.4、测试结果 1、PIT 1.1、PIT概述 PIT是一组定时器&#xff0c;可用于引发中断和触发器&#xff0c;包括一…

【AI】人类视觉感知特性与深度学习模型(2/2)

目录 二、人类视觉感知特性对深度学习模型的启发 2.1 视觉关注和掩盖与调节注意力模型的关系 1.视觉关注和掩盖 2. 注意力机制模型 2.2 对比敏感度与U形网络的联系 2.3 非局部约束与点积注意力的联系 续上节 【AI】人类视觉感知特性与深度学习模型&#xff08;1/2&#…

【如何选择Mysql服务器的CPU核数及内存大小】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…

python练习2【题解///考点列出///错题改正】

一、单选题 【文件】 *1.【单选题】 ——文件&#xff1a;读取方法 下列哪个选项可以从文件中读取任意字节的内容&#xff1f;&#xff08;C &#xff09;A A.read() B.readline() C.readlines() D.以上全部 A\B\C三种方法都是可以读取文件中任意的字节内容的&#xff0…

emacs:Searching for program: No such file or directory,sml;

首先&#xff0c;编辑一个现有的或新的 SML 文件&#xff08;如果没有其他方便的方法&#xff0c;可尝试C-x C-f test.smlC-x C-f test.sml 创建一个新文件&#xff09;。你会看到 Emacs 窗口底部的模式显示从 "基本"&#xff08;或其他任何模式&#xff09;变成了 S…

【ArcGIS微课1000例】0084:甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)

甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)。 文章目录 一、成果预览二、实验数据三、符号化四、地图整饰一、成果预览 本实验最终效果图如下所示: 二、实验数据 以下数据可以从本专栏配套的实验数据包中0084.rar中获取。 1. 历史灾害数据。为2005-2020时…

【c++】使用vector存放键值对时,明明给vector的不同键赋了不同的值,但为什么前面键的值会被后面键的值给覆盖掉?

错误描述 运行程序得到结果如下图所示&#xff08;左边是原始数据&#xff0c;xxml文件中真实数据的样子&#xff0c;右图是程序运行得到的结果结果&#xff09;&#xff1a; 对比以上两图可以发现&#xff0c;右图中两个实例的三个属性值都来自左图中的第二个User实例&#x…

思维训练-怎样设计一个MQ

架构师需要做各种设计&#xff0c;要不断地提高自己的设计能力。这有没有方法可以训练呢&#xff1f;有的&#xff0c;就是看到什么、想到什么&#xff0c;就假设对面坐着产品经理&#xff0c;一起讨论怎么把它设计出来。比如怎样设计一个MQ 我&#xff1a;首先我确认一下需求。…

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项样题卷①

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项&#xff08;高职组&#xff09; 样题&#xff08;第1套&#xff09; 目录 2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项&#xff08;高职组&#xff09; 样题&#xff08;第1套&#xff09; 模块一…

系统学习Python——装饰器:函数装饰器-[装饰器状态保持方案:外层作用域和非局部变量]

分类目录&#xff1a;《系统学习Python》总目录 我们在某些情况下可能想要共享全局状态。如果我们真的想要每个函数都有自己的计数器&#xff0c;要么像前面的文章那样使用类&#xff0c;要么使用Python3.X中的闭包函数&#xff08;工厂函数&#xff09;和nonlocal语句。由于这…

【产品设计】表对象建模

随着不断深入的拆解&#xff0c;从产品经理的设计方法到系统的拆解&#xff0c;每一部分都有值得探索的地方。 随着不断深入的拆解&#xff0c;从产品经理的设计方法&#xff0c;到经典系统的拆解&#xff0c;到零代码平台的构建&#xff0c;一直在走系统建设方法路线。如今再看…

SpringBoot解决前后端分离跨域问题:状态码403拒绝访问

最近在写和同学一起做一个前后端分离的项目&#xff0c;今日开始对接口准备进行 登录注册 的时候发现前端在发起请求后&#xff0c;抓包发现后端返回了一个403的错误&#xff0c;解决了很久发现是【跨域问题】&#xff0c;第一次遇到&#xff0c;便作此记录✍ 异常描述 在后端…

【AIGC风格prompt】风格类绘画风格的提示词技巧

风格类绘画风格的提示词展示 主题&#xff1a;首先需要确定绘画的主题&#xff0c;例如动物、自然景观、人物等。 描述&#xff1a;根据主题提供详细的描述&#xff0c;包括颜色、情感、场景等。 绘画细节&#xff1a;描述绘画中的细节&#xff0c;例如表情、纹理、光影等。 场…

设计模式(4)--对象行为(11)--访问者

1. 意图 表示一个作用于某对象结构中的各元素的操作。 使你可以在不改变各元素的类的前提下定义于作用于这些元素的新操作。 2. 五种角色 抽象访问者(Visitor)、具体访问者(Concrete Visitor)、抽象元素(Element)、 具体元素(Concrete Element)、对象结构(ObjectStructure) 3…

回顾2023在CSDN的足迹与2024展望

目录 一、关于博主 二、2023的历程 1、博客分类 2、年度创作数据 3、解锁勋章 4、主要的方向 二、技术感悟 1、技术深入 2、还是实践 三、展望2024 今天是2024年的第一天&#xff0c;告别2023年&#xff0c;让我们以全新的姿态&#xff0c;去迎接新的一年的挑战。2023年…

影视后期:Pr 调色处理之风格调色

写在前面 整理一些影视后期相关学习笔记博文为 Pr 调色处理中风格调色&#xff0c;涉及下面几个Demo 好莱坞电影电影感调色复古港风调色赛博朋克风格调色日系小清晰调色 理解不足小伙伴帮忙指正 简单地说就是害怕向前迈进或者是不想真正地努力。不愿意为了改变自我而牺牲目前所…

Xgboost分类模型的完整示例

往期精彩推荐 数据科学知识库机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归PySpark大数据处理详细教程 定义问题 UCI的蘑菇数据集的主要目的是为了分类任务&#xff0c;特别是区分蘑菇是可食用还是有毒。这个数据集包含了蘑菇的各种特征&#xff0c;如…