【C++】类和对象
1.面向过程和面向对象初步认识
我们知道,C语言是面向过程的,关注的就是问题解决的过程;
C是面向过程和面向对象混编,因为C兼容了C语言,而面向对象关注的不再是问题解决的过程;
而是一件事情所关联的不同对象。
在点外卖时我们会经常使用饿了么/美团,而它们对应的就是一个点餐系统,面向过程和面向对象在这个系统中关注的对象是不同的。
面向过程的语言会关注点餐、接单、以及骑手接单等功能函数的实现,而面向对象关注的是商家、用户、骑手之间的关系;用户点餐商家出餐,然后交由骑手配送。
2.类的引入
提到类,很多学过C语言的朋友就会联想到结构体,C兼容C语言——C语言中的struct在C中同样适用。
struct student
{char* name;int age;
};
C++在兼容struct的同时,将struct升级成了类。
类中既可以有成员变量,也可以有成员函数,像Stack中的Init、Push、Pop。
struct Stack
{//这里的Stack操作可以直接命名为Init、Push、Pop,因为成员函数在类内void Init(){a = NULL;top = capacity = 0;}void Push(int x){}void Pop(){}int* a;int top;int capacity;
};
定义了Stack类型,下面来定义对象:
int main()
{struct Stack st1; //兼容了C语言的定义方式Stack st2; //Stack是一个类,类名就是类型return 0;
}
此外,类相比于结构体的使用还有一个便捷之处:
typedef struct ListNode_C
{struct ListNode_C* next; //必须要写struct,因为typedef到LTNode;之后才生效int val;
}LTNode;// C++
struct ListNode_CPP
{ListNode_CPP* next; //不需要写struct,ListNode_CPP是一个类名,ListNode_CPP*是一个指向ListNode_CPP的指针int val;
};
3.类的定义格式
class className
{//成员变量+成员函数
};
C 中虽然可以使用struct来定义类,但是C 用户更偏向于使用class来定义类。
4.类的访问限定符和封装
4.1 访问限定符
public | 公有 |
---|---|
private | 私有 |
protected | 保护 |
public修饰的成员在类内和类外都能被访问,和我们在C语言中使用的struct相似。
protected和private在类外不能访问,且在继承中才能体现两者的区别;
访问限定符作用域从该访问限定符出现位置到下一个访问限定符,若再无访问限定符出现,就到结束。
需要特别注意的是class和struct有默认访问权限,class默认是私有的,struct默认是公有的:
class Stack
{void Init(){a = 0;top = capacity = 0;}void Push(int x){}void Pop(){}int* a;int top;int capacity;
};int main()
{Stack st;st.Init();//class默认访问权限私有,无法访问return 0;
}
在struct中访问权限默认是公有,可以直接访问。
4.2 类的两种定义方式
1.声明和定义全放在类中
像我们前面写的Stack类,声明和定义都在class Stack{}中完成。
成员函数在类内定义,如果符合inline的条件,编译器会将其当做内联函数处理。
这里为了方便观察,借助一下反汇编:
2.声明和定义分离
声明在.h文件中,成员函数定义在.cpp文件中:
//f.h
struct QueueNode
{QueueNode* next;int val;
};class Queue
{
public:void Init();void Push(int x);void Pop();
private:QueueNode* head;QueueNode* tail;
};
//f.cpp
void Queue::Init()
{head = tail = nullptr;
}void Queue::Push(int x)
{}void Queue::Pop()
{}
在f.cpp中,注意限定类域(如果不写类域,无法找到对应的声明);inline函数的条件需要满足声明和定义不能分离,所以这种定义方式不会出现inline函数。
- 小函数(汇编指令少),想成为inline直接在类中定义即可
- 大函数,应该声明和定义分离
【面试题】
C++中struct和class的区别是什么?
解答:C兼容了C语言中struct定义结构体的使用;另外C中的struct还可以用来定义类,这和class定义类是一样的,区别在于struct定义的类默认访问权限是public,而class定义的类默认访问权限是private。
继承和模板参数列表位置
4.3 封装
面向对象三大特性:封装、继承、多态。
封装:将数据和操作数据的方法进行结合隐藏对象的属性和实现细节,仅仅对外公开接口来和对象交互。
封装本质上是一种管理,让用户使用起来更方便。
- 像可供参观的兵马俑,如果不加以保护,那么可能慢慢的就没有了参观价值
- Stack返回栈顶的元素,如果不加以封装,数据可能有误
cout << st.a[st.top] << endl;
cout << st.a[st.top-1] << endl;
栈顶元素的下标是top还是top-1是不清楚的,如果直接访问数据,可能是错的,所以封装一个Top()就显得格外的重要。
int Top()
{return a[top - 1];//设计者来写,一定是对的
}
这样以来还能保证数据的正确性。
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是一个类名,下面对其进行实例化:
int main()
{Person p1;//实例出的对象是需要占用空间的Person p2;return 0;
}
7.类对象模型
7.1 类对象的存储方式
以Person类为例:
class Person
{
public:void SetPersonInfo(char* name){_name = name;}void PrintPersonInfo(){cout << _name << " "<< _gender << " " << _age << endl;}
private:char _name[20];char _gender[3];int _age;
};
实际上类对象的存储模型可以用一张图来解释:
各对象的成员变量存储在各自的地址空间中,成员函数则是放在了公共代码区;编译链接时根据函数名去公共代码区找到函数的地址。
int main()
{Person p;p.SetPersonInfo("kangkang");//编译链接时找到函数地址p.PrintPersonInfo();
}
7.2 类对象的大小
观察下面程序:
// 类中既有成员变量,又有成员函数
class A1
{
public:void f1() {}
private:int _a;
};
// 类中仅有成员函数
class A2
{
public:void f2() {}
};
// 类中什么都没有---空类
class A3
{};int main()
{//编译环境为MSVC//sizeof(类名)得到的是该类型实体的大小——类对象的大小cout << sizeof(A1) << endl;//4cout << sizeof(A2) << endl;//1cout << sizeof(A3) << endl;//1return 0;
}
根据类对象的存储方式得知:成员函数是放在公共代码段的,并没有在类的内部,A2和A3结果之所以为1(编译器不同结果可能出现差异),只是标识对象存在。
成员变量的存储方式,遵循结构体内存对齐原则。
7.3 结构体内存对齐原则
- 第一个成员对齐到结构体偏移量为0的地址处。
- 其他成员变量要对齐到对齐数的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
- 结构体总大小为最大对齐数的整数倍。
- 如果出现了嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是
所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
【面试题】
1.为什么要进行内存对齐?
平台原因(移植原因) :不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
2.如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐?
#pragma pack(8)
//#pragma pack可以修改编译器默认对齐数
不能按照任意字节对齐,修改对齐数时可能遇到:warning C4086: pragma 参数应为 “1”、“2”、“4”、“8” 或者 “16”。
3.什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景?
大端是将数据的低位存储在高地址处,小端是将数据的低位存储在低地址处。
int testPort()
{int a = 0x11223344;if (*((char*)(&a)) == 0x44)return 0;//小端elsereturn 1;//大端
}
场景: 在不同类型的机器之间通过网络传送二进制数据时,机器大小端不同可能导致传输的数据出问题。
8.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 a;
};int main()
{Date d1;Date d2;return 0;
}
在d1调用Init初始化的时候,该函数是如何知道该初始化d1,而不是初始化d2的?
编译器给每个非静态的成员函数增加了一个隐藏的this指针,让该指针指向当前对象。
注意:this指针的类型是类名 const this。*
【面试题】
this指针存在哪?
解答:栈;因为this指针是一个形参。
9.类的6个默认成员函数
一个类中,什么都没写简称为空类,但是空类中真的什么都没有吗?
并不是,类中会自动生成6个默认的成员函数。
10.构造函数
现假设一个栈的实现是使用一个指针指向一片空间,其他成员变量暂不考虑;然后对栈进行操作,如果栈并没有初始化,那么指针就是一个野指针,如果不初始化就对栈进行操作,很有可能导致程序的崩溃。
既然我们可能会忘记进行初始化工作,那么有没有一种办法可以一定进行初始化呢?
C++新增了非常牛的构造函数,就是为了解决这一问题。
typedef int DataType;
class Stack
{
public:Stack(int capacity = 4){cout << "Stack(int capacity = 4)" << endl;_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_size = 0;_capacity = capacity;}
private:DataType* _array;int _capacity;int _size;
};class MyQueue
{
private:Stack _st1;Stack _st2;
};int main()
{MyQueue q;Stack st;return 0;
}
C++规定:默认构造函数对内置类型不做处理,对自定义类型会调用它的构造函数。
队列的实现可以借助两个栈,那我们写栈的构造函数,不写队列的构造函数,那么编译器生成队列的默认构造会调用栈的默认构造函数。
默认构造函数由三种:
- 编译器自己生成的
- 全缺省的构造函数
- 无参构造函数
注意:一个类中不写构造函数的话,编译器会自动生成;写了构造函数编译器就不会再自动生成。
C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时
可以给默认值。
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};
像这个程序就用到了C++11中的补丁:
- Date类没有写构造函数,编译器会自动生成
- 自动生成的构造函数会使用Date类中给的缺省值进行初始化
- 自定义类型调用它的构造函数
总结:一般的类都不会让编译器默认生成构造函数,都会自己写;只有特殊情况才会使用默认生成。
11.析构函数
11.1 概念
通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没的呢?
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成
的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
11.2 特性
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数
- 对象生命周期结束时,C++编译系统系统自动调用析构函数
class Time
{
public:~Time(){cout << "~Time()" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:int _year = 1970;int _month = 1;int _day = 1;Time _t;
};
Date类没有写析构函数,编译器会自动生成一个析构函数;自动生成的析构函数在遇到自定义类型时,会调用它的析构函数。
在销毁对象时内置类型不需要进行资源清理; Date类默认生成的析构函数,会调用Time类的析构函数。
总结:如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;但是有
资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
12.拷贝构造函数
12.1 概念
在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
那在创建对象时,可否创建一个与已存在对象一模一样的新对象呢?
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般使用const修饰)。
12.2 特征
1.拷贝构造函数是构造函数的一个重载形式
2.拷贝构造函数的参数只有一个,且必须是类对象的引用; 使用传值方式编译器直接报错,会引发无穷的递归调用。
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};
3.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按字节序完成拷贝,这种拷贝叫做浅拷贝/值拷贝。
注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
现假设有一个struct结构体Stack,它内部有一个成员变量是一个指针类型,创建两个Stack对象st1和st2。
那么st1指向的是一片动态开辟的空间,而编译器默认生成的拷贝构造会进行值拷贝,也就是说st2中的a也指向这片空间,所以在Stack完成资源清理的时候会将同一片空间释放两次,导致程序崩溃。
可以很明显的看出,同一片空间释放了两次,所以默认生成的拷贝构造函数还是不够的。
13.赋值运算符重载
13.1 运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类****型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator 后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
- . :: sizeof ?: .* 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}bool operator==(const Date& d2){return _year == d2._year&&_month == d2._month&& _day == d2._day;}
private:int _year;int _month;int _day;
};
13.2 赋值运算符重载
赋值运算符重载的形式:
- 参数类型:const T&,传递引用可以提高传参效率
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回*this :要复合连续赋值的含义
Date& operator=(const Date& d){if(this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
注意:赋值运算符只能重载成类的成员函数不能重载成全局函数。
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}
原因:赋值运算符如果不显式实现,编译器会生成一个默认的;此时用户再在类外自己实现一个全局的 赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的 成员函数。
如果我们并没有显示的去写赋值运算符重载,编译器会自动生成一个,以值的方式逐字节的拷贝;内置类型直接赋值,自定义类型会调用对应的赋值运算符来完成。
14.取地址和const取地址运算符重载
这两个默认成员函数,编译器会自动生成,一般不需要我们去写。
class Date
{
public:Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year; // 年int _month; // 月int _day; // 日
};
这两个成员函数一般默认的就可以使用,只有特殊情况才需要我们去写——比如想让别人获取指定的内容。
15.初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
下面举个栗子:
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
private:int _year = 0;//缺省值int _month;int _day;
};
Date类中的成员变量使用初始化列表还是在构造函数体内进行初始化都是可以的,但是有三种成员变量只能使用初始化列表来初始化:引用成员变量、const成员变量 、自定义类型成员(且该类没有默认构造函数时),否则是无法通过编译的。
15.1 引用成员变量
class Date
{
public:Date(int& x): ref(x){}
private:int& ref;
};
15.2 const成员变量
class Date
{
public:Date(int year, int num): _year(year), N(num){}
private:int _year;const int N;
};
15.3 自定义类型成员(且该类没有默认构造函数时)
class Time
{
public:Time(int hour){_hour = hour;}
private:int _hour;
};class Date
{
public:Date(int year, int hour){_year = year;Time t(hour);_t = t;}
private:int _year;Time _t;
};
16.explicit关键字
这里同样以Date类为例,并且有单参数的构造函数:
class Date
{
public:Date(int year):_year(year){cout << " Date(int year)" << endl;}
private:int _year;
};
那我们创建类对象的方式可以这样写:
int main()
{Date d1 = 2022;return 0;
}
2022会发生隐式类型转换,然后生成一个Date类的临时对象,之后通过拷贝构造创建d1。
如果给Date类的单参数构造函数,加上explicit关键字,那么就无法发生隐式类型转换,像以上这种写法就会报错。
explicit Date(int year):_year(year)
{cout << " Date(int year)" << endl;
}
17.static成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。
注意:静态成员变量一定要在类外进行初始化。
【面试题】
实现一个类,计算程序中创建出了多少个类对象。
实现:
class A
{
public:A() { ++_scount; }A(const A& t) { ++_scount; }~A() { --_scount; }static int GetACount() { return _scount; }
private:static int _scount;
};
int A::_scount = 0;
静态的_scount任何类对象都可以访问,调用构造函数和拷贝构造函数的时候++_scount,析构的时候
–_scount就可以了。
需要注意的是静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
下面给class A增加上成员变量nostatic,并在static成员函数中访问:
class A
{
public:A() { ++_scount; }A(const A& t) { ++_scount; }~A() { --_scount; }static int GetACount() { nostatic = 0;return _scount; }
private:static int _scount;int nostatic;
};
没有this指针,这种访问是无法进行的。
C++类和对象的大部分内容在本篇中都有提到,可能还有一小部分知识点没有涉及到,没有涉及到的知识点大家可以私信我,我后续也会对内容做一些补充,尽可能抽出时间来慢慢完成。
相关文章:

【C++】类和对象
1.面向过程和面向对象初步认识 我们知道,C语言是面向过程的,关注的就是问题解决的过程; C是面向过程和面向对象混编,因为C兼容了C语言,而面向对象关注的不再是问题解决的过程; 而是一件事情所关联的不同…...

Java缓存面试题——Redis应用
文章目录1、为什么要使用Redis做缓存?2、为什么Redis单线程模型效率也能那么高?3、Redis6.0为什么要引入多线程呢?4、Redis常见数据结构以及使用场景字符串(String)哈希(Hash)列表(list)集合&am…...

KMP算法详细理解
一、目的1.KMP应用场景:可以解决字符串匹配问题; 在一个串中查找是否出现过另一个串。2.KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。3.KMP算法关键在于&…...

RabbitMQ单节点安装
在学习RabbitMQ之前,必须要把RabbitMQ的环境搭建起来,刚开始学习时,搭建单节点是入门RabbitMQ最方便、最快捷的方式,这篇文章就是介绍如何使用RabbitMQ压缩包的方式搭建一个单节点的RabbitMQ。 在实际项目中,服务器都…...

tomcat 服务的目录结构和tomcat的运行模式
目录 一、tomcat 服务的目录结构解析: 1、tomcat目录结构: bin目录: conf目录: lib目录: logs目录: temp目录: webapps目录: wokr目录: 二、tomcat服务的运行模…...

