当前位置: 首页 > news >正文

C++之运算符重载系列深入学习:从入门到精通!

为什么需要对运算符进行重载

C++预定义中的运算符的操作对象只局限于基本的内置数据类型,但是对于我们自定义的类型是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算,这个时候就需要我们对这么运算符进行重新定义,赋予其新的功能,以满足自身的需求。比如:

class Complex
{
public:Complex(double real = 0, double image = 0): _real(real), _image(image){}
private:double _real;double _image;
};
void test()
{Complex c1(1, 2), c2(3, 4);Complex c3 = c1 + c2;//编译出错
}

为了使对用户自定义数据类型的数据的操作与内置数据类型的数据的操作形式一致,C++提供了运算符的重载,通过把C++中预定义的运算符重载为类的成员函数或者友元函数,使得对用户的自定义数据类型的数据(对象)的操作形式与C++内部定义的类型的数据一致。

运算符重载的实质就是函数重载函数多态。运算符重载是一种形式的 C++ 多态。目的在于让人能够用同名的函数来完成不同的基本操作。要重载运算符,需要使用被称为运算符函数的特殊函数形式,运算符函数形式:

返回类型 operator 运算符(参数表)
{//...
}

运算符重载的规则

运算符是一种通俗、直观的函数,比如:int x = 2 + 3; 语句中的 “+” 操作符,系统本身就提供了很多个重载版本:

int operator+(int, int);
double operator+(double, double);

但并不是所有的运算符都可以重载。可以重载的运算符有:

在这里插入图片描述

运算符重载还具有以下规则:

1、为了防止用户对标准类型进行运算符重载,C++规定重载的运算符的操作对象必须至少有一个是自定义类型或枚举类型

2、重载运算符之后,其优先级和结合性还是固定不变的。

3、重载不会改变运算符的用法,原来有几个操作数、操作数在左边还是在右边,这些都不会改变。

4、重载运算符函数不能有默认参数,否则就改变了运算符操作数的个数。

5、重载逻辑运算符(&&,||)后,不再具备短路求值特性。

6、不能臆造一个并不存在的运算符,如@、$等

运算符重载的形式

运算符重载的形式有三种:

1、采用普通函数的重载形式

2、采用成员函数的重载形式

3、采用友元函数的重载形式

以普通函数形式重载

在上面的例子中,Complex对象无法执行加法操作,接下来我们重载+运算符。由于之前的定义中Complex的成员都设置成了private成员,所以不能访问,我们需要在类中添加2个get函数,获取其值。

class Complex
{
public:Complex(double real = 0, double image = 0): _real(real), _image(image){}public:double getReal() const{return _real;}double getImage() const{return _image;}private:double _real;double _image;
};Complex operator+(const Complex& lhs, const Complex& rhs)
{return Complex(lhs.getReal() + rhs.getReal(), lhs.getImage() +rhs.getImage());
}void test()
{Complex c1(1, 2), c2(3, 4);Complex c3 = c1 + c2;//此时编译通过
}

以成员函数形式重载

成员函数形式的运算符声明和实现与成员函数类似,首先应当在类定义中声明该运算符,声明的具体形式为:

返回类型 operator 运算符(参数列表);

既可以在类定义的同时定义运算符函数使其成为 inline 型,也可以在类定义之外定义运算符函数,但要使用作用域限定符::,类外定义的基本格式为:

返回类型 类名::operator 运算符(参数列表)
{//...
}

注意:用成员函数重载双目运算符时,左操作数无须用参数输入,而是通过隐含的 this 指针传入。回到 Complex 的例子,如果以成员函数形式进行重载,则不需要定义get函数:

class Complex
{
public://...Complex operator+(const Complex & rhs){return Complex(_real + rhs._real, _image + rhs._image);}
};

以友元函数形式重载

如果以友元函数形式进行重载,同样不需要定义get函数:

class Complex{//...friend Complex operator+(const Complex &lhs, const Complex &rhs);
};Complex operator+(const Complex &lhs, const Complex &rhs){return Complex(lhs._real + rhs._real, lhs._image + rhs._image);
}

运算符重载可以改变运算符内置的语义,如以友元函数形式定义的加操作符:

Complex operator+(const Complex &lhs,const Complex &rhs){return complex(lhs._real - rhs._real, lhs._image - rhs._image);
}

明明是加操作符,但函数内却进行的是减法运算,这是合乎语法规则的,不过却有悖于人们的直觉思维,会引起不必要的混乱。因此,除非有特别的理由,尽量使重载的运算符与其内置的、广为接受的语义保持一致。

特殊运算符的重载

复合赋值运算符

复合赋值运算符推荐以成员函数的形式进行重载,包括这些(+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=),因为对象本身会发生变化。

class Complex
{
public://对于复合赋值运算符,对象本身发生了改变,推荐使用成员函数形式Complex &operator+=(const Complex &rhs){cout << "Complex &operator+=(const Complex &)" << endl;_dreal += rhs._dreal;_dimag += rhs._dimag;return *this;}Complex &operator-=(const Complex &rhs){cout << "Complex &operator-=(const Complex &)" << endl;_dreal -= rhs._dreal;_dimag -= rhs._dimag;return *this;}
};

自增自减运算符

自增运算符++和自减运算符–推荐以成员函数形式重载,分别包含两个版本,即运算符前置形式(如 ++x)和运算符后置形式(如 x++),这两者进行的操作是不一样的。因此,当我们在对这两个运算符进行重载时,就必须区分前置和后置形式。

C++根据参数的个数来区分前置和后置形式。如果按照通常的方法(成员函数不带参数)来重载++/–运算符,那么重载的就是前置版本。要对后置形式进行重载,就必须为重载函数再增加一个int类型的参数,该参数仅仅用来告诉编译器这是一个运算符后置形式,在实际调用时不需要传递实参

class Complex
{
public://...//前置形式Complex& operator++(){++_real;++_image;return *this;}//后置形式Complex operator++(int) //int作为标记,并不传递参数{Complex tmp(*this);++_real;++_image;return tmp;}
};
void test()
{int a = 3;int b = 4;(++a);//表达式的值与a的值,需要进行区分,对于重载前置++与后置++是有一定参考价值的(a++)
}

赋值运算符

对于赋值运算符=,只能以成员函数形式进行重载,我们已经在类和对象中讲过了,就不再赘述,大家可以翻看前面的内容。

函数调用运算符

我们知道,普通函数执行时,有一个特点就是无记忆性,一个普通函数执行完毕,它所在的函数栈空间就会被销毁,所以普通函数执行时的状态信息,是无法保存下来的,这就让它无法应用在那些需要对每次的执行状态信息进行维护的场景。大家知道,我们学习了成员函数以后,有了对象的存在,对象执行某些操作之后,只要对象没有销毁,其状态就是可以保留下来的,但在函数作为参数传递时,会有障碍。为了解决这个问题,C++引入了函数调用运算符。函数调用运算符的重载形式只能是成员函数形式,其形式为:

返回类型 类名::operator()(参数列表){//...
}

在定义 () 运算符的语句中,第一对小括号总是空的,因为它代表着我们定义的运算符名,第二对小括号就是函数参数列表了,它与普通函数的参数列表完全相同。对于其他能够重载的运算符而言,操作数个数都是固定的,但函数调用运算符不同,它的参数是根据需要来确定的, 并不固定。

接下来,我们来看一个例子:

class FunctionObject
{
public:FunctionObject(int count = 0) : _count(count){}void operator()(int x){++_count;cout << " x = " << x << endl;}int operator()(int x, int y){++_count;return x + y;}int _count;//函数对象的状态
};
void test()
{FunctionObject fo;int a = 3, b = 4;fo(a);cout << fo(a, b) << endl;
}

从例子可以看出,一个类如果重载了函数调用operator(),就可以将该类对象作为一个函数使用。对于这种重载了函数调用运算符的类创建的对象,我们称为函数对象(Function Object)。函数也是一种对象,这是泛型思考问题的方式。

下标访问运算符

下标访问运算符 [] 通常用于访问数组元素,它是一个二元运算符,如 arr[ idx ] 可以理解成 arr 是左操作数,idx 是右操作数。对下标访问运算符进行重载时,只能以成员函数形式进行,如果从函数的观点来看,语句 arr[idx] ;可以解释为 arr.operator[] (idx) ;,因此下标访问运算符的重载形式如下:

