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

C++深入学习part_1

Linux下编译C++程序

安装g++命令:sudo apt install g++

编译命令:$ g++ *.cc 或者 *.cpp -o fileName;

hellworld

在这里插入图片描述

编译程序可以看到:
在这里插入图片描述

namespace命名空间

首先,命名空间的提出是为了防止变量重名冲突而设置的。
浅浅试一下:
在这里插入图片描述
现在我们进行编译的时候会发现报错:
在这里插入图片描述
从报错提示可以看到,他希望我们使用wd::display()的方式来调用该函数。(::称为作用域限定符)
这是因为display函数是定义在namespace命名空间里的,所以想要使用其内置成员的时候我们需要加上其对应的空间名:
在这里插入图片描述
再来编译:
在这里插入图片描述
完美运行通过。

命名空间还可以嵌套使用:
在这里插入图片描述
完美运行:
在这里插入图片描述
这是命名空间的一种使用方式,还有一种则是如下:
在这里插入图片描述
使用了using编译指令之后就可以不用再带对应的命名空间名了,因为上图中using编译指令会将std该空间的所有实体全部引入。

注意:第二种方式使用时必须知道该空间中有哪些实体,如果不知道,这样的写法就依然存在可能造成冲突的风险。

什么意思?
我们来试一下,这里使用std标准命名空间测试:
在这里插入图片描述
可以看见我们的cout函数具有二义性,因为std中也有一个该函数,编译会报错:
在这里插入图片描述
ambiguous:二义性。

所以初学时大型项目里面最好不要使用using编译指令,因为有可能造成冲突(你并不知道std中有多少函数)。

推荐使用using声明机制,即:using std::cout; 它只引入这一个实体。
另外在命名空间中直接定义的实体,不要直接缩进。

匿名命名空间

其实就是不带空间名就是匿名的命名空间,匿名命名空间可以直接使用其内部定义的实体。

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;}int main(void){cout << number << endl;return 0;
}

我们看存在的一种情况:

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;}int number = 5;int main(void){cout << number << endl;return 0;
}

此时不出意料肯定会报错,因为具有二义性:
在这里插入图片描述
所以我们为了强调我们用的是哪个number,就需要使用匿名空间的作用域操作符:

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;//而这个变量只能在本模块内部使用}int number = 5; //该全局变量可以跨模块使用,即可以在另一个.cpp文件中使用
//所谓模块:一个*.c/*.cc/*.cpp的文件就可以叫做一个模块//同理:
static int s_number = 5; //也只能在本模块内部使用int main(void){//使用作用域操作符来使用匿名命名空间cout << ::number << endl;return 0;
}

运行就没有问题了,因为我们强调了使用命名空间中的number:
在这里插入图片描述

跨模块调用extern关键字

当我们要跨模块调用另一个cpp文件中的变量或者函数时,需要使用extern关键字。
我们在hello.cpp文件中写上g_number = 100;

#include <iostream>
using namespace std;//等待被namespace1.cpp调用的变量
int g_number = 100;//这里要注意嗷,我们上面的所谓跨模块调用意思是这些模块本身就属于同一个项目
//而一个项目只能有一个main函数,所以这里我们注释掉hello.cpp中的main函数
//int main() {//      cout << "hello world" << endl;//    return 0;
//}

然后我们在namespace1.cpp文件中通过extern关键字来引用它:

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;//而这个变量只能在本模块内部使用}int number = 5; //全局变量可以跨模块使用,即可以在另一个.cpp文件中使用//使用hello.cpp文件中的g_number变量
extern int g_number;int main(void){//使用作用域操作符来使用匿名命名空间cout << ::number << endl;//打印g_numbercout << g_number << endl;return 0;
}

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

另外,在同一个模块中可以定义多次命名空间;在不同的模块中也可以定义多次命名空间:

#include <iostream>using namespace std;//第一次定义命名空间wd
namespace wd{void show(); //这里是第一次声明实体show()
}int main(){//调用wd中的show()wd::show();return 0;
}//这里我们第二次定义命名空间wd
namespace wd{//第二次定义实体showvoid show();
}//这里我们第三次定义命名空间wd
namespace wd{//第三次声明并且定义实体showvoid show(){cout << "我是第三次被声明啦" << endl;}
}