vector迭代器失效问题
一、迭代器: 迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所…...

2023年排名前茅的十大饭店装修设计!
相信大家都是知道的,饭店装修设计其实是一门很深的学问,只有掌握这门学问才能够打造出来精美的空间,因此饭店装修必须要有专业餐饮设计公司的设计师进行设计。但是在国内饭店装修设计公司那么多,饭店老板要如何选择呢?…...

MFCCA多通道多说话人语音识别模型上线魔搭(ModelScope)
实验室研发的基于多帧跨通道注意力机制(MFCCA)的多说话人语音识别模型近日上线魔搭(ModelScope)社区,该模型在AliMeeting会议数据集上获得当前最优性能。欢迎大家下载。开发者可以基于此模型进一步利用ModelScope的微调…...

刷题记录:牛客NC25078[USACO 2007 Ope S]City Horizon
传送门:牛客 题目描述: Farmer John has taken his cows on a trip to the city! As the sun sets, the cows gaze at the city horizon and observe the beautiful silhouettes formed by the rectangular buildings. The entire horizon is represented by a number line …...

【Java|golang】 1238. 循环码排列---格雷编码
给你两个整数 n 和 start。你的任务是返回任意 (0,1,2,…,2^n-1) 的排列 p,并且满足: p[0] start p[i] 和 p[i1] 的二进制表示形式只有一位不同 p[0] 和 p[2^n -1] 的二进制表示形式也只有一位不同 示例 1: 输入:n 2, start …...