返回类型 &类名::operator[](参数类型);
返回类型 &类名::operator[](参数类型) const;

下标运算符的重载函数只能有一个参数,不过该参数并没有类型限制,任何类型都可以。如果类中未重载下标访问运算符,编译器将会给出其缺省定义,在其表达对象数组时使用。

class CharArray
{
public:CharArray(size_t size = 10): _size(size), _array(new char[_size]()){}char& operator[](size_t idx){if (idx < _size){return _array[idx];}else{static char nullchar = '\0';return nullchar;}}const char& operator[](int idx) const//针对的是const对象{if (idx < _size){return _array[idx];}else{static char nullchar = '\0';return nullchar;}}~CharArray(){delete[] _array;}
private:size_t _size;char* _array;
};

我们之前使用过的std::string同样也重载了下标访问运算符,这也是为什么它能像数组一样去访问元素的原因。

成员访问运算符

成员访问运算符包括箭头访问运算符->和解引用运算符*,我们先来看箭头运算符->.

箭头运算符只能以成员函数的形式重载,其返回值必须是一个指针或者重载了箭头运算符的对象。来看下例子:

class Data
{
public:int getData() const{return _data;}
private:int _data;
};class MiddleLayer
{
public:MiddleLayer(Data* pdata): _pdata(pdata){}//返回值是一个指针Data* operator->(){return _pdata;}Data& operator*(){return *_pdata;}~MiddleLayer(){delete _data;}
private:Data* _pdata;
};class ThirdLayer
{
public:ThirdLayer(MiddleLayer* ml): _ml(ml){}//返回一个重载了箭头运算符的对象MiddleLayer& operator->(){return *_ml;}~ThirdLayer(){delete _ml;}
private:MiddleLayer* _ml;
};void test()
{MiddleLayer ml(new Data());cout << ml->getData() << endl;cout << (ml.operator->())->getData() << endl;cout << (*ml).getData() << endl;ThirdLayer tl(new MiddleLayer(new Data()));cout << tl->getData() << endl;cout << ((tl.operator->()).operator->())->getData() << endl;
}

输入输出流运算符

在之前的例子中,我们如果想打印一个对象时,常用的方法是通过定义一个 print 成员函数来完成,但使用起来不太方便。我们希望打印一个对象,与打印一个整型数据在形式上没有差别(如下例子),那就必须要重载 << 运算符。

void test()
{int a = 1, b = 2;cout << a << b << endl;Point pt1(1, 2), pt2(3, 4);cout << pt1 << pt2 << endl;
}

从上面的形式能看出,cout 是左操作数,a 或者 pt1 是右操作数,那输入输出流能重载为成员函数形式吗?我们假设是可以的,由于非静态成员函数的第一个参数是隐含的 this 指针,代表当前对象本身,这与其要求是冲突的,因此 >> 和 << 不能重载为成员函数,只能是非成员函数,如果涉及到要对类中私有成员进行访问,还得将非成员函数设置为类的友元函数。

class Point
{
public://...friend ostream& operator<<(ostream& os, const Point& rhs);friend istream& operator>>(istream& is, Point& rhs);
private:int _ix;int _iy;
};ostream& operator<<(ostream& os, const Point& rhs)
{os << "(" << rhs._ix<< "," << rhs._iy<< ")";return os;
}istream& operator>>(istream& is, Point& rhs)
{is >> rhs._ix;is >> rhs._iy;return is;
}

通常来说,重载输出流运算符用得更多一些。同样的,输入流运算符也可以进行重载,如上。

总结

对于运算符重载时采用的形式的建议:

1、所有的一元运算符,建议以成员函数重载

2、运算符 = () [] -> * ,必须以成员函数重载

3、运算符 += -= /= *= %= ^= &= != >>= <<= 建议以成员函数形式重载

4、其它二元运算符,建议以非成员函数重载

类型转换

前面介绍过对普通变量的类型转换,比如说 int 型转换为 long 型,double 型转换为 int 型,接下来我们要讨论下类对象与其他类型的转换。转换的方向有:

1、由其他类型向自定义类型转换

2、由自定义类型向其他类型转换