注意虽然命名空间可以随便声明,但是它里面的函数声明可是只能有一次定义嗷(就和正常的函数一样)。
编译运行:
在这里插入图片描述
不光是本文件可以重复声明命名空间,跨文件(或者说跨模块)也一样可以,这里我们在namespace3文件中定义一个同名wd:

#include<iostream>
using namespace std;//在namespace3.cpp文件中定义重名namespace wd
namespace wd
{void print(){cout << " 我是跨模块的命名空间嗷  " << endl;}
}

然后我们返回到刚刚的测试文件中去调用它:


#include <iostream>using namespace std;//在这里调用跨文件的namespace wd
namespace wd{void print();
}//第一次定义命名空间wd
namespace wd{void show(); //这里是第一次声明实体show()
}int main(){//调用wd中的show()//wd::show();//调用跨文件的namespace3.cpp中的wdwd::print();return 0;
}//这里我们第二次定义命名空间wd
namespace wd{//第二次定义实体showvoid show();
}//这里我们第三次定义命名空间wd
namespace wd{//第三次声明并且定义实体showvoid show(){cout << "我是第三次被声明啦" << endl;}
}

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

总结:
1、命名空间的提出是为了防止变量重名冲突而设置的,可以嵌套使用
2、去除了命名空间名就是所谓的匿名命名空间,匿名命名空间的实体无法跨模块调用。
3、在同一个模块中可以定义多次命名空间,在不同的模块中也可以定义多次命名空间。

const修饰类型和对象

const:修饰类型或对象成为常量值的关键字,常量值不可以改变且必须初始化。

#include <iostream>using namespace std;#define MAX 1000void test(){int a;//const int b; 必须要继续初始化,否则报错const int c = 1;//c = 2; error 常量是不能进行修改的//有同样效果的还有宏定义#definecout << MAX << endl;
}int main(){test();return 0;}

宏定义与const常量的区别(面试常考)

1、发生的时机不一样:
宏定义是在预处理时,而const常量是在编译时
2、类型检查不一样:
宏定义是没有类型检查的,只是简单做了字符串的替换,虽然也有编译阶段,但在编译阶段没有报错,将出错的时机推迟到了运行时,但运行时阶段的错误是更难发现的;而const是由类型检查的,这样更加安全一些

那么什么叫宏定义只是作了字符串的简单替换呢?
这里我们举例说明:

#include <iostream>using namespace std;//举例说明为什么宏定义只是进行了简单的字符串替换
#define kBase 3+4void test(){int a = 10;int d = a * kBase;cout << "d: " << d << endl;
}int main(){test();return 0;}

上面代码理想的值应该是得到10 * (3 + 4) = 70,但编译运行结果为:
在这里插入图片描述
我们可以用如下命令去查看预处理阶段的代码长什么样:
在这里插入图片描述
上面的constL.cpp和constL.i都是文件名,.i文件就是我们的预处理文件,打开它可以看见:
在这里插入图片描述

3+4被当作字符串一样直接替换了kBase,所以最后的结果就成了10*3+4-34。

总结:要定义常量时最好使用const或者枚举enum类型。

const修饰指针

# include <iostream>using namespace std;int main(){int a = 10;//这种形式是常量指针,表示p1所指向的a对象的值不可以改变//即p1也可以指向别人,如p1 = &b;//但(*p1) = 20; 企图修改a的值就是错误的,该值不可改变const int* p1 = &a;//int const* p2 = &a; 这种格式和上面p1指针是一个意思,且不怎么用//这种形式是指针常量,表示p3所指的地址值不可以改变//即p3不可以指向别人了,如p3 = &b; 就是错误的,指向不可改变//而(*p3) = 30; 这是可以的,其所指对象的值可以改变int* const p3 = &a;
}

C++堆空间申请方式以及内存泄露

C语言中申请堆内存空间使用的是malloc和free函数。
在C++中也有类似的方式:new表达式与delete表达式。

int * pint = new int(10); //new表达式申请空间的同时,也进行了初始化delete pint; //释放申请的堆空间

简单尝试:

#include <iostream>using namespace std;int main(){//new表达式执行完毕之后,返回的是相应类型的指针int* pint = new int(1);cout << "*pint  = " << *pint << endl;
}