Python自动化测试框架封装和调用
封装与调用函数与参数化前言 面实现了参数的关联,那种只是记流水账的完成功能,不便于维护,也没什么可读性,接下来这篇可以把每一个动作写成一个函数,这样更方便了。参数化的思维只需记住一点:不要写死 登录…...

线程的执行
承接上文CPU原理简介程序的执行是由控制器发信号推动整个程序一步一步向前走,将数据存储在寄存器,从程序计数器中获取指令,比如先把3放到寄存器,再把5放到寄存器,再做一个加法,加法就是一个指令,…...

【视频】海康摄像头、NVR网络协议简介
1、软硬件整体架构 2、涉及的网络协议 3、协议简介 3.1 海康私有协议 设备发现SADP:进行设备的发现、激活、修改网络参数、忘记密码等; SDK:4200、系统平台的接入前端设备,协议不对外开放,但对外提供接口库; ISAPI:Intelligent Security API(智能安全API),基于HTTP传输…...

【Spring的事务传播行为有哪些呢?Spring事务的隔离级别?讲下嵌套事务?】
如果你想寻求一份与后端相关的开发工作,那么关于Spring事务相关的面试题你就不能说不会并且不能不知道? 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步? 一.Spring中声明事务的方式 1.1 编程式事务 编程…...

