【C++】揭开C++类与对象的神秘面纱(首卷)(类的基础操作详解、实例化艺术及this指针的深究)

在这里插入图片描述

文章目录

  • 一、类的定义
    • 1.类定义格式
    • 2.类访问限定符
    • 3.类域
  • 二、类的实例化
    • 1.实例化概念
    • 2.对象的大小
  • 三、隐藏的this指针与相关练习
    • 1.this指针的引入与介绍
    • 练习1
    • 练习2
    • 练习3

一、类的定义

1.类定义格式

   在讲解类的作用之前,我们来看看类是如何定义的,在C++中,class就是定义类的关键字,类的定义和C语言中结构体的定义类似,class后面跟类名,然后用一段大括号来定义类,收尾的大括号后要加分号,我们将struct结构体和class类放在一起作对比,如下:

//结构体的定义
struct stack
{int* arr;int size;int capacity;
};//要以分号结尾//类的定义
class stack
{int* _arr;int _size;int _capacity;
};//要以分号结尾

   上面就是结构体和类定义的对比,可以看到类和结构体的定义几乎一模一样,因为class类本身就是为了修正C语言中结构体的不足而创造,所以定义和结构体几乎一样,那么接下来我们就来说说类的特点、以及C++和C语言结构体、C++结构体和C++类的区别

   class为定义类的关键字,Stack为类的名字,{ }中为类的主体,注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中可以定义函数,这些函数称为类的⽅法或者成员函数

   为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_或者m开头,比如上面的例子中,我在类中的定义就使用 _ 来定义变量,这是为了区分成员变量,现在可能感受不到,慢慢学习就懂了,但是注意C++中这个并不是强制的,只是⼀些惯例,具体看公司的要求

   C++中的struct兼容C语言中struct的⽤法,同时C++将struct升级成了类,其中比较明显的变化是struct中可以定义函数,它们的区别就在于默认情况下的类域访问权限,这个在后面的访问限定符部分再讲,虽然struct也可以定义类,但是⼀般情况下我们还是推荐⽤class定义类

   定义在类中的成员函数默认为内联函数,相当于普通函数前面加了inline关键字,当然,成员函数是否在原地展开依然是由编译器决定,接下来我们还是来见识见识类中成员函数的定义,如下:

class stack
{
//这是类访问限定符,可以暂时不管
public://假设返回有效数据个数//定义一个成员函数size来解决,它默认为内联函数int size(){return _size;}private:int* _arr;int _size;int _capacity;
};

2.类访问限定符

   C++类的访问限定符有三个,分别是public、private、protected,public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是⼀样的,以后继承章节才能体现出他们的区别,现在我们就暂时不区分protected和private

   我们看看之前的代码:

class stack
{//这是类访问限定符public,被它修饰的成员变量或函数//将可以被外界直接访问public:int size(){return _size;}//这时类访问限定符private,被它修饰的成员变量或函数//将不能直接被外界直接访问private:int* _arr;int _size;int _capacity;
};

   在上面的代码中,我们创建了一个stack类,里面的成员函数size被public修饰,可以直接在外部对它进行访问,而里面的数据则是使用private修饰,外部不能直接访问,我们来测试一下:

int main()
{stack st;//调用类中的成员函数的方法就是//类名.函数名,这里size没有参数也就不用传参cout << st.size() << endl;return 0;
}

   在这段代码中,我们通过C++的方式去访问了类中的成员函数size,这里的size是由public限定符修饰的,我们来看看代码能不能正常运行,如下:

在这里插入图片描述

   接着我们再来测试一下下面这一段代码,这段代码中我们就不通过size函数来访问size,而是直接访问stack的内部成员_size,注意_size是被private修饰的

int main()
{stack st;//这里就是我们不使用size这个成员函数//而是直接去访问里面的成员_sizecout << st._size << endl;return 0;
}

   我们来看看这段代码的运行结果,看看能否成功运行,如下:

在这里插入图片描述

   可以看到代码出错了,报错为:无法访问 private 成员,由上面两段代码的实验我们就知道了,由public修饰的成员变量或成员函数可以被外部直接访问,被private修饰的成员变量或成员函数不能被外部直接访问,pretected同private,这里就不再举例,它们两个只有在继承章节我们才能说明白它们的区别

   那么我们一般怎么使用类访问限定符呢?简单来讲就是只要是需要我们保护的数据,都要用private修饰,比如上面我们stack类中的三个成员变量,如果它们使用public修饰,那么别人就可以从外部轻易更改我们的数据,这显然是不能接受的,所以我们用private来修饰它们

   此时如果我们想要得到stack中的size,也就是有效数据个数,我们就可以写一个成员函数size,然后用public修饰,这样我们就确保了使用者不能直接更改size的大小,但是可以通过成员函数得到size的大小

   最后我们来简单补充 + 总结一下类访问限定符,把一些简单的知识放在这里讲解,重要的内容我们已经在上面讲过了:

    C++⼀种实现封装的⽅式,⽤类将对象的属性与⽅法结合在⼀块,让对象更加完善,通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤

    public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是⼀样的,在继承才能看出区别

   访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到}即类结束

   如果使用class定义类,并且内部的成员变量和成员函数都没有被访问限定符修饰时,它的默认限制规则为私有private,而如果使用struc定义类,并且内部的成员变量和成员函数都没有被访问限定符修饰时,它的默认限制规则为公有public

   ⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public,上面stack类中的size就是最好的例子,想要底层数据不被轻易修改,又要确保使用者能正常使用,就需要将成员变量设置为私有private或protected保护,而成员函数则设置为公有public

   类中的成员函数可以直接访问类中的成员变量,不受类访问限定符的影响,所以类访问限定符的限定是针对类外部的访问的

3.类域

   类定义了⼀个新的作⽤域,和命名空间有点类似,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤域访问限定符::,作⽤域操作符指明成员属于哪个类域,接下来我们就举一个声明和定义分离的例子来说明

class stack
{
public://声明和定义分离,这里是声明(使用了之前学过的缺省参数)void Init(int n = 4);private:int* _arr;int _size;int _capacity;
};//对初始化方法进行定义,要指定类域
//指定方式为:类名::函数名
void stack::Init(int n)
{_arr = (int*)malloc(n * sizeof(int));if (_arr == NULL){perror("malloc");return;}_size = 0;_capacity = n;
}

   在上面的示例中,我们将类的初始化成员函数定义在了类的外面,甚至是另一个文件中,此时如果我们想要正确的定义它,就必须在定义的时候指定类域,指定方式为类名::函数名,我们来看看程序运行结果:
在这里插入图片描述
那么这时为什么呢?这是因为类域影响的是编译的查找规则,程序中Init如果不指定类域stack,那么编译器就把Init当成全局函数,那么编译时,找不到_arr等成员的声明/定义在哪⾥,就会报错

   当我们指定类域stack之后,编译器就是知道Init是stack的成员函数,首先在当前的全局域找_arr等成员变量,如果找不到就会到类域中去查找,接下来我们测试一下如果不指定类域来定义函数会怎么样,如下:

void Init(int n)
{_arr = (int*)malloc(n * sizeof(int));if (_arr == NULL){perror("malloc");return;}_size = 0;_capacity = n;
}

   我们来看看代码的运行结果:

在这里插入图片描述

   可以看到VS也是直接报错了,没有找到对应的那些变量,归根结底就是类域影响了编译器的查找逻辑,类似域命名空间的作用,在没有指定类域的情况下只会在当前域进行查找,没有找到就会报错,而指定类域后,会先在当前域进行查找,找不到就会去指定的类域中进行查找,才能保证程序的正确性

二、类的实例化

1.实例化概念

   ⽤类类型在物理内存中创建对象的过程,称为类实例化出对象,说通俗一点就是使用我们写好的类创建一个类对象,如下:

//使用stack类创建一个对象的过程就叫类的实例化
stack st;

   类是对象进⾏⼀种抽象的描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间,相当于类是一种模板一样的东西,当使用类创建对象时,编译器就会照着类的样子去内存中创建这个对象,这就叫类的实例化,但是如果光有一个类,而不用它创建对象,这个类本身,也就是这个模板本身是不会占据空间的

   ⼀个类可以实例化出多个对象,实例化出的对象占⽤实际的物理空间,存储类成员变量,打个⽐⽅:类实例化出对象就像现实中使⽤建筑设计图建造出房⼦,类就像是设计图,设计图规划了有多少个房间,房间⼤⼩功能等,但是并没有实体的建筑存在,也不能住⼈,⽤设计图修建出房⼦,房⼦才能住⼈。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据
在这里插入图片描述