运行编译:
在这里插入图片描述
但此时我们的代码是有问题的,因为没有释放掉我们的pint空间,即发生了内存泄漏。
那我们怎么检测我们的程序是否发生了内存泄露呢?
答案是使用一些内存检测工具,比较常用的如:valgrind
执行下面的命令下载它:
在这里插入图片描述

内存泄露检测工具-valgrind(面试高频考点)

下载完毕后我们执行以下操作:
在这里插入图片描述

上面的操作结束后现在我们就可以直接使用别名命令memcheck来检测内存泄露了,我们来检测一下我们刚刚的内存是否存在泄露:
在这里插入图片描述
从in use at exit:4bytes in 1 blocks等信息中可以看出,存在内存泄露问题,着就是内存检测工具valgrind的简单使用。

所以要记得回收内存啊!

关于new还有一种使用方式:

#include <iostream>using namespace std;int main(){//1、第一种new的使用方式//new表达式执行完毕之后,返回的是相应类型的指针int* pint = new int(1);cout << "*pint  = " << *pint << endl;delete pint;//2、第二种new的使用方式//new表达式要申请的空间为数组int* parr = new int[10];//注意数组的堆空间申请和释放的语法嗷,中括号别掉delete[] parr;}

引用

在这里插入图片描述

引用作为函数参数传递

首先回忆一下之前的几种参数传递方式:

1、值传递

还是使用经典的交换两个变量值的内容来作例子:
在这里插入图片描述
由上图可知,在使用值传递时,其实传递的仅仅是变量值的拷贝,而我们建立的在swap函数中定义的tmp值也不过是个临时变量,当swap函数执行结束后tmp变量也就随即消失了。所以值传递并不能实现交换两个值的内容,这是由于两个函数并不共享一块内存空间决定的,虽然它们都存在在栈空间内。
注意图中的箭头仅仅意味着进行了一个参数的拷贝而已,即a1的值拷贝给了变量x。

2、地址传递

在这里插入图片描述
地址传递就不一样了,上图明显可以看见指针px指向了a的地址,指针py指向了b的地址。
所以对两个指针解引用可以得到:*px = 1; *py = 2
在swap函数中,第20行代码tmp暂存了 *px的值,然后第二十一行中,px所指地址空间上的值被修改成了 *py的值,即 *px = 2;
第22行代码则将 *py的值变成了 1。
所以达到了我们想要交换两个变量值的目的:
在这里插入图片描述

3、引用传递

在这里插入图片描述
这就没啥好说的了,因为引用其实就是别名,所以操作x和y其实就是在直接操作a和b罢了。

引用的出现就是为了替代指针,尽量让程序员减少犯错的概率。
其底层实现依然是指针,而且是一个受限制的指针,即引用一旦被绑定到某个对象上后就不能够解绑去绑定到别的对象上。

引用作为函数的返回值

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

强制转换与函数重载

C风格强制转换:

TYPE a = (TYPE) EXPRESSION;

但这种风格存在缺陷,就是安全性不足,无法保证转换之后类型的合法性。

而C++风格的强制转换就不一样了

有四种:
1static_cast(最常用,比如常见的指针转换:把void*转换成其它类型的指针)2const_cast(去除常量属性)3dynamic_cast(动态类型转换,只用在多态时基类与派生类之间的转换)4reinterpret_cast(在任意类型之间轻易转换,但是不要轻易使用,用的最少)

上面四种转换方式只是含义不一样,但写法是通用的,形式都如下:
在这里插入图片描述

static_cast

这个没什么好讲的,就是正常用就型:

int* p = static_cast<int*> (malloc(sizeof(int)));

上面这一句代码就是一个很典型的应用,在C风格中malloc函数返回的是void*,如果要使该行代码不报错的话,就必须进行类型转换,那么此时用static_cast是非常合适的。

const_cast


#include <iostream>using namespace std;void display(int* p){ //明显要求传入一个非const指针*p = 10;cout << "*p = " << *p << endl;}int main(){const int a = 1;display(&a);//在实参传递时,只有const变量,如果传递成功的话就有修改a的值的风险}