由其它类型向自定义类型转换

由其他类型向定义类型转换是由构造函数来实现的,只有当类中定义了合适的构造函数时,转换才能通过。这种转换,一般称为隐式转换。下面,我们通过一个例子进行说明:

class Point
{
public:Point(int ix = 0, int iy = 0): _ix(ix), _iy(iy){}//...friend ostream& operator<<(ostream& os, const Point& rhs);
private:int _ix;int _iy;
};
ostream& operator<<(ostream& os, const Point& rhs)
{os << "(" << rhs._ix<< "," << rhs._iy<< ")";return os;
}
void test()
{Point pt = 1;//隐式转换cout << "pt = " << pt << endl;
}

这种隐式转换有时候用起来是挺好的,比如,我们以前学过的std::string,当执行:

std::string s1 = "hello,world";

该语句时,这里其实是有隐式转换的,但该隐式转换的执行很自然,很和谐。而上面把一个 int 型数据直接赋值给一个 Point 对象,看起来就是比较诡异的,难以接受,所以这里我们是不希望发生这样的隐式转换的。那怎么禁止隐式转换呢,比较简单,只需要在相应构造函数前面加上 explicit 关键字就能解决。

由自定义类型向其它类型转换

由自定义类型向其他类型的转换是由类型转换函数完成的,这是一个特殊的成员函数。它的形式如下:

operator 目标类型()
{//...
}

类型转换函数具有以下的特征:

1、必须是成员函数;

2、参数列表中没有参数;

3、没有返回值,但在函数体内必须以return语句返回一个目标类型的变量。

我们来看一个例子:

class Fraction
{
public:Fraction(double numerator, double denominator): _numerator(numerator), _denominator(denominator){}operator double(){return _numerator / _denominator;}operator Point(){return Point(_numerator, _denominator);}
private:double _numerator;double _denominator;
};
void test()
{Fraction f(2, 4);cout << "f = " << f << endl;double x = f + 1.11;cout << "x = " << x << endl;double y = f;
}

补充:类作用域

作用域可以分为类作用域、类名的作用域以及对象的作用域几部分内容。在类中定义的成员变量和成员函数的作用域是整个类,这些名称只有在类中(包含类的定义部分和类外函数实现部分)是可见的,在类外是不可见的,因此,可以在不同类中使用相同的成员名。另外,类作用域意味着不能从外部直接访问类的任何成员,即使该成员的访问权限是 public ,也要通过对象名来调用,对于 static 成员函数,要指定类名来调用。

如果发生 “屏蔽” 现象,类成员的可见域将小于作用域,但此时可借助 this 指针或 “类名::” 形式指明所访问的是类成员,这有些类似于使用 :: 访问全局变量。例如:

#include <iostream>
using std::cout;
using std::endl;int num = 1;namespace test
{int num = 20;class Example{public:void print(int num) const{cout << "形参num = " << num << endl;cout << "数据成员num = " << this->num << endl;cout << "数据成员num = " << Example::num << endl;cout << "命名空间中num = " << test::num << endl;cout << "全局变量num = " << ::num << endl;}private:int num;};
}//end of namespace test

和函数一样,类的定义没有生存期的概念,但类定义有作用域和可见域。使用类名创建对象时,首要的前提是类名可见,类名是否可见取决于类定义的可见域,该可见域同样包含在其作用域中,类本身可被定义在3种作用域内,这也是类定义的作用域。

全局作用域

在函数和其他类定义的外部定义的类称为全局类,绝大多数的 C++ 类是定义在该作用域中,我们在前面定义的所有类都是在全局作用域中,全局类具有全局作用域。

类作用域

一个类可以定义在另一类的定义中,这是所谓嵌套类或者内部类,举例来说,如果类 A 定义在类 B 中,如果 A 的访问权限是 public ,则 A 的作用域可认为和 B 的作用域相同,不同之处在于必须使用 B::A 的形式访问 A 的类名。当然,如果 A 的访问权限是 private ,则只能在类内使用类名创建该类的对象,无法在外部创建 A 类的对象。

