C++深入学习part_2

news2024/10/6 16:25:11

面向对象思想

这一节Java里面都有类似的概念,比较简单,就不写的太啰嗦了。

首先是认识以前面向过程的思想:
在这里插入图片描述
而现在再来感受一下面向对象的思想:
在这里插入图片描述
这玩意儿学过Java的话应该是比较简单的,就不再赘述了。

类的定义

在这里插入图片描述

代码风格注意

首先类中的public的内容应该放在类的最上面,这样可以让人一眼就看出该类能够给外部提供什么样的功能和作用,而private的内容则应该放在类的最底下。
其次类中的数据成员都要以_下划线开头,不过不同公司可能代码规范不太一样,选一种后面进了公司再改也是一样的。
在这里插入图片描述
大括号内部称为类内部,大括号之外称为类外部。

类的声明

另外,class类的默认访问权限是private私有的。
在这里插入图片描述
类的定义过程就是类的所有成员函数实现的过程。

struct与class的区别(面试常考)

1、在C++中,struct的功能被扩展了,基本上与class的功能相同
2、唯一不同的是默认访问权限不同:struct是默认public的,而class默认是private的

对象的创建

在这里插入图片描述
在这里插入图片描述

对象的数据成员的初始化问题

首先要区别一下在C++中赋值与初始化的区别,这与Java是很不一样的(有点咬文嚼字的感觉,但这样会更好理解C++中的列表初始化语法),在C++中假如一个类的构造函数如下:

class Computer{
public:
	Computer(int price){
		_price = price;//这里在Java中其实就已经是初始化了,而在C++中严格来讲这其实只是赋值操作
	}
private:
	int _price;
};

int main(){
	//更直观的来说
	int a = 10; //这是赋值操作
	int b(10); //这才是实际意义上的C++中的初始化操作
}

所以出现了C++中比较独树一帜的构造器写法:初始化列表。
在这里插入图片描述

示例:

class Computer{
public:
	//C++中初始化列表的构造器写法,也可以给予参数默认值
	Computer(int price): _price(price) , _cnt(0) {}
private:
	int _price;
	int _cnt;
};

对象的数据成员初始化问题

在这里插入图片描述
再强调一下声明和定义的区别:

int x; //变量的声明,未初始化,这不推荐
int y = 0//变量的定义,有初始化,大力提倡
int* p; //不推荐,这是野指针

对象的销毁

在这里插入图片描述
验证代码:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int)" << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};


void test0(){
        //语句块,当我们将代码包进一个语句块时
        //此时它的作用域就仅剩这个范围
        //当这个范围结束时即Point对象就被销毁了
        {
        Point pt(1,2);//存放在栈上的对象,称为栈对象
        cout << "pt: " << endl;
        pt.print();
        }
        cout << "1111111111111" << endl;
}

int main(){
        test0();
}

编译运行:
在这里插入图片描述
可以看见pt对象此时作为栈上的对象确实是在被销毁时自动执行的,这就出现了析构函数的一个调用时机问题。

析构函数的调用时机

全局对象的销毁时机:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): " << _ix << _iy << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};

//全局对象,声明周期大于main函数
//即main函数进入之前该对象就已经存在,在main函数退出之后该对象才会被销毁
Point g_pt(10,11); 


void test0(){
        //语句块,当我们将代码包进一个语句块时
        //此时它的作用域就仅剩这个范围
        //当这个范围结束时即Point对象就被销毁了
        {
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        }
        cout << "1111111111111" << endl;
}


int main(){
        cout << "enter main()" << endl;
        test0();
        cout << "exit main()" << endl;
}

编译运行:
在这里插入图片描述

可以看见全局对象是在main函数进入之前就已经执行了,然后在main函数退出之后才会调用其析构函数。

静态对象的调用时机:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): " << _ix << _iy << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};

//全局对象,声明周期大于main函数
//即main函数进入之前该对象就已经存在,在main函数退出之后该对象才会被销毁
//Point g_pt(10,11); 

//静态对象,因为也是全局静态区,所以自然生命周期也大于main函数
static Point s_pt(22,22);

void test0(){
        //语句块,当我们将代码包进一个语句块时
        //此时它的作用域就仅剩这个范围
        //当这个范围结束时即Point对象就被销毁了
        {
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        }
        cout << "1111111111111" << endl;
}