如上面注释所说,这肯定是报错的:
在这里插入图片描述
那如果我们一定要传这个const常量参数呢?
那就可以用上const_cast了:

#include <iostream>using namespace std;void display(int* p){ //明显要求传入一个非const指针*p = 10;cout << "*p = " << *p << endl;}int main(){const int a = 1;//进行了去除const的强制类型转换display(const_cast<int*> (&a));}

现在再运行就没有问题了:
在这里插入图片描述
虽然我们使用了const_cast进行转换,但是我们并没有真正改变const变量的值,这一点要注意,即上面代码中的a的值是没有变化的。

而且更有意思的是,当我们打印指针p的值(即变量a的地址)的时候会发现,它的地址居然和常量a是一模一样的:

#include <iostream>using namespace std;void display(int* p){ //明显要求传入一个非const指针*p = 10;cout << "*p = " << *p << endl;cout << "p所指地址为: " << p << endl;
}int main(){const int a = 1;//进行了去除const的强制类型转换display(const_cast<int*> (&a));cout << "a的地址为: " << &a << endl;}

运行结果:
在这里插入图片描述
这就很扯:地址值是一样的,但是值不一样。

所以迷惑性很强,一般情况下最好不要用const_cast。(这里很多资料里面都没有一个明确的说法,据说是*p的值存在了所谓的寄存器中,并没有真正写入内存啥的,反正知道有这么回事就行了)

dynamic_cast和reinterpret_cast两个就不讲了,基本用不到

函数重载

在这里插入图片描述

C语言不支持函数重载!

在这里插入图片描述
在这里插入图片描述
由上图可以发现,确实对于不同的重载函数其实就是改变一下对应的名字来调用而已。add是函数名,然后add后面的ii就是参数列表中各个参数的缩写。

C++与C的混合编程

上一节我们知道了C++在内部是使用了名字改编的原理来支持函数重载的,但是C语言不支持函数重载自然也就没有所谓名字改编的操作,这就导致了C和C++在进行混合编程的时候会出现一些兼容问题:

在这里插入图片描述

为什么需要进行混合编程:很明显,C比C++早十二年出来,很多库都是C写的,C++只能去兼容和适应C的法则;

为了解决这样的问题,我们引入了下面的方式来解决兼容性问题:

在这里插入图片描述
上图右侧就是混合编程的编译结果,示例代码如下:

#include <iostream>using namespace std;//用C语言的方式来调用该函数
extern "C"{//只要放在该区域的代码,就会按照C的方式进行调用//不会进行名字改编int add(int x, int y){return x + y;}
}// end of extern "C"//下面这些重载函数都是按C++方式来进行调用的
long add(long x , long y){return x + y;
}int add (int x,int y,int z){return x + y + z;
}int add(int x,long y){return x + y;
}int add(long x, int y){return x + y;
}int main(){return 0;
}

上述这是 extern "C"声明在实现文件.cpp文件中的情况,但是如果是在头文件中情况又当如何呢?

在头文件中,文件是有可能被C编译器编译的,也有可能是被C++编译器编译的,自然的说C编译器肯定是不需要上面那段extern ''C"就能编译,会节省时间,而C++编译器则需要这段代码,如何做才能得到这样的效果?

答案是使用C++中的条件编译,宏定义:
在这里插入图片描述
所以在头文件中加上上述内容:

//宏_cplusplus只有C++的编译器才会定义
//C的编译器没有该宏
//意思就是只有该被包围起来的代码是被C++编译器编译时才会出现
//若是被C编译器编译的话就不会出现
#ifdef _cplusplus
extern "C"
{
#endifint add(int x,int y){return x + y;}
#ifdef _cplusplus
}
#endif

通过上述方法就可以完美解决C与C++的混合编程问题。

默认参数

在这里插入图片描述
这其实没啥好说的,就注意一下上面说的一个点:
默认参数的设置要求必须从右到左进行;
另外设置默认参数的时候要注意是否有其它的重载函数与其设置了默认参数的参数列表产生调用时的二义性就行。

inline函数

首先在C语言中,其实有类似的语法,函数宏定义:

#include <iostream>using namespace std;//C语言中的函数宏定义
#define multiply(x,y) x * yint main(){int a = 3, b = 4;int c = 5, d = 6;cout << multiply(a,b) << endl;//输出为12//但还是之前的问题,宏定义只是简单替换成了字符串//所以下面的语句其实是:a+b*c+d = 29cout << multiply(a+b,c+d) << endl;
}