其实一点不难学会这三步一定让你学会制作一个『3D建模』大屏
上次已经教过大家怎样制作一个简单的2D数据可视化大屏~那有一些朋友们就会说那些炫酷的3D可视化大屏是怎样制作的呢?这不就来了,今天就教大家怎样用山海鲸可视化软件制作一个带3D建模的可视化大屏,并且最重要的是无需会特别复杂的3D建模知识。…...

【C++】C++的内存模型之四大分区
程序的内存模型 C程序在执行时,将内存大方向划分为4个区域 代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:由编译器自动分配释放,存放函数的参数值&…...

Vue跨级通信(重点)
当不使用Vuex的前提下,子孙传递就得使用另外一种办法:provide 和 inject 总结:provide / inject 类似于消息的订阅和发布。- inject接收数据。- provide提供或发送数据,(1)provide(name…...

支付系统中的设计模式07:责任链模式
最近公司业务的发展果然如老板当初所画(预)饼(言)的那样红(恍)红(恍)火(惚)火(惚),蒸蒸日上,每天的流水都在不断攀升到新的高度,有不少人都从公司开发的电商平台挣到了钱。 不过问题也接着来了——运营部门经过老板的同意,也学着产品经理提出了下面几项非常合理…...

期末综合考试
一、概率论1、全概率公式、贝叶斯公式应用2、期望、方差、协方差的定义以及性质证明(1) 期望(2) 方差(3) 协方差二、数理统计1、参数估计(1) 矩估计(2) 最大似然估计(3) 综合例题一、概率论 1、全概率公式、贝叶斯公式应用 记住标黄的两段,上考场直接套数据&#x…...

数据结构与算法之爬楼梯动态规划
一.题目(爬楼梯)假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?注意:给定 n 是一个正整数。示例 1:输入: 2输出: 2解释: 有两种方法可以爬…...

CleanMyMac4.12最新Mac电脑系统垃圾清理神器
CleanMyMac是Mac一款神器,特别是清理已卸载软件残留垃圾文件信息库比较全面。 clearmymac以极其快速和时尚的方式为您提供及时的建议,组织,更新和保护您的Mac。完全支持macOS 11(Big Sur)操作系统;它以其简…...

数据治理如何做?火山引擎 DataLeap 帮助这款产品 3 个月降低计算成本 20%
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 本文讲述字节跳动一款 App 产品的数据治理故事。该产品随着用户体量和数据体量不断增长,数仓的任务量、数据量也不断攀升,运维难、成本贵、稳…...

求职3个月,简历大多都石沉大海,一听是手工测试都纷纷摇头....太难了
距离被上家公司裁员已经过去了3个月了,3个月的求职经历真的让我痛不欲生,我也从中理解感叹到了很多,想写出来,告诫跟我一样的经历的人。 我今年26岁,大学是一所普通的大专,学的是机电专业,如何…...

Visual Studio快捷键汇总
常用快捷键CtrlEC 注释代码CtrlEU 取消注释代码CtrlED 格式化全部代码CtrlShiftA 新建类CtrlRG 删除无效UsingCtrlH 批量替换CtrlG 跳转到指定行CtrlEE 在交互窗口中运行选中代码(很实用)AltEnter 快速引用shiftF9 监控(代码运行时)shiftF6 生成(当前类库)F6 生成(整个解决方案…...

ctf pwn基础-2
今天学了一个保护的绕过,这里讲一讲,这个好像是使用的是格式化字符串漏洞。 目录 基础 实例讲解 基础 首先我们要知道什么是canary保护,就是在入栈EBP以后加一个Canary 我可能讲的不是很好,大家可以看看这些 文章 用通俗一点将就…...

从一个SQL打印全年日历漫谈数据仓库中时间操作场景的重点写法
文章目录前言一、我如何快速确定今年是否是闰年的😣二、 我如何从DATE类型数据获取年、月(月初&月末)、周、日、时、分、秒信息🤯三、我如何快速查到本月月初第一周的周一和本月最后一周周一是在几号😑四、我如何快速确定每个季度的开始和…...

Java跳槽涨薪之路-想学Java的赶紧上车了
前言Java 是近 10 年来计算机软件发展过程中的传奇,在很多开发者心中的地位可谓“爱不释手”,与其他一些计算机语言随着时间的流逝影响也逐渐减弱不同,Java 随着时间的推移反而变得更加强大。按应用范围,Java 可分为 3 个体系&…...

MyBatis解析全局配置文件
目录 MyBatis介绍 传统JDBC和Mybatis相比的弊病 传统JDBC的问题如下 mybatis对传统的JDBC的解决方案 Mybaits整体体系图 使用大致过程 MyBatis 源码编译 源码解析 配置文件解析 读取配置文件 返回SqlSessionFactory 配置文件内容 解析的核心方法 解析出来的对象 …...

37-Golang中的封装
封装介绍 封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作 封装的理解和好处 1.隐藏实现细节 2.可以对数据进行验证,保证安全合理 如何体现封…...

Python Pytorch开发环境搭建(Windows和Ubuntu)
Python Pytorch开发环境搭建(Windows和Ubuntu) 目录 Pytorch开发环境搭建 1. 安装cuda cudnn (1)Windows安装方法 (2)Ubuntu18.04安装方法 2. 安装Python(推荐使用Anaconda) (1)Windows安装方法 (2)Ubuntu18.04安装方法 3. Pytorch安装 4. 安装…...