class Line
{
public:Line(int x1, int y1, int x2, int y2);void printLine() const;
private:class Point{public:Point(int x = 0, int y = 0): _x(x), _y(y){}void print() const;private:int _x;int _y;};
private:Point _pt1;Point _pt2;
};
Line::Line(int x1, int y1, int x2, int y2): _pt1(x1, y1), _pt2(x2, y2)
{
}
void Line::printLine() const
{_pt1.print();cout << " ---> ";_pt2.print();cout << endl;
}
void Line::Point::print() const
{cout << "(" << _x<< "," << _y<< ")";
}

相关文章:

C++之运算符重载系列深入学习:从入门到精通!

为什么需要对运算符进行重载 C预定义中的运算符的操作对象只局限于基本的内置数据类型&#xff0c;但是对于我们自定义的类型是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算&#xff0c;这个时候就需要我们对这么运算符进行重新定义&#xff0c;赋予其…...

国赛分析。。。。

山东 6散落2两元素 浙江 8散落两元素 安徽不公布 4散落2元素 120s 华南 8散落两元素 西部 8散落两元素 华北 8 2.。。。 华东 东北 路边6张两元素...

无缝融入,即刻智能[一]:Dify-LLM大模型平台,零编码集成嵌入第三方系统,42K+星标见证专属智能方案

无缝融入,即刻智能[一]:Dify-LLM大模型平台,零编码集成嵌入第三方系统,42K+星标见证专属智能方案 1.Dify 简介 1.1 功能情况 Dify,一款引领未来的开源大语言模型(LLM)应用开发平台,革新性地融合了后端即服务(Backend as a Service,BaaS)与LLMOps的精髓,为开发者铺…...

PLSQL导入导出ORACLE数据提示失败问题修改PLSQL配置

oracle中plsql导入提示无法导入问题 1.首先看下是否环境变量已经配置(具体配置看下面环境变量配置) 2.plsql数据导入中tools-->Preferences中配置如下框中的内容 3.设置 tnsnames.ora文件中看下是否设置有问题 4.PLSQL乱码问题 NLS_LANG SIMPLIFIED CHINESE_CHINA.ZHS16…...

从Shift+F6到雪花算法:IDEA开发中的那些坑与解法

在日常开发中&#xff0c;提升代码质量和开发效率是每个开发者追求的目标。作为 Java 开发者常用的集成开发环境&#xff0c;IntelliJ IDEA 提供了丰富的功能与快捷键&#xff0c;帮助我们更快速、更高效地完成日常工作。然而&#xff0c;即便是如此强大的工具&#xff0c;也会…...

Linux知识点总结

学习目标 常见的快捷键 Linux 是一个开源的类 Unix 操作系统&#xff0c;广泛应用于服务器、桌面和嵌入式系统。以下是一些重要的 Linux 知识点总结&#xff1a; 基础概念 文件系统&#xff1a;Linux 使用层次化的文件系统结构&#xff0c;根目录为 /&#xff0c;其他目录如 …...

Gradio 快速开发网页应用

Gradio 是一个开源的 Python 框架&#xff0c;可以快速开发页面&#xff0c;Gradio 主要用于 AI 模型 Demo 的开发&#xff0c;通过几行代码可以快速生成一个 Web Demo&#xff0c;由于 AI 算法工程师使用的都是 Python 语言&#xff0c;使用 Python 开发 Demo 会相对简单&…...

spring使用validation参数及全局异常检测

1.validation参数验证工具 1.1.validation-api技术链 validation-api是一个Java的数据校验规范&#xff0c;它定义了一套用于校验Java Bean的API。它是JSR 303规范的一部分&#xff0c;也被称为Bean Validation。validation-api提供了一系列的注解&#xff0c;用于在Java类的…...

学习笔记 韩顺平 零基础30天学会Java(2024.8.8)

P492 第三代日期使用 P493 第三代日期方法 P495 String翻转 作业代码见chapter12homework 对于需要异常处理的情况&#xff0c;可以通过这种想法得到&#xff1a; P495 注册处理题 P496 字符串统计 P497 String内存布局测试题 P498 常见类阶段梳理 P499 集合介绍 集合的使用并不…...

45.跳跃游戏

&#xff1a;双层for。复杂度n*n n class Solution {public int jump(int[] nums) {// 找到所有的条约方法&#xff0c;返回其中的最小次数// 从后向前&#xff0c;依次记录到最后的次数int n nums.length;if(n 1) return 0;// int[] temp new int[n];// temp[n-1] 0;fo…...