int main(){
        cout << "enter main()" << endl;
        test0();
        cout << "exit main()" << endl;
}

编译运行:
在这里插入图片描述
堆对象的调用时机:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): " << _ix << _iy << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};

//全局对象,声明周期大于main函数
//即main函数进入之前该对象就已经存在,在main函数退出之后该对象才会被销毁
//Point g_pt(10,11); 

//静态对象,因为也是全局静态区,所以自然生命周期也大于main函数
//static Point s_pt(22,22);

void test0(){
        //语句块,当我们将代码包进一个语句块时
        //此时它的作用域就仅剩这个范围
        //当这个范围结束时即Point对象就被销毁了
        {
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        }
        cout << "1111111111111" << endl;
		
		//堆对象
        Point* ppt = new Point(31,32);
        cout << "*ppt: " << endl;
        ppt->print();
}


int main(){
        cout << "enter main()" << endl;
        test0();
        cout << "exit main()" << endl;
}

编译运行:
在这里插入图片描述
可以看见11111111部分下面的才是我们的堆对象打印的消息,可以发现只调用了构造函数却没有析构函数,这是因为我们没有手动去释放堆空间。
加上delete:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): " << _ix << _iy << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};

//全局对象,声明周期大于main函数
//即main函数进入之前该对象就已经存在,在main函数退出之后该对象才会被销毁
//Point g_pt(10,11); 

//静态对象,因为也是全局静态区,所以自然生命周期也大于main函数
//static Point s_pt(22,22);

void test0(){
        //语句块,当我们将代码包进一个语句块时
        //此时它的作用域就仅剩这个范围
        //当这个范围结束时即Point对象就被销毁了
        {
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        }
        cout << "1111111111111" << endl;
		
		//堆对象
        Point* ppt = new Point(31,32);
        cout << "*ppt: " << endl;
        ppt->print();
		delete ppt;	
}


int main(){
        cout << "enter main()" << endl;
        test0();
        cout << "exit main()" << endl;
}

编译运行:
在这里插入图片描述
可以看见此时才调用了析构函数,所以执行delete表达式时也会自动调用析构函数。

还有最后一个局部静态变量的调用时机:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): " << _ix << _iy << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};

//全局对象,声明周期大于main函数
//即main函数进入之前该对象就已经存在,在main函数退出之后该对象才会被销毁
//Point g_pt(10,11); 

//静态对象,因为也是全局静态区,所以自然生命周期也大于main函数
//static Point s_pt(22,22);

void test0(){
        //语句块,当我们将代码包进一个语句块时
        //此时它的作用域就仅剩这个范围
        //当这个范围结束时即Point对象就被销毁了
        {
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        }
        cout << "1111111111111" << endl;
		
		//堆对象
        //Point* ppt = new Point(31,32);
        //cout << "*ppt: " << endl;
        //ppt->print();

        //delete ppt;
        
        //局部静态对象
        //在程序中第一次调用test0函数时,会创建该局部静态对象
        //之后再调用test0函数时就会直接使用而不再创建
        static Point pt2(44,44);
        pt2.print();

}


int main(){
        cout << "enter main()" << endl;
        test0();
        cout << "exit main()" << endl;
}

编译运行:
在这里插入图片描述
可以看见局部静态变量在退出了main函数也就是整个程序执行结束之后才会调用析构函数。

总结一下:
在这里插入图片描述

本类型对象的复制(拷贝)

对象的拷贝有两种情况:
1、对象未创建,通过拷贝的方式创建一个新对象
2、对象已经创建,要直接通过赋值语句来拷贝对象(面试常考)

下面来分情况讨论。

1、对象未创建时的情况

在这里插入图片描述

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int)" << endl;
        }


        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }

        //析构函数
        ~Point(){ cout << "~Point()" << endl;}

private:
        int _ix;
        int _iy;
};


void test0(){
        int a = 1;
        int b = a; //用已经创建的对象a去初始化尚未创建的b

        //同理,对于自定义类型对象Point我们也可以做到
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        cout << endl;

        Point pt2 = pt;
        cout << "pt2: " << endl;
        pt2.print();
}


int main(){
        test0();
}

编译运行:
在这里插入图片描述
可以看见对于自定义类型的对象我们也可以用已经创建的对象去初始化未创建的对象。
这说明系统给我们自动提供了一个拷贝构造函数,形式如下:

//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int)" << endl;
        }

        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }

        //能够使用已经创建对象去初始化尚未创建的对象的原因是
        //系统给我们默认自动提供了下面这个拷贝构造函数
        //这里要注意因为下面这个函数的参数正好是Point类型,就把他当作是在类内部,所以可以用.的方式访问
        //但如果是其它类型的话就绝对不行
        Point(const Point& rhs): _ix(rhs._ix), _iy(rhs._iy) {
                cout << "拷贝对象时自动调用了 Point(const Point& rhs) 拷贝构造函数!" << endl;
        }
        

        //析构函数
        ~Point(){ cout << "~Point()" << endl;}

private:
        int _ix;
        int _iy;
};

void test0(){
        int a = 1;
        int b = a; //用已经创建的对象a去初始化尚未创建的b

        //同理,对于自定义类型对象Point我们也可以做到
        Point pt(1,2);
        cout << "pt: " << endl;
        pt.print();
        cout << endl;
		
        Point pt2 = pt;
        //另一种自动调用拷贝构造的形式:Point pt2(pt);
        cout << "pt2: " << endl;
        pt2.print();
}


int main(){
        test0();
}

编译运行:
在这里插入图片描述
可以看见原来果然是提供了默认的拷贝构造函数。

拷贝构造参数可以改变吗

注意:拷贝构造函数的形式是固定的,不可以改变。

去掉&符号后果

在这里插入图片描述
去掉引用符号的感觉大致就是,当我们程序执行到Point pt2 = pt;时,因为这是一个已经创建的对象去拷贝未创建对象,所以会去调用拷贝构造函数,但是此时我们将拷贝构造函数的&符号给去掉了,在函数参数传递时本身就也是一次拷贝,即调用拷贝函数时:
const Point rhs = pt;
这很明显又是一次已经创建的对象去拷贝创建未创建对象呀,所以很自然的会再次调用拷贝构造函数,重复上述事情:
const Point rhs = pt;
这会一直持续陷入无限递归的情况,直到最后栈溢出程序崩溃结束。

去掉const后果

先来看一段代码:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): "<< _ix << _iy << endl;
        }

        Point(const Point& rhs): _ix(rhs._ix), _iy(rhs._iy){
                cout << "拷贝构造函数Point(const Point &)被调用啦" << endl;
        }


        void print(){
                cout << "(" << _ix << "," << _iy << ")"  << endl;
        }


        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};

Point func(){
        Point pt(11,12);
        pt.print();
        return pt;
}
void test0(){

        Point pt2 = func();
        cout << "pt2: " << endl;
        pt2.print();

}


int main(){

        test0();
}

这一段代码肯定是没有问题的,那么现在我们去掉拷贝构造函数中的const再运行。。。
离谱的事情发生了,我看老师写的代码都是会显示报错,但是我这里没有,多方查证最后还问了GPT-4:
在这里插入图片描述
只能说编译器现在还挺牛逼的。
反正记住拷贝构造函数肯定别改变其形式就可以。

补充一个知识点:

int a = 1;
int& ref = a; //这是没问题的
int& ref2 = 1; //这是错误的
//这是因为上面这个1是一个字面值常量,对1是无法取地址的
&1; //错误 无法对字面值常量取地址,那么也就无法使用引用绑定,所以这种值我们也称右值(它并没有存放在内存当中)
//而a是可以去取地址的,这种我们又称为左值
//可以取地址这意味着a的数据是存放在内存当中的
&a; 

所以对于这种字面值常量,我们想要用引用绑定的话,加上const关键字即可使一般引用成为常量引用即可绑定右值
const int& ref2 = 1; //正确

所以对于上面代码来说,func()函数返回的是一个临时变量,这个临时变量和1一样是个右值,是没有写进内存的跟个字面值常量性质差不多。
那么自然的执行代码 Point pt2 = func(); 在拷贝时调用拷贝函数会出现参数传递如: Point& rhs = func();
肯定会报错,因为func()返回是的个临时变量,是字面值常量,一般引用不能绑定到该值上,必须得是常量引用才行,所以为了解决这个问题我们就必须得给拷贝构造函数的参数加上const。
在这里插入图片描述

2、对象已经创建的情况(面试常考)

this指针