编译运行:
在这里插入图片描述
接下来我们看在C++中有同样功能的inline函数:

#include <iostream>using namespace std;//C语言中的函数宏定义
#define multiply(x,y) x * y//C++中的inline函数
//为什么有inline函数:就是因为每次函数的调用都是绝对有开销的(比如栈空间的消耗)
//那么加上inline关键字的话,在编译时编译器会将该函数进行语句的替换
//下面的函数调用就会被替换成语句 x / y,极大的提升了效率
//它的效率与宏函数保持一致,还更加安全inline int divide(int x,int y){return x / y;
}int main(){int a = 3, b = 4;int c = 5, d = 6;cout << multiply(a,b) << endl;//输出为12//但还是之前的问题,宏定义只是简单替换成了字符串//所以下面的语句其实是:a+b*c+d = 29cout << multiply(a+b,c+d) << endl;//调用inline内联函数cout << divide(d,a) << endl;
}

为了降低犯错误的概率,尽量使用inline内联函数。

内联函数的使用要求

在这里插入图片描述

C++内存布局(面试常考)

在这里插入图片描述
上图是每一个进程被装载到内存中运行时的内存空间分布示意图,每个进程被装载到内存中运行时都会有如上几个区。
32位操作系统意思就是每一次读写数据的话只能读写32个位也就是四个字节,因为只有32根地址线来传送数据(所以32根地址线最大传送的数据就是当这32个位全为1的时候,最小就是当这三十二个位全为0的时候,这就决定了该类型操作系统的内存地址空间范围),那么2的32次方也就是4G大小的内存空间,其中一部分用来作OS的系统空间,即上图中的内核态,用来运行一些内核程序,而剩下的部分就是用户去区,也就是上图的用户态,用户进程(也就是我们所编写的程序)都会运行在用户态中。我们的C++程序也一样会运行在用户态里,只不过完整的程序根据其代码的不同会被分到不同的内存区域中,其中栈区总是位于虚拟地址的高位部分,向低地址方向进行生长,而堆区则在其下面由低地址向高地址生长,全局/静态区(或者说读写段)和只读段(或者说文字常量区和程序代码区)则依次往下存放。

接下来我们来一一验证,通过本次学习以后必须清楚自己写下的每一句代码中的数据是存储在哪个空间里的。