Golang | Leetcode Golang题解之第328题奇偶链表

题目&#xff1a; 题解&#xff1a; func oddEvenList(head *ListNode) *ListNode {if head nil {return head}evenHead : head.Nextodd : headeven : evenHeadfor even ! nil && even.Next ! nil {odd.Next even.Nextodd odd.Nexteven.Next odd.Nexteven even.N…...

【ARM】CMSIS 软件标准接口

目录 CMSIS&#xff1a;Cortex Microcontroller Software Interface Standard1. 概述2. CMSIS-Core2.1 概述2.2 关键组件2.3 示例代码2.4 详细解释 3. CMSIS-DSP3.1 概述3.2 关键组件3.3 示例代码3.4 详细解释 4. CMSIS-RTOS4.1 概述4.2 关键组件4.3 示例代码4.4 详细解释 5. C…...

Qt 小功能:加载等待动画——转圈圈

加载等待动画实现——转圈圈 效果图&#xff1a;&#xff08;看封面最好&#xff09; 关键要点 流畅的动画&#xff1a; 使用 QTimer 每 50 毫秒更新一次动画&#xff0c;确保动画流畅。 视觉效果&#xff1a; 使用 QPainter 的平滑像素转换和抗锯齿选项&#xff0c;提高动画…...

【Linux进程篇】进程终章:POSIX信号量线程池线程安全的单例模式自旋锁读者写者问题

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 前言&#xff1a;在之前的进程间通信时我们就讲到过信号量&#xff0c;他的本质就是一个计数器&#xff0c;用来描述临界资源的一个计数器。我们当时使用电影院的例子来说明信号量。电影院的座位被我们称为临界资源&a…...

MathType7.5破解版下载安装激活图文详细教程(附激活秘钥)

&#x1f31f; 引言&#xff1a;揭秘MathType&#xff0c;数学编辑的瑞士军刀&#xff01; 嘿&#xff0c;各位小伙伴&#xff0c;今天我要给你们安利一个我超级喜欢的数学神器——MathType&#xff01;如果你跟我一样&#xff0c;在处理数学公式时常常感到头疼&#xff0c;那你…...

2-62 基于MATLAB gui 编制短波通信系统

基于MATLAB gui 编制短波通信系统&#xff0c;录制一段语音信号&#xff0c;分别通过AM SSB DSB 等调制信号&#xff0c;加入噪声&#xff0c;然后解调出来&#xff0c;可比较各种调制解调方式的优劣。程序已调通&#xff0c;可直接运行。 2-62 matlab gui - 小红书 (xiaohongs…...

windows C++-C++/WinRT 中创建组件和事件(下)

跨 ABI 的简单信号 如果无需连同事件传递任何形参或实参&#xff0c;则可以定义自己的简单 Windows 运行时委托类型。 以下示例展示 Thermometer 运行时类的更简易版本。 它声明名为 SignalDelegate 的委托类型&#xff0c;然后使用该类型来引发信号类型事件&#xff0c;而不是…...

C++初学者指南-5.标准库(第二部分)--二叉堆操作

C初学者指南-5.标准库(第二部分)–二叉堆操作 文章目录 C初学者指南-5.标准库(第二部分)--二叉堆操作背景什么是“堆”二叉最大堆二叉树的表示 堆操作C标准库中的堆初始化堆收缩堆增长堆 辅助操作sort_heap (Heap → Sorted Array)is_heapis_heap_until 相关内容 不熟悉 C 的标…...

在Ubuntu 16.04上安装Git的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 在现代软件开发中&#xff0c;一个不可或缺的工具是某种版本控制系统。版本控制系统允许您在源代码级别跟踪软件。您可以跟踪更改…...

redis内存淘汰策略-------Reservoir Sampling(水库采样)

文章目录 过期删除策略和内存淘汰策略内存淘汰策略evictionPoolEntryevictionPoolPopulate Reservoir SamplingdictGetRandomKeydictGetSomeKeysReservoir Samplingchatgpt对Reservoir Sampling的介绍 过期删除策略和内存淘汰策略 详细介绍请参考博客“redis过期删除策略和内存…...