解释都在注释里啦:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): "<< _ix << _iy << endl;
        }

        Point(const Point& rhs): _ix(rhs._ix), _iy(rhs._iy){
                cout << "拷贝构造函数Point(const Point &)被调用啦" << endl;
        }

        //每一个成员函数都拥有一个隐含的this指针
        //this指针作为成员函数的第一个参数传入函数
        //但这个this参数并不需要我们手动添加,编译器会自动帮我们加上
        //this指针永远指向的是当前对象
        //所以成员函数隐含的效果如下
        void print(/* Point* const this */){
                //明显this是不能更改指向的,不然咋表示对象
                //this = 0x1000; 错误 this无法更改指向
                cout << "(" <<this-> _ix << "," <<this-> _iy << ")"  << endl;
        }
        

        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};
void test0(){

        Point pt1(1,2);
        cout << "pt1: ";
        //编译器给我们实现的隐含效果
        pt1.print(/* &pt1 */);

        Point pt2(3,4);
        cout << "pt2: ";
        pt2.print();

}


int main(){

        test0();
}

赋值运算符函数

代码示例,重点看test0()函数中的部分:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): "<< _ix << _iy << endl;
        }

        Point(const Point& rhs): _ix(rhs._ix), _iy(rhs._iy){
                cout << "拷贝构造函数Point(const Point &)被调用啦" << endl;
        }

        //每一个成员函数都拥有一个隐含的this指针
        //this指针作为成员函数的第一个参数传入函数
        //但这个this参数并不需要我们手动添加,编译器会自动帮我们加上
        //this指针永远指向的是当前对象
        //所以成员函数隐含的效果如下
        void print(/* Point* const this */){
                //明显this是不能更改指向的,不然咋表示对象
                //this = 0x1000; 错误 this无法更改指向
                cout << "(" <<this-> _ix << "," <<this-> _iy << ")"  << endl;
        }
        

        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};
void test0(){

        int a = 1, b=2;
        a = b; //赋值语句

        Point pt(1,2),pt2(3,4);

        pt2 = pt;//赋值语句

}


int main(){

        test0();
}

=就是赋值运算符,一般作用于内置类型(比如上面的int),但当我们使用=来作类与类之间的赋值时,会发现也是一样起作用的,这是因为在类中其实编译器给我们自动提供了一个赋值运算符函数:

#include <iostream>

using namespace std;


//自定义类
class Point{

public:
        Point(int ix = 0,int iy = 0): _ix (ix), _iy(iy){
                cout << "Point(int,int): "<< _ix << _iy << endl;
        }

        Point(const Point& rhs): _ix(rhs._ix), _iy(rhs._iy){
                cout << "拷贝构造函数Point(const Point &)被调用啦" << endl;
        }

		//赋值运算符函数
        //不写的话系统会自动提供一个
        //下面的是系统的默认实现
        Point& operator=(const Point& rhs){
                _ix = rhs._ix;
                _iy = rhs._iy;
                cout << "赋值运算符函数被运行啦" << endl;
                return *this;
        }


        //每一个成员函数都拥有一个隐含的this指针
        //this指针作为成员函数的第一个参数传入函数
        //但这个this参数并不需要我们手动添加,编译器会自动帮我们加上
        //this指针永远指向的是当前对象
        //所以成员函数隐含的效果如下
        void print(/* Point* const this */){
                //明显this是不能更改指向的,不然咋表示对象
                //this = 0x1000; 错误 this无法更改指向
                cout << "(" <<this-> _ix << "," <<this-> _iy << ")"  << endl;
        }
        

        //析构函数
        ~Point(){ cout << "析构函数~Point()被执行了哟" << endl;}

private:
        int _ix;
        int _iy;
};
void test0(){

        int a = 1, b=2;
        a = b; //赋值语句

        Point pt(1,2),pt2(3,4);

        pt2 = pt;//赋值语句,会调用赋值运算符函数,重载运算符的功能
		//所以它的等价替换如下
		pt2.operator=(pt);

}


int main(){

        test0();
}

编译运行:
在这里插入图片描述
可以看见确实赋值运算符被调用了。

注意拷贝构造函数与赋值运算符函数的使用区别:

Point pt2(pt); //此时因为pt2还没有创建完成,所以肯定是在用pt来初始化pt2,自然用的是拷贝构造函数

Point pt(1,2), pt2(3,4);
pt2 = pt; //这里两个对象都已经创建完毕,所以自然是在调用赋值运算符函数来完成赋值