2.对象的大小

   首先我们分析一下一个类包含哪些成员,一种是成员函数,一种是成员变量,其中成员变量肯定是要存储在对象中的,那么成员函数是否需要存储在对象中呢?其实是不需要的,成员函数和普通函数一样,存放在内存的代码段中,当我们调用成员函数时会直接通过它的地址对它进行调用,不需要存放在某一个对象中

   具体原理就是:当编译链接时就已经找到了成员函数的地址,保存在了符号表中,当调用成员函数时,只需要去符号表中找到对应成员函数的地址,然后在调用成员函数的位置形成call(地址)语句,在程序运行时就通过call语句调用成员函数,而只有动态多态是在运⾏时找,我们在多态部分会讲到,动态多态就需要存储函数地址

   那么话又说回来了,既然对象中只需要存储成员变量而不需要存储成员函数,那么它又是怎样存储成员变量的呢?是不是直接从第一个成员变量开始,依次紧挨着存储呢?答案是否定的,对象存储成员变量时需要遵循 “内存对齐” 的规则

   至于内存对齐我在C语言的结构体篇章中已经详细举了例子并且画图讲解,这里只再罗列一下内存对齐的规则(和C语言结构体只有一点不同,写在下方规则的第5条了),没有接触过内存对齐的小伙伴可以看这篇文章:【C语言】自定义类型:结构体

内存对齐规则:
   1. 第⼀个成员在与结构体偏移量为0的地址处,其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
   2. 对⻬数=编译器默认的⼀个对⻬数与该成员⼤⼩的较⼩值,VS中默认的对⻬数为8
   3. 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍
   4. 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍
   5. 如果类中没有任何成员变量,那么这个类实例化出来的对象会占1字节的空间,这是为了证明它存在,如果为0字节,那么这个对象就不存在了,而C语言中的结构体则是直接规定不能创建空的结构体

   这里我们再做三道练习题,如下:

// 计算⼀下A / B / C实例化的对象是多⼤?
class A
{
public:void Print(){cout << _ch << endl;}
private:char _ch;int _i;
};class B
{
public:void Print(){//...}
};class C
{};
int main()
{A a;B b;C c;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl;return 0;
}

   有了之前学习结构体的知识,在加上补充的第5条,这道题就很简单了,首先成员函数不占空间,所以不需要管,然后由于内存对齐,a对象的大小为8字节,而b和c对象都没有成员变量,所以大小为1字节,这是为了标识它们是一个存在的对象,我们来看看代码运行结果:

在这里插入图片描述

三、隐藏的this指针与相关练习

1.this指针的引入与介绍

   为了讲明白this指针,这里我们引入一个代码场景,我们要写一个日期类帮我们进行日期的运算,现在我们要写一个日期类的初始化函数,如下:

#include <iostream>
#include <algorithm>
using namespace std;class Date
{
public:void Init(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2;d1.Init(2025, 1, 1);d2.Init(2025, 1, 6);d1.Print();d2.Print();return 0;
}

   大家好好观察一下代码,接着我们不管三七二十一,直接运行一下代码,等一下我们通过提问的方式来引导我们的学习

在这里插入图片描述

   可以看到,我们使用Date类实例化出来了两个对象,我们分别对它们进行了初始化和打印,看起来代码没有问题呀,但是我们现在需要了解的一个点是:Print()是日期类的成员函数,我们在上面类的实例化那里也讲过,类的成员函数和普通函数在存储的本质上差不多,都存放在代码段,并且都使用call(地址)的形式调用函数

   那么既然成员函数和普通函数差不多,为什么我们使用d1调用Init和Print函数时,编译器能知道这是d1在调用这些函数,进行初始化和打印都是针对d1进行,d2也是同理,编译器也知道是d2在调用这些函数,但是传参时也没有传能够区分不同对象的参数,那当d1和d2调⽤Init和Print函数时,成员函数是如何知道应该访问的是d1对象还是d2对象呢?

   这就是成员函数和普通函数最大的区别,虽然它们的存储方式类似,但是成员函数在调用的时候更加特殊,它会在传递参数的时候悄悄地把当前对象的指针传过去,并且它是成员函数的第一个形参,比如用上面的Print成员函数的调用举一个例子,如下:

//这句函数调用经过编译器处理
//其实本质上是d1.Print(&d1)
d1.Print();
//这句函数调用经过编译器处理
//其实本质上是d2.Print(&d2)
d2.Print();

   在上面的代码注释中,我写出了Print成员函数传参时的原本样子,这是C++规定的语法,不需要我们自己手动进行传参,编译器会自动帮我们进行传参,然后在调用成员函数时,编译器也会自动帮我们在参数列表加上一个当前类类型的this指针,如下:

//这个成员函数经过编译器处理,实际上为
//void Print(Date* const this)
void Print()

   注意这个this指针的类型,是当前类类型的右const指针,也就是this指针的内容可以被改变,但是本身的地址不能被修改,随后就是如果在函数体内使用了当前类的成员变量,它们的前面都会被编译器加上this指针进行访问,如下:

void Print()
{
//下面的语句本质上是:
//cout << this->_year << "-" << this->_month << "-" << this->_day << endl;cout << _year << "-" << _month << "-" << _day << endl;
}

   可以看到,编译器在看到出现当前类的成员变量时,就会悄悄地在成员变量前加上this->,这样我们就能确保,当d1来调用Print函数时,Print函数拿到了对应的对象的地址,也就是this指针,然后通过d1的this指针访问d1的成员变量,最终实现了使用和修改d1的成员变量

这里我们总结一下编译器悄悄为我们做了哪些事:
   1. 编译器在传参时,悄悄地将当前对象的地址作为第一个参数传给成员函数
   2. 成员函数在接收参数时,编译器也会在第一个参数的位置放一个当前>   类类型的this指针,让它来接收传来的对象的地址,注意这个this指针是右const修饰的指针
   3. 当接收到对象的this指针后,编译器就会在使用了成员变量的地方加上this->,这样就可以成功访问到当前对象的对应成员变量,然后对它们进行使用和修改

   所以根据上面的总结我们才发现,一直是编译器在替我们负重前行,悄悄为我们做了很多事,让我们在使用成员函数时做到,用哪个对象调用成员函数,成员函数就对哪个对象进行处理,接下来我们就补充几个注意事项:
   1. 在传参时,我们不要自己去显示地,也就是手动去传当前对象的地址,直接传参会报错,既然编译器帮我们做了这些事,我们就不要再去画蛇添足
   2. 在函数的形参那里,不能显示地去写一个this指针来接收,编译器会直接报错
   3. 在成员函数中可以直接显示地使用this指针,比如_year我们可以写成this->_year,也就是这里的this可写可不写,初学者建议这样写两周熟悉过程,然后就可以将它省略掉,当然它可以显示使用的主要价值不在这上面,在我们后续文章中才会体现它的重要性

   那么接下来我们来做几道题熟悉熟悉,希望大家能够尽可能地思考这些问题

练习1

1.下⾯程序编译运⾏结果是()A、编译报错 B、运⾏崩溃 C、正常运⾏#include<iostream>
using namespace std;class A{public:void Print(){cout << "A::Print()" << endl;}private:int _a;};int main(){A* p = nullptr;p->Print();return 0;}

   希望大家先思考再来看这里的解析,首先我们要排除的第一个答案就是A,因为编译报错其实是语法有问题,这里的语法显然没有问题,虽然p是一个空指针,但是它的类型依然是A*的,可以调用Print函数,没有语法错误

   随后我们应该排除的是B选项,因为在这道题中只有出现空指针解引用才可能让程序运行崩溃,但是我们在调用Print函数时,只会将当前对象的地址传过去,也就是空指针传过去,但是不存在解引用,这里的p->并不是解引用,只是调用成员函数的一种语法规则,并且在函数体中没有使用成员变量,也不会存在解引用,所以这个题不会出现运行错误

   所以这道题最终答案是C,这段程序会正常运行,不会有任何问题,是不是比较吃惊,我们来运行代码看看是否如我们上面分析的一样,如下:

在这里插入图片描述

   可以看到代码确实没有任何问题,接下来我们来看下一道题:

练习2

2.下⾯程序编译运⾏结果是()A、编译报错 B、运⾏崩溃 C、正常运⾏#include<iostream>
using namespace std;class A{public:void Print(){cout << "A::Print()" << endl;cout << _a << endl;}private:int _a;};int main(){A* p = nullptr;p->Print();return 0;}

   可以看到这道题和上面的那道题很相似,只有Print函数中有一个不同,就是在函数中打印了成员变量_a,那么这道题又该怎么做呢?

   首先我们还是排查A选项,因为代码中并没有出现语法错误,随后就是分析B选项,在这道题中看看有没有出现空指针的解引用,很明显出现了,就是在Print函数中打印了成员变量_a,我们在前面就讲过,在编译的时候编译器会在成员变量的前面加上this->来访问当前对象的成员变量,而这里的this是空指针,所以导致了空指针解引用,最终导致运行崩溃,所以这道题的答案是B,最后我们来看看代码调试结果:

在这里插入图片描述

   可以看到代码确实因为空指针解引用而运行报错了,然后我们来看最后一道题,如下:

练习3

 3. this指针存在内存哪个区域的()A. 栈 B.堆 C.静态区 D.常量区 E.对象⾥⾯

   这道题相对于来说就简单多了, 只要记住我们刚刚说的this指针是怎么来的就好了,this指针是编译器帮我们加上的一个形参,所以它应该存放在内存的栈区,栈区是专门存放函数栈帧、函数形参以及函数中的局部变量的,所以这道题选A

   那么今天关于类和对象首卷的内容就到这里结束了,这篇文章相当于给我们下一篇文章做知识预备,在下一篇文章我们就可以真正见识到一点C++的真正难度了,敬请期待吧!
   bye~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端JavaScript中some方法的运用

一&#xff0e;前言 在我们的日常工作中&#xff0c;有时候仅仅需要找到某个数组中的值&#xff0c;就可以返还结果的话&#xff0c;笔者建议就可以使用some方法&#xff0c;这比遍历整个数组高效一些。 二&#xff0e;应用 首先&#xff0c;看官方定义&#xff1a;JavaScri…

安装vue脚手架出现的一系列问题

安装vue脚手架出现的一系列问题 前言使用 npm 安装 vue/cli2.权限问题及解决方法一&#xff1a;可以使用管理员权限进行安装。方法二&#xff1a;更改npm全局安装路径 前言 由于已有较长时间未进行 vue 项目开发&#xff0c;今日着手准备开发一个新的 vue 项目时&#xff0c;在…

基于Python实现的通用小规模搜索引擎

基于Python实现的通用小规模搜索引擎 1.项目简介 1.1背景 《信息内容安全》网络信息内容获取技术课程项目设计 一个至少能支持10个以上网站的爬虫程序&#xff0c;且支持增量式数据采集;并至少采集10000个实际网页;针对采集回来的网页内容&#xff0c; 能够实现网页文本的分…

鸿蒙面试 2025-01-10

写了鉴权工具&#xff0c;你在项目中申请了那些权限&#xff1f;&#xff08;常用权限&#xff09; 位置权限 &#xff1a; ohos.permission.LOCATION_IN_BACKGROUND&#xff1a;允许应用在后台访问位置信息。 ohos.permission.LOCATION&#xff1a;允许应用访问精确的位置信息…

【硬件测试】基于FPGA的BPSK+帧同步系统开发与硬件片内测试,包含高斯信道,误码统计,可设置SNR

目录 1.硬件片内测试效果 2.算法涉及理论知识概要 2.1 bpsk 2.2 帧同步 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.硬件片内测试效果 本文是之前写的文章 《基于FPGA的BPSK帧同步系统verilog开发,包含testbench,高斯信道,误…

MySQL 视图 存储过程与存储函数

第十四章_视图、第十五章 _存储过程与存储函数 1.常见的数据库对象 1. 表&#xff08;Table&#xff09; 用于存储结构化数据的基本对象&#xff0c;由行&#xff08;记录&#xff09;和列&#xff08;字段&#xff09;组成。 2. 视图&#xff08;View&#xff09; 基于一…

Chrome_60.0.3112.113_x64 单文件版 下载

单文件&#xff0c;免安装&#xff0c;直接用~ Google Chrome, 免費下載. Google Chrome 60.0.3112.113: Chrome 是 Google 開發的網路瀏覽器。它的特點是速度快,功能多。 下载地址: https://blog.s3.sh.cn/thread-150-1-1.htmlhttps://blog.s3.sh.cn/thread-150-1-1.html

CTFshow—文件包含

Web78-81 Web78 这题是最基础的文件包含&#xff0c;直接?fileflag.php是不行的&#xff0c;不知道为啥&#xff0c;直接用下面我们之前在命令执行讲过的payload即可。 ?filephp://filter/readconvert.base64-encode/resourceflag.php Web79 这题是过滤了php&#xff0c;…

python学opencv|读取图像(二十九)使用cv2.getRotationMatrix2D()函数旋转缩放图像

【1】引言 前序已经学习了如何平移图像&#xff0c;相关文章链接为&#xff1a; python学opencv|读取图像&#xff08;二十七&#xff09;使用cv2.warpAffine&#xff08;&#xff09;函数平移图像-CSDN博客 在此基础上&#xff0c;我们尝试旋转图像的同时缩放图像。 【2】…

24下半年软考「单独划线」合格标准已公布!

2024年下半年计算机技术与软件专业技术资格考试单独划线地区合格标准已公布&#xff01; 其中初级和中级单独划线地区合格标准各科目均为39分&#xff0c;高级各科目为40分&#xff0c;符合单独划线地区的同学可以去申请证书了。 一、证书效力 在单独划线地区报名参加相关职业…

Linux第一课:c语言 学习记录day06

四、数组 冒泡排序 两两比较&#xff0c;第 j 个和 j1 个比较 int a[5] {5, 4, 3, 2, 1}; 第一轮&#xff1a;i 0 n&#xff1a;n个数&#xff0c;比较 n-1-i 次 4 5 3 2 1 // 第一次比较 j 0 4 3 5 2 1 // 第二次比较 j 1 4 3 2 5 1 // 第三次比较 j 2 4 3 2 1 5 // …

前端用json-server来Mock后端返回的数据处理

<html><body><div class"login-container"><h2>登录</h2><div class"login-form"><div class"form-group"><input type"text" id"username" placeholder"请输入用户名&q…

是德科技M9010A PXIe 机箱+M9037A模块,台式应用的理想之选

Keysigh是德科技M9010A PXIe 机箱M9037A模块 M9010A PXIe 10 插槽 Gen 3 机箱提供***的灵活性、兼容性和性能&#xff0c;而且外形更小巧&#xff0c;是台式应用的理想之选。它拥有 8 个 PXIe 混合插槽&#xff0c;使系统设计人员能够更灵活地混合和搭配 PXIe 和混合兼容模块的…

【算法刷题】leetcode hot 100 滑动窗口

文章目录 3. 无重复字符的最长子串438. 找到字符串中所有字母异位词总结 3. 无重复字符的最长子串 leetcode&#xff1a;https://leetcode.cn/problems/longest-substring-without-repeating-characters/?envTypestudy-plan-v2&envIdtop-100-liked 滑动窗口 &#xff0…

企业级PHP异步RabbitMQ协程版客户端 2.0 正式发布

概述 workerman/rabbitmq 是一个异步RabbitMQ客户端&#xff0c;使用AMQP协议。 RabbitMQ是一个基于AMQP&#xff08;高级消息队列协议&#xff09;实现的开源消息组件&#xff0c;它主要用于在分布式系统中存储和转发消息。RabbitMQ由高性能、高可用以及高扩展性出名的Erlan…

基于SpringBoot的洗浴管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

直流无刷电机控制(FOC):电流模式

目录 概述 1 系统框架结构 1.1 硬件模块介绍 1.2 硬件实物图 1.3 引脚接口定义 2 代码实现 2.1 软件架构 2.2 电流检测函数 3 电流环功能实现 3.1 代码实现 3.2 测试代码实现 4 测试 概述 本文主要介绍基于DengFOC的库函数&#xff0c;实现直流无刷电机控制&#x…

51单片机——串口通信(重点)

1、通信 通信的方式可以分为多种&#xff0c;按照数据传送方式可分为串行通信和并行通信&#xff1b; 按照通信的数据同步方式&#xff0c;可分为异步通信和同步通信&#xff1b; 按照数据的传输方向又可分为单工、半双工和全双工通信 1.1 通信速率 衡量通信性能的一个非常…

oracle位运算、左移右移、标签算法等

文章目录 位运算基础与或非同或同或应用场景 异或异或应用场景 什么是真值表 oracle基础函数创建bitor(按位或)函数bitnot(按位非)函数bitxor(按位异或)函数左移函数BITSHIFT()函数(实测不可用&#xff0c;废弃掉该方案)右移函数(略&#xff0c;有此场景吗?) 实际应用资质字典…

VS2015 + OpenCV + OnnxRuntime-Cpp + YOLOv8 部署

近期有个工作需求是进行 YOLOv8 模型的 C 部署&#xff0c;部署环境如下 系统&#xff1a;WindowsIDE&#xff1a;VS2015语言&#xff1a;COpenCV 4.5.0OnnxRuntime 1.15.1 0. 预训练模型保存为 .onnx 格式 假设已经有使用 ultralytics 库训练并保存为 .pt 格式的 YOLOv8 模型…