#include <iostream>using namespace std;int gNumber = 1;static long sNumber = 2;const int kNumber = 3;void test(){//对于使用指针声明的字符串,该字符串位于文字常量区,声明时应该加上const否则会有警告//所以正确的声明应该是:const char* pstr = "hello,world";char* pstr = "hello,world";//*ptr = 'H'; 错误,因为文字常量区是只读区域,所以从侧面反映了其确实位于文字常量区//该字符串位于栈上,相当于用"hello,world"字符串去初始化了这个字符数组char pstr2[] = "hello,world";int number = 1;const int number2 = 1;const int* const p = &number2;static int sLocalNumber = 10;//pint本身位于栈上int* pint = new int(1); //堆区delete pint;printf("pstr: %p\n",pstr);printf("&pstr: %p\n",&pstr);/*这里要注意辨析一下pstr2和&pstr2的区别,虽然它们俩打印出来的地址是一样的* pstr2是指该字符数组的首个元素的地址,即&pstr2[0]的地址,我们通过对其+1可以拿到&pstr2[1]的地址,也可以访问其元素* 而&pstr2的意思则是取整个字符数组的地址,也就是首个元素的地址* 但此时对&pstr2+1的话我们不会拿到第二个元素的地址,反而是会把整个字符数组当作第一个元素,然后去访问下一个字符数组* 的元素,也就是偏移的是整个数组的长度而不是偏移一个元素的长度*/printf("pstr2: %p\n",pstr2);printf("&pstr2: %p\n",&pstr2);//pstr2 = 0x11; 错误 数组名是一个常量,不能修改它的值printf("&gNumber: %p\n",&gNumber);//全局静态区printf("&sNumber: %p\n",&sNumber);//全局静态区printf("&number: %p\n",&number);//栈区printf("&number2: %p\n",&number2);//依然是放在栈上,因为该常量是定义在本函数内的,声明周期在本函数内printf("&kNumber: %p\n",&kNumber); //放在文字常量区,所谓文字常量意思是“字面常量“//包括数值常量、字符常量和符号常量printf("&sLocalNumber: %p\n",&sLocalNumber);//放在全局静态区//查看函数的地址//函数的名称即函数的入口地址存在于全局静态区,即程序存在它就存在//所以查看其地址时会发现函数地址和全局变量的地址相近//但是通过函数名去调用具体函数时就会在栈空间里了//所以函数内部的局部变量都是存放在栈空间上printf("&test: %p\n",&test);
}int main(){test();//查看main函数的地址printf("&main: %p\n",&main);}

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

自己可以对照着看看,加深一下理解,然后下面是对上面代码中提到的pstr2和*pstr2的区别图示:在这里插入图片描述

相关文章:

C++深入学习part_1

Linux下编译C程序 安装g命令&#xff1a;sudo apt install g 编译命令&#xff1a;$ g *.cc 或者 *.cpp -o fileName; hellworld 编译程序可以看到&#xff1a; namespace命名空间 首先&#xff0c;命名空间的提出是为了防止变量重名冲突而设置的。 浅浅试一下&#xff1…...

leetCode 300.最长递增子序列 (贪心 + 二分 ) + 图解 + 优化 + 拓展

300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff…...

Spring加载后置处理器方式之模板方法

Spring加载后置处理器方式之模板方法 1. 未使用模板方法时2. 使用模板方法后 1. 未使用模板方法时 public static void main(String[] args) {MyBeanFactory myBeanFactory new MyBeanFactory();myBeanFactory.getBean();}static class MyBeanFactory {public Object getBean(…...

【高性能计算】CUDA编程之OpenCV的应用(教程与代码-4)//test error

imread命令将返回以蓝色、绿色和红色&#xff08;BGR格式&#xff09;开头的三个通道 处理视频的main函数中需要做的第一件事是创建VideoCapture对象。 GPU CUDA模块中的函数都定义在cv::cuda命名空间中&#xff0c;将设备上配置给图像数据用的显存块作为其参数。 gettickcount…...

高德地图行政区域四级级联数据拉取;省市区县乡镇级联数据

高德地图行政区域四级级联数据拉取 高德地图行政区域级联选择 高德地图行政区域级联选择 使用以下代码拉取高德官方省市区县乡镇四级级联数据 function p(name/* 行政区域名称 */){return $.ajax({"url": "https://lbs.amap.com/_AMapService/v3/config/dis…...

Qt_基础

目录 1概述1.1 什么是QT1.2 QT的发展史1.3 支持的平台1.4 QT版本1.5 下载与安装1.6 QT的优点1.7 成功案例 2 创建 Qt 项目2.1 使用向导创建2.2 .pro文件2.3 帮助文档(QTcreator自带的)2.4 QT应用程序介绍 3 创建第一个小程序3.1 按钮的创建3.1.1 设置主窗口标题的函数3.1.2 **固…...

最新AI创作系统源码ChatGPT网站源码V2.6.3/支持Midjourney绘画/支持OpenAI GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统&#xff0c;支持OpenAI GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Chat…...

UML建模语言分析和设计

UML&#xff08;Unified Modeling Language&#xff0c;统一建模语言&#xff09;是一种用于软件系统分析、设计和实现的标准化建模语言。UML提供了多种图形化工具&#xff0c;用于描述系统的不同方面&#xff0c;包括用例、类、对象、状态、活动和序列等。 在软件开发中&…...

SystemUI导航栏

SystemUI导航栏 1、系统中参数项1.1 相关开关属性2.2 属性设置代码 2、设置中设置“三按钮”导航更新流程2.1 属性资源覆盖叠加2.2 SystemUI导航栏接收改变广播2.3 SystemUI导航栏布局更新2.4 时序图 android13-release 1、系统中参数项 1.1 相关开关属性 设置->系统->…...

3d 贴图下载quixel

Quixel Megascans https://polyhaven.com/a/studio_small_03 Quixel Bridge&#xff1a;3D艺术家的宝库 在3D建模和渲染的世界中&#xff0c;找到高质量、适合项目的贴图素材至关重要。Quixel Bridge就是这样一个为3D艺术家提供大量免费贴图素材的资源库。在本文中&#xff…...

Linux权限维持

SSH 后门 软链接sshd 目标主机建立软连接&#xff1a; ln -sf /usr/sbin/sshd /tmp/su;/tmp/su -oport1189 #端口可以任意指定&#xff0c;最好伪装一下 查看端口&#xff1a; netstat -anlp|grep 1189 攻击机ssh登录&#xff1a; ssh rootx.x.x.x -p 1189 #如果root用户…...

互联网通信的核心协议HTTP和HTTPS

HTTP&#xff1a;超文本传输协议 HTTP&#xff0c;全称为超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff09;&#xff0c;是一种用于在Web上传输超文本文档的协议。它是Web通信的基础&#xff0c;允许浏览器与Web服务器之间的数据交换。HTTP使用了经典的客户…...

javaWeb网上购物系统的设计与实现

摘 要 随着计算机网络技术的飞速发展和人们生活节奏的不断加快&#xff0c;电子商务技术已经逐渐融入了人们的日常生活当中&#xff0c;网上商城作为电子商务最普遍的一种形式&#xff0c;已被大众逐渐接受。因此开发一个网上商城系统&#xff0c;适合当今形势&#xff0c;更加…...

MySQL 主从复制、读写分离

MySQL 主从复制、读写分离 1、MySQL 主从复制1.1什么是主从复制&#xff1f;1.2为什么要读写分离呢&#xff1f;1.3 什么时候要读写分离&#xff1f;1.4主从复制与读写分离1.5mysql支持的复制类型1.6主从复制的工作过程1.7MySQL 读写分离原理1.8目前较为常见的 MySQL 读写分离分…...

基于虚拟阻抗的下垂控制——孤岛双机并联Simulink仿真

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

windows内核编程(2021年出版)笔记

1. Windows内部概览 1.1 进程 进程包含以下内容&#xff1a; 可执行程序&#xff0c;代码和数据私有的虚拟地址空间&#xff0c;分配内存时从这里分配主令牌&#xff0c;保存进程默认安全上下文&#xff0c;进程中的线程执行代码时会用到它私有句柄表&#xff0c;保存进程运…...

时序预测 | MATLAB实现EMD-iCHOA+GRU基于经验模态分解-改进黑猩猩算法优化门控循环单元的时间序列预测

时序预测 | MATLAB实现EMD-iCHOAGRU基于经验模态分解-改进黑猩猩算法优化门控循环单元的时间序列预测 目录 时序预测 | MATLAB实现EMD-iCHOAGRU基于经验模态分解-改进黑猩猩算法优化门控循环单元的时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 EMD-iCHOAGR…...

FFmpeg 命令:从入门到精通 | FFmpeg 解码流程

FFmpeg 命令&#xff1a;从入门到精通 | FFmpeg 解码流程 FFmpeg 命令&#xff1a;从入门到精通 | FFmpeg 解码流程流程图FFmpeg 解码的函数FFmpeg 解码的数据结构补充小知识 FFmpeg 命令&#xff1a;从入门到精通 | FFmpeg 解码流程 本内容参考雷霄骅博士的 FFmpeg 教程。 流…...

连接虚拟机工具推荐

连接虚拟机工具推荐 连接虚拟机的工具有很多种&#xff0c;以下是一些常用的推荐&#xff1a; PuTTY&#xff1a;这是一个非常常用的SSH和telnet客户端&#xff0c;适用于Windows系统。它允许你在本地机器上通过命令行接口远程登录到虚拟机。 SecureCRT&#xff1a;这是一个支…...

万字详解HTTP协议面试必备技能

目录 一、HTTP 是什么 二、理解 "应用层协议" 2.1理解 HTTP 协议的工作过程 2.2HTTP 协议格式 2.3抓包工具的使用 2.4抓包工具的原理 2.5抓包结果 2.5.1HTTP请求 2.5.2HTTP响应 2.6协议格式总结 三、HTTP 请求 (Request) 3.1认识 URL 3.1.1URL 基本格式 …...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...