在这里插入图片描述

有些时候默认的赋值运算符函数会有隐患,比如下面涉及的情况:
在这里插入图片描述
简单捋一下上面的问题:
Computer是我们自己设计的类,然后其中两个数据成员一个是brand一个是price。
其中brand是char*的数据类型,这意味着它的数据是存储在堆空间上的。
当我们按照系统默认的赋值运算符函数进行两个对象之间的赋值时,就会出现上图中的两个问题:
1、c1原来申请的空间没有得到释放造成了内存泄露(因为它在赋值运算符函数中被修改了指向,此时它指向c2的堆空间地址)
2、c1和c2对象现在所指向的空间相同,导致释放时该空间会被释放两次

解决方式也很简单,重写该赋值运算符函数,改变赋值的逻辑,让逻辑如下:
先回收C1对象的堆空间,就是原来左操作数的空间,然后进行深拷贝。

如图所示:
在这里插入图片描述
自复制是什么意思:就是c1对象自己赋值自己,如 c1 = c1;

三合成原则

在这里插入图片描述

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

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

相关文章

数据安全防护:云访问安全代理(CASB)

云访问安全代理&#xff08;Cloud Access Security Broker&#xff0c;CASB&#xff09;&#xff0c;是一款面向应用的数据防护服务&#xff0c;基于免应用开发改造的配置方式&#xff0c;提供数据加密、数据脱敏功能。数据加密支持国密算法&#xff0c;提供面向服务侧的字段级…

玄子Share- IDEA 2023 Web 热部署

玄子Share- IDEA 2023 热部署 添加热部署依赖 普通 Web 项目使用热部署插件需指定版本 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>3.1.3</version><!-…

nrm 安装教程(图文教程)

序&#xff1a; 1、都知道nvm解决的是node版本切换的问题&#xff0c;nrm 解决的是则是npm指向的问题。 2、雪狼的公众号&#xff1a;“程序员野区”&#xff0c;也许未来的莫一天&#xff0c;你会用到&#xff0c;不凡先进来瞅瞅公众号都分享了啥内容 正文&#xff1a; 1、安…

vue3+elementPlus:el-select选择器里添加按钮button

vue3elementPlus&#xff1a;el-select选择器里添加按钮button&#xff0c;在el-select的option后面添加button //html <el-select class"selectIcon" value-key"id" v-model"store.state.HeaderfilterText" multiple collapse-tagscollapse-…

力扣(LeetCode)2034. 股票价格波动(C++)

哈希表有序集合 请看本题解的分析&#xff1a; 题目的关键是四大操作&#xff0c;其中 current/maximum/minimum 明示我们&#xff0c;数据流应有快速找到一些数据的能力&#xff1a; 时间戳最大的股票所对应的价格&#xff0c;即题目所定义的最新股票价格在当前数据流节点&a…

Headless CMS(strapi)

Headless CMS(strapi) 玩了玩微信小程序的cms&#xff0c;感觉还挺好的&#xff0c;不过目前处于公测阶段&#xff0c;后续应该还是要收费的&#xff0c;不过这个操作还挺好的。文档地址 不过其获取图片的时候默认用到的是小城云开发环境的链接样式&#xff0c;如果用在公开网…

【Zookeeper专题】Zookeeper经典应用场景实战(二)

目录 前置知识课程内容一、Zookeeper分布式锁实战1.1 什么是分布式锁1.2 基于数据库设计思路1.3 基于Zookeeper设计思路一1.4 基于Zookeeper设计思路二1.5 Curator 可重入分布式锁工作流程1.6 总结 二、基于Zookeeper实现服务的注册与发现2.1 设计思路2.2 Zookeeper实现注册中心…

聊聊分布式架构04——RPC通信原理

目录 RPC通信的基本原理 RPC结构 手撸简陋版RPC 知识点梳理 1.Socket套接字通信机制 2.通信过程的序列化与反序列化 3.动态代理 4.反射 思维流程梳理 码起来 服务端时序图 服务端—Api与Provider模块 客户端时序图 RPC通信的基本原理 RPC&#xff08;Remote Proc…

算法通过村第十二关-字符串|黄金笔记|冲刺难题