C++《类和对象》(上)

在之前的C入门基础知识中我们了解了C的发展过程已经重要性&#xff0c;还初步了解了C中一些相比C语言特有的知识点&#xff0c;例如命名空间、缺少参数、函数重载、引用等&#xff0c;接下来在本篇中我们将开始C整个体系中非常重要的一个知识章节——类和对象&#xff0c;类和对…...

LLM大语言模型算法特训

百度 LLM&#xff08;Large Language Model&#xff09;大语言模型算法特训是一个深度学习领域的高级培训项目&#xff0c;专门设计用于训练和优化大规模语言模型的开发者和研究人员。本文将详细探讨LLM算法的基本原理、训练技术、应用领域以及参与者可以预期的学习收获和挑战。…...

Docker相关笔记

Docker笔记 1. Dockerfile编译构建docker Dockerfile 是一个文本文件&#xff0c;包含了构建 Docker 镜像的所有指令。 Dockerfile 常用的有如下关键字&#xff1a; FROM&#xff1a;指定基础镜像&#xff0c;后续定制操作都是基于这个基础镜像&#xff0c;比如&#xff1a; …...

前端技术day01-HTML入门

一、前端介绍 技术描述HTML用于构建网站的基础结构的CSS用于美化页面的&#xff0c;作用和化妆或者整容作用一样JS实现网页和用户的交互Vue主要用于将数据填充到html页面上的Element主要提供了一些非常美观的组件 二、工具软件 VsCode 在前端领域&#xff0c;有一个公认好用…...

Multisim 用LM358 运放模拟线性稳压器 - 运放输出饱和 - 前馈电容

就是拿运放搭一个可调的LDO 稳压器&#xff0c;类似下面这个功能框图里的感觉。本来应该非常简单&#xff0c;没什么好说的&#xff0c;没想到遇到了两个问题。 原理 - 理想运放 我用PNP 三极管Q2 作为输出&#xff0c;运放输出电压升高时&#xff0c;流过PNP 三极管BE 的电流变…...

宁德大屏第二版总结

碰到难点 1.wss 心跳机制 实现前端和后端双向绑定 只要后端发送了消息 前端通过全局总线去触发你想要的函数。 全局总线 vue3可以全局总线下一个mitt 新建一个eventBus.js import mitt from "mitt"; const eventBus mitt();export default eventBus; 然后wss…...

冥想第一千二百四十七天(1247)

1.今天上午带桐桐去游泳了&#xff0c;买了卡吉诺&#xff0c;吃过最好吃的甜点。推荐。还有鸡排。 2.回来后带着媳妇&#xff0c;先加油。去给丈母娘看腿&#xff0c;等丈母娘等了好久&#xff0c;还帮她推车。 3.回来后&#xff0c;在丈母娘家跑步。很舒服。家长麦田的香味。…...

基于光学动捕定位下的Unity-VR手柄交互

Unity VR 场景手柄交互实现方案 需求 在已创建好的 Unity VR 场景中&#xff0c;接入游戏手柄&#xff0c;通过结合动捕系统与 VRPN&#xff0c;建立刚体&#xff0c;实时系统获取到手柄的定位数据与按键数据&#xff0c;通过编写代码实现手柄的交互逻辑&#xff0c;实现手柄…...

php json_decode 带反斜杠字符串json解析

PHP json_decode 带反斜杠字符串json解析 今天再次遇到了json字符串中包含反斜杠的问题&#xff0c;记录下解决方法 在JSON字符串中&#xff0c;反斜杠\用作转义字符。当JSON_UNESCAPED_SLASHES选项被用于json_encode()函数时&#xff0c;不会在slashes前面添加反斜杠。 但是…...

【NLP】文本张量表示方法【word2vec、词嵌入】

文章目录 1、文本张量表示2、one-hot词向量表示2.1、one-hot编码代码实现&#xff1a;2.2、onehot编码器的使用2.3、one-hot编码的优劣势 3、word2vec模型3.1、模型介绍3.2、CBOW模式3.3、skipgram模式3.4、word2vec的训练和使用3.4.1、获取训练数据3.4.2、训练词向量3.4.3、查…...