【C++学习】类和对象(上)
前言:
由于之前电脑“嗝屁”了,导致这之前一直没有更新博客,今天才拿到电脑,在这里说声抱歉。接下来就进入今天的学习,在之前我们已经对【C++】进行了初步的认识,有了之前的知识铺垫,今天我们将来带领大家学习我们【C++】中的一个重要知识,即“类和对象”的学习。这个知识点我将分为三期进行讲解。好了,废话不多说直接进入本期【类和对象(上)】的学习。
本文目录
- 1.面向过程和面向对象初步认识
- 2.类的引入
- 2.1类的解读
- 3.类的定义
- 4.类的访问限定符及封装
- 4.1 访问限定符
- 5.类的作用域
- 6.类的实例化
- 7.类对象模型
- 7.1 如何计算类对象的大小
- 7.2 类对象的存储方式
- 8.this指针
- 8.1 this指针的引出
- 8.2 this指针的特性
- 👉8.3 this指针存在哪里?
- 👉8.4 this指针可以为空吗?
1.面向过程和面向对象初步认识
在正式开始学习之前,我们先来了解一个概念,那就是【面向对象和面向过程】的区别。
👉C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
我们通过日常生活中的洗衣服案例用通俗易懂的话来带领大家认识。

在生活中洗衣服通常要进行以上过程,而【面向对象】关心的就是将这些具体的过程/解决问题的步骤按部就班的进行下去,每个步骤可以将其封装成一个函数,这些函数按照一定的次序来调用,最终完成所需要做的事情,我们将这种思想称之为 【面向对象】。
👉C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
我们还是以洗衣服为例进行理解说明:

对于“我们”来说,洗衣服就很少会亲自动手去洗,至少“我”本人就是一直往洗衣机丢。我们将衣服,洗衣液,放到洗衣机里面(由洗衣机完成后续工作)而对于“我们”来说并不用关心衣服怎么来洗,洗衣机会帮我们完成接下来的任务,洗衣机实际是真正洗衣服的。我们不需要考虑其中具体过程,经过别的物品之手完成此事便可,我们将这种思想称之为【面向对象】,通过【面向对象】的方式处理,“人”“脏衣服”“洗衣机”“洗衣液”“水”均为对象,我们“洗衣服这件事情”是通过这些对象之间的“交互”把事情做完的。
2.类的引入
C++在C语言的基础上做了一些改进,使得C++具有了面向对象编程的特性。其中最重要的改进就是提供了类的概念。
2.1类的解读
👉类的基本思想是数据抽象和封装。是具有相同的属性和操作的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述。封装实现了类的接口和实现的分离。封装隐藏了类的实现,封装过后,用户只能访问类的接口,而不能访问类的实现。
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
有了基本认识过后,我们可能会想到“类”是怎么来的呢?
在之前我们已经说过了,由于【C++】兼容我们的【C】,它最开始就是从结构体延伸而来的,在【C++】中照样可以使用结构体,但是实际上在【C++】中我们把结构体升级为了今天我们将会探讨的“类”。
我们还是以直观形象的代码为例进行理解,接下来我将通过【C】和【C++】两种语法使用情况下写出两种代码,具体如下:
// C++兼容C结构体用法
typedef struct ListNode
{int val;struct ListNode* next;
}LTN;// C++把结构体升级成了类
struct ListNode
{int val;ListNode* next;
};
👉解析:
在【C】语言中,我们定义的结构体,struct ListNode是它的类型,而在【C++】当中我们的类型就是ListNode,所以大家才会看到下面那种代码格式。
此时,大家可能就会好奇,既然【C++】中的类是由【C】语言中的结构体引入来的,那么是否这两者之间就是一样的呢?答案当然是否定的,大家试着想想如果都是一样的话,我们在【C】语言中已经学过了,在【C++】完全就没必要在引入新概念了。
👉那么类和结构体之间到底有什么区别呢?
1.C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数;
2.结构使用栈存储,而类使用堆存储。结构在声明的时候就已经为其分配栈上的内存了,而类需要用new为其分配堆上的内存。;
3.结构体内的变量和函数一般是 public 的,可以在结构体作用域之外的地方使用,但是类的变量和函数一般是 private 的,只能被类作用域内的函数使用,类外不可以直接获取到类中的变量。(public 和private 具体什么意思后面会讲)
具体什么意思呢?我们一点点的来进行理解,在我们之前学习数据结构的时候,学到过【栈】的基本知识,首先是定义了一个结构体,在定义了各功能函数紧接着去一一实现,即数据和方法是分离开的。但是在【C++】中方法可以定义在其里面,即功能函数的实现可以定义在“类”里面。通过代码大家仔细体会:
struct Stack
{// 成员函数void Init(int n = 4){//...capacity = n;size = 0;}void Push(int x){//...a[size++] = x;}// 成员变量int* a;int size;int capacity;};int main()
{Stack zp; // 用类stack实例化出对象zpzp.Init(10);zp.Push(1);zp.Push(2);zp.Push(3);return 0;
}
上面结构体的定义,在C++中更喜欢用【class】来代替。
3.类的定义
定义一个类,本质上是定义一个数据类型的蓝图。这实际上并没有定义任何数据,但它定义了类的名称意味着什么,也就是说,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。

👉class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
👉类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
例如,我们使用关键字 【class】 定义 【student 】数据类型,如下所示:
class Date
{private:int _year;int _month;int _day;
};
关键字 【public】 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。您也可以指定类的成员为 【private 】或 【protected】,这个我们稍后会进行讲解。
👉 类中函数的两种定义方式:
1. 声明和定义全部放在类体中;
注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。2.类声明放在.h文件中,成员函数定义放在.cpp文件中(推荐)
注意:成员函数名前需要加类名::
对于第一种方法,就是我们上边写【栈】的那种方法。
至于第二种方法,就是声明和实现分开的方法,这种方法我们之前写数据结构时都是用的这种方法。(一般情况下,更期望采用第二种方式)

最后,说明一点,在类中定义成员函数以及成员变量时,不需要考虑定义的先后顺序,也就是说,即使成员变量放在成员函数的下面,成员函数中依然可以使用成员变量
4.类的访问限定符及封装
在上述类的定义的两种方式中,声明和定义方式下我们采用的是【struct】关键字,大家是否验证过当我们换成【C++】中的【class】之后是否还能正常编译成功呢?在这里给出大家答案,之后大家可以下去测试,当我们换成【class】之后程序跑起来就会出现报错的情况,具体如下:

但是当我们使用【class】之后,程序就会出现以下现象:

那么到底是什么原因呢?这就需要研究访问限定符的问题了。
4.1 访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

👉【访问限定符说明】
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问,现阶段我们只要会用public和private就可以(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } 即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C)
👉注意:

还有一个细节问题,就是我们的成员函数在定义时,为了防止之后的冲突,一般进行一些限制会在其前面或者后面加【_】(根据个人习惯)。
👉注意事项:
1.不能在类的声明中对数据变量进行初始化;
2.在类中声明的任何成员不能使用 extern、auto 和 register 存储类型关键字修饰;
3.类声明中可以给出成员函数的参数的默认值;
4.类中可以不含有任何成员变量和成员函数,这样的类被称为空类。
5. 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
此外,在定义类的成员函数的时候,如果不在类的内部定义(在内部给出定义,默认为内联函数),使用具体如下:
返回值类型 类名::成员函数名(形参列表)
{函数体
}
5.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 【::】作用域操作符指明成员属于哪个类域。
class Person
{
public:void PrintPersonInfo();
private:char _name[20];char _gender[3];int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{cout << _name << " " << _gender << " " << _age << endl;
}
6.类的实例化
用类类型创建对象的过程,称为类的实例化
- 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
Person p;//p占有实际的物理空间,这里就是一个实例化的过程
Person p1;
Person p2;
int main()
{Person._age = 100; // 编译失败:error C2059: 语法错误:“.”return 0;
}
Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄。
7.类对象模型
7.1 如何计算类对象的大小
class Date
{
public:// 定义void Init(int year, int month, int day){_year = year;_month = month;_day = day;}//private:int _year; // 声明int _month;int _day;
};int main()
{Date d1;Date d2;d1.Init(2023, 2, 2);d1._year++;d2.Init(2022, 2, 2);d2._year++;cout << sizeof(d1) << endl;
}
问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
我们直接运行程序,得到的结果如下:

此时,通过上述结合代码我们可以想到,打印出来的大小似乎只打印了成员变量的大小,而成员函数似乎不在我们的对象里面。要理解这个问题我们就需要探讨为什么成员变量在对象中,成员函数不在对象中呢?
7.2 类对象的存储方式
👉对象中包含类的各个成员,首先我们需要理解我们硬是要把成员函数的地址存到对象当中可以吗?答案当然是可以的,但是为什么不放到对象里面呢?大家可以想一想,当我们真的放到对象里面的时候会不会存在巨大的浪费,每个对象里面成员变量是独立的,但是成员函数是公共的,假设当我有10个函数,就有10个指针,就有40个字节,当我们每个对象都放一份时就会造成巨大的浪费。因此就没有必要在放到对象里面去,那么不当到对象里面应该放到哪去呢?我们可以放到一个公共的区域,这个公共的区域就是【代码段】,调用时就不到对象里面去找,而去这个公共的区域去查找。
因此在上述代码中就只需计算成员变量的大小,因此就为12.

因此实例化后的对象的大小,只需要计算成员变量大小即可,当然,类对象大小的计算与struct一样遵循结构体内存对齐规则。
我们通过一些例子来进行深入了解:
class A1 {
public:void f1() {}
private:int _a;
};
// 类中仅有成员函数
class A2 {
public:void f2() {}
};
// 类中什么都没有---空类
class A3
{};int main()
{cout << sizeof(A1) << endl;cout << sizeof(A2) << endl;cout << sizeof(A3) << endl;return 0;
}
输出结果为:

👉结论:
一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象用来占位,标识对象被实例化定义出来了。
8.this指针
8.1 this指针的引出
我们通过以下代码来引入指针问题:
class Date
{
public:// 定义void Init(int year, int month, int day){_year = year;_month = month;_day = day;}//private:int _year; // 声明int _month;int _day;
};int main()
{Date d1;Date d2;d1.Init(2023, 2, 2);d1._year++;d2.Init(2022, 2, 2);d2._year++;cout << sizeof(d1) << endl;
}
👉对于上述类,有这样的一个问题:
Date类中有 Init 这个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init
函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
这就引入了即将要学的指针的知识:
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
编译器在编译过后,它会自主的做一件事(对我们来说不能做),增加一个【this】,并且调用的地方也会被改。所以这里的“年月日”并不是我们声明的,而是【d1 or d2】的,具体到底是【d1】的还是【d2】的,如果是【d1】调用即是赋值给【d1】的,【d2】同理。(在实参或形参处千万不能自己显示去加,这是编译器自己做的,但是函数体内部可以使用这个this指针。)

函数体内部使用这个this指针:
class Date
{
public:// 定义void Init(int year, int month, int day){cout << this << endl;this->_year = year;//这里可以使用this->_month = month;this->_day = day;}private:int _year; // 声明int _month;int _day;
};int main()
{Date d1;Date d2;d1.Init(2023, 2, 2);d2.Init(2022, 2, 2);}
输出结果为:

8.2 this指针的特性
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

👉8.3 this指针存在哪里?
其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了寄存器【ecx】中(VC++编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。

👉8.4 this指针可以为空吗?
我们还是借助代码来进行理解:
class Date
{
public:// 定义void Init(int year, int month, int day){cout << this << endl;this->_year = year;this->_month = month;this->_day = day;}void func(){cout << this << endl;cout << "func()" << endl;}private:int _year; // 声明int _month;int _day;
};int main()
{Date d1;Date d2;d1.Init(2023, 2, 2);d2.Init(2022, 2, 2);Date* ptr = nullptr;//ptr->Init(2022, 2, 2); // 运行崩溃ptr->func(); // 正常运行(*ptr).func(); // 正常运行
}
👉解析:
1.对于【ptr->func();】这行代码执行起来,最后的结果是什么样的呢?
当我们运行代码后,我们的程序是正常运行的。我们一步步分析,开始时去调用这个【func】这个成员函数时会显得十分奇怪。为什么呢?因为这是一个【date】的指针,但是却是一个空指针,结果显示却是正常运行。**大家是不是都“蒙圈”了呀!!!**怎么会是正常运行呢?

2.对于【ptr->Init(2022, 2, 2); 】这行代码运行起来之后结果是怎么样的呢?

上面两个问题我们结合起来看。首先大家是否明白【func】和【Init】是否在对象里面,在【C】语言中我们学过在对象调用就用【.】,而指针调用则用【->】,我们这个函数显然是不在对象里面的,那么要调用【func】这个函数就要转换为【call】即一个地址,那么这个地址到哪里去找呢?我们在之前说过成员函数是在公共区域,所以是去公共区域部分去找,就是代码段,所以说虽然这里有个【->】,但并没有发生解引用操作。其次,我们调用成员函数需要传递【this】指针,所以【ptr->func();】并没有解引用,但是这个【ptr】传递给了【this】指针,所以这里的【this】是个空,并不会报错,所以【ptr->func();】这行运行起来之后是正常运行。而对于【ptr->Init(2022, 2, 2); 】这行代码而言,同理调用的时候不会崩,但是紧随之后用【this】去进行解引用操作了,所以程序就会崩溃。
最后看一行代码:【 (*ptr).func(); 】 这行代码运行后结果怎么样呢?
有了上面的知识我们知道,在调用的时候这里是不会发生错误的,大家注意这里的【ptr】是传递给【this】,所以是能够正常运行的!!!

我们通过调试,在汇编情况看下这两行代码:

大家会发现这两行代码的从汇编视角下看没有区别!!!
👉注意:
有没有解引用的行为取决于要访问右边的东西在不在对象里面,而不是用没用那个符号。千万不要被事物的表面现象所迷惑!!!
到此,类和对象(上)的学习便到此结束了。大家一定要结合知识点,多去总结和理解,争取消化掉这部分知识。
本期知识如果对你有帮助的话,记得点赞三连哟!!!
相关文章:
【C++学习】类和对象(上)
前言: 由于之前电脑“嗝屁”了,导致这之前一直没有更新博客,今天才拿到电脑,在这里说声抱歉。接下来就进入今天的学习,在之前我们已经对【C】进行了初步的认识,有了之前的知识铺垫,今天我们将来…...
一文带你深入理解【Java基础】· Java反射机制(下)
写在前面 Hello大家好, 我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正࿰…...
JVM的几种GC
GC JVM在进行GC时,并不是对这三个区域统一回收。大部分时候,回收都是新生代~ 新生代GC(minor GC): 指发生在新生代的垃圾回收动作,因为Java对象大多都具备朝生夕灭的特点,所以minor GC发生得非…...
掌握Shell脚本的if语句,让你的代码更加精准和高效
前言 大家好,我是沐风晓月,本文首发于csdn, 作者: 我是沐风晓月。 文章收录于 我是沐风晓月csdn专栏 【系统架构实战】专栏中的【shell脚本入门到精通】专栏。 本专栏从零基础带你层层深入,学会shell脚本,不是梦。 &…...
音质好的蓝牙耳机有哪些?音质最好的蓝牙耳机排行
说起当代人外出必备是数码产品,蓝牙耳机肯定存在。不管是听歌还是追剧,蓝牙耳机在音质上的表现也是越来越好了。下面,我来给大家推荐几款音质好的蓝牙耳机,一起来看看吧。 一、南卡小音舱蓝牙耳机 参考价:259 蓝牙版…...
一次Android App NDK崩溃问题的分析及解决
文章目录小结NDK崩溃的问题通过logcat查看崩溃日志提取tombstone的记录通过ndk-stack来输出日志取得的日志分析并解决分析使用add2line定位具体报错的行数解决参考小结 最近碰一次Android App NDK崩溃的问题,这个NE(Native Exception)是从ND…...
因果图判定表法
因果图&判定表法 在了解了等价类和边界值比较适宜搭档的测试用例方法之后 接下来我们来了解另外一队就是因果图和判定表 因果图会产生判定表法 因果图法 等价类划分法和边界值分析方法都是着重考虑输入条件而不考虑输入条件的各种组合、输入条件之间的相互制约关系。例…...
Oracle 数据库相关信息清单列表
Oracle 数据库相关信息清单列表 一、设置Oracle安装目录 Oracle基目录(ORACLE_BASE):D:\databases\oracle\oracle_11g\app\Administrator 软件位置(ORACLE_HOME):D:\databases\oracle\oracle_11g\app\Administrator\product\11.2.0\dbhome_1 数据库文件位置:D:\databa…...
射频资料搜集--推荐几个网站和链接
https://picture.iczhiku.com/resource/eetop/wHKYFQlDTRRShCcc.pdfhttps://picture.iczhiku.com/resource/eetop/wHKYFQlDTRRShCcc.pdfVCO pulling的资料 模拟滤波器与电路设计手册 - 射频微波仿真 - RF技术社区 Practical RF Amplifier Design Using the Available Gain Pr…...
B1048 数字加密
decription 本题要求实现一种数字加密方法。首先固定一个加密用正整数 A,对任一正整数 B,将其每 1 位数字与 A 的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对 13 取余——这里用 J 代表 10、Q 代表 11、K 代表 …...
Qt使用FFmpeg播放视频
一、使用场景 因为项目中需要加载MP4播放开机视频,而我们的设备所使用的架构为arm架构,其中缺乏一些多媒体库。安装这些插件库比较麻烦,所以最终决定使用FFmpeg播放视频。 二、下载编译ffmpeg库 2.1 下载源码 源码下载路径:http…...
Win32 ListBox控件
Win32 ListBox控件 创建ListBox控件 创建窗口函数 HWND CrateWindowEx(DWORD dwExStyle , // 窗口的扩展风格,基本没用LPCTSTR lpClassName, // 已经注册的窗口类名称LPCTSTR lpWindowName, // 窗口标题栏的名字DWORD dwStyle, // 窗口的基本风格int x, // 左上角水平坐标int …...
最大值池化与均值池化比较分析
1 问题在深度学习的卷积网络过程中,神经网络有卷积层,池化层,全连接层。而池化层有最大值池化和均值池化两种情况,而我们组就在思考,最大值池化和均值池化有什么区别呢?两者的模型准确率是否有所不同&#…...
统计学 多元线性回归
文章目录统计学 多元线性回归多元线性回归模型拟合优度显著性检验线性关系检验回归系数检验多重共线性及其处理多重共线性的问题多重共线性的识别与处理变量选择利用回归方程进行预测哑变量回归统计学 多元线性回归 多元线性回归模型 多元线性回归模型:设因变量为…...
tar和gzip压缩和解压
打包和压缩的区别:打包:将多文件 封装在一起压缩:将多文件 封装在一起 通过特定的算法 将冗余的数据 进行删除tar默认是打包命令,如果想用tar进行压缩 必须加选项1、gzip格式压缩:tar zcvf 压缩包包名 文件1 文件2 文件…...
搭建Docker企业私有仓库
什么是仓库 仓库(Repository)是存储和分发 Docker 镜像的地方。镜像仓库类似于代码仓库,Docker Hub 的命名来自 GitHub,Github 是我们常用的代码存储和分发的地方。同样 Docker Hub 是用来提供 Docker 镜像存储和分发的地方。 谈…...
[NOIP2009 提高组] 最优贸易(C++,tarjan,topo,DP)
题目描述 $C 国有国有国有 n 个大城市和个大城市和个大城市和 m$ 条道路,每条道路连接这 nnn个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 mmm 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的…...
计算机网络:移动IP
移动IP相关概念 移动IP技术是移动结点(计算机/服务器)以固体的网络IP地址,实现跨越不同网段的漫游功能,并保证了基于网络IP的网络权限在漫游中不发生任何改变。移动结点:具有永久IP地址的设备。归属代理(本…...
binutils工具集——GNU binutils工具集简介
以下内容源于网络资源的学习与整理,如有侵权请告知删除。 GNU binutils是一个二进制工具集,主要包括: ld,GNU链接器。as,GNU汇编器。addr2line,把地址转化为文件名和行号。nm,列出目标文件的符…...
Golang编译选项(ldflags)有趣应用
本文介绍如何在构建时使用ldflags选项给Golang应用程序注入变量,用于给Go可执行文件增加版本标识或GIT提交摘要等信息。 应用程序的版本信息 我们首先查看Docker Cli 包含的提交信息: docker version 返回结果: Server: Docker Engine - Co…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