文章目录 前言最长公共前缀纵向比较横向比较 字符串压缩问题表示数值的字符串总结 前言 提示&#xff1a;我有时候在想&#xff0c;我是真的不太需要其他人&#xff0c;还是因为跟他们在一起时没法自己&#xff0c;所以才保持距离。我们的交谈就像是平行而毫无交集的自言自语。…

高压功率放大器是什么东西

高压功率放大器是一种电子设备&#xff0c;用于将低功率信号放大到高功率水平。它通常由多个功率放大器组成&#xff0c;可以处理来自各种信号源的输入信号&#xff0c;并输出具有更大功率的信号。 高压功率放大器广泛应用于许多领域&#xff0c;包括无线通信、广播、雷达、医学…

Pytorch-学习记录-1-Tensor

1. 张量 (Tensor): 数学中指的是多维数组&#xff1b; torch.Tensor data: 被封装的 Tensor dtype: 张量的数据类型 shape: 张量的形状 device: 张量所在的设备&#xff0c;GPU/CPU requires_grad: 指示是否需要计算梯度 grad: data 的梯度 grad_fn: 创建 Tensor 的 Functio…

要多搞笑才能上B站热门?

自从B站开放竖屏模式之后&#xff0c;越来越多站外的优秀创作者加入B站。这其中搞笑UP主的数量大大增加&#xff0c;有很多竖屏UP主、短视频UP主在B站发光发热。 飞瓜数据&#xff08;B站版&#xff09;热门视频显示&#xff0c;近期热度最高的是来自UP主七颗猩猩QKXX发布的竖…

three.js点击模型实现模型边缘高亮选中效果

three.jsreact实现点击模型实现高亮选中效果 1、创建一个场景 let scene, camera, renderer, controls; let stats null; // 检测动画运行时的帧数 let clock new THREE.Clock(); // getDelta()方法获得两帧的时间间隔 let FPS 30; let renderT 1 / FPS; let timeS 0;con…

机器学习基础-数据分析:房价预测

mac设置中文字体 #要设置下面两行才能显示中文 Arial Unicode MS 为字体 plt.rcParams[font.sans-serif] [Arial Unicode MS] #设置图片大小 plt.figure(figsize(20, 11), dpi200)pie官方文档 总体代码 python import pandas as pd import numpy as np import matplotlib.…

Qt/C++原创推流工具/支持多种流媒体服务/ZLMediaKit/srs/mediamtx等

一、前言 1.1 功能特点 支持各种本地视频文件和网络视频文件。支持各种网络视频流&#xff0c;网络摄像头&#xff0c;协议包括rtsp、rtmp、http。支持将本地摄像头设备推流&#xff0c;可指定分辨率和帧率等。支持将本地桌面推流&#xff0c;可指定屏幕区域和帧率等。自动启…

【大数据 | 综合实践】大数据技术基础综合项目 - 基于GitHub API的数据采集与分析平台

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

在供应链管理中,如何做好库存分析?库存分析有哪些监控指标?

在供应链管理中&#xff0c;库存分析是其重要的一环。库存分析的方法繁杂且广泛&#xff0c;选择正确的方法才能更好的进行库存分析&#xff0c;下面就为大家盘点一些常用的库存分析方法和监控指标&#xff0c;全程干货&#xff0c;建议收藏&#xff01; 01 如何进行库存分析&…

【MySQL】基本查询(三)聚合函数+group by

文章目录 一. 聚合函数二. group by子句结束语 建立如下表 //创建表结构 mysql> create table exam_result(-> id int unsigned primary key auto_increment,-> name varchar(20) not null comment 同学姓名,-> chinese float default 0.0 comment 语文成绩,->…

08_selenium实战——学习平台公开数据批量获取

0、:前言 该实战任务是对某视频平台中’标题’、 ‘点赞数量’、 ‘投币数量’、‘收藏数量’、‘播放次数’、以及前五条评论进行爬取。要求1:可以控制爬取视频的主题(爬取主题搜索之后的内容)要求2:可以控制爬取视频的数量要求3:对于评论数不足5条的用0填充评论内容爬虫…

vue启动项目,npm run dev出现error:0308010C:digital envelope routines::unsupported

运行vue项目&#xff0c;npm run dev的时候出现不支持错误error:0308010C:digital envelope routines::unsupported。 在网上找了很多&#xff0c;大部分都是因为版本问题&#xff0c;修改环境之类的&#xff0c;原因是对的但是大多还是没能解决。经过摸索终于解决了。 方法如…