C++对象模型实验(clang虚函数表结构)

  摘要:本科期间有对比过msvc,gcc,clang的内存布局,距今已经6-7年了,当时还是使用的c++11。时间过得比较久了,这部分内容特别是内存对齐似乎C++17发生了一些变化,因此再实践下C++类模型。本文描述了C++不同类型的实际内存模型实现,主要关注虚函数表的具体内存布局。虽然clang,msvc都提供了对应的命令让我们直接查看类对象的内存布局,但是我们自己解析一下理解更深一点儿。
  关键字:c++,对象布局,clang++,msvc,g++

  测试环境:

  • Target: x86_64-unknown-linux-gnu
  • clang version 16.0.6 (https://github.com/llvm/llvm-project.git 7cbf1a2591520c2491aa35339f227775f4d3adf6)

  不同的编译器提供了不同的方式提取内存布局:

  • clang++ -cc1 -emit-llvm -fdump-record-layouts -fdump-vtable-layouts;
  • cl /d1 reportSingleClassLayout[class_name] [filename];
  • g++ -fdump-class-hierarchy

1 无虚函数类

1.1 简单对象

  即POD对象,对象中不存在虚函数,对象的内存仅仅有对象成员构成。C++的内存实现时表驱动,数据和函数分别存放在不同的位置,这里的SimpleClass同时包含了非静态成员,静态成员,非静态函数,静态函数。包含函数是为了更加直观的看到C++表驱动的实现方式,后续类只会包含虚函数,其他的函数就不会再列举。而类中给三个分别为不同类型的变量是为了查看内存对齐的策略,确认不同编译器对齐的策略是否不同。

class SimpleClass{
    friend std::ostream& operator<<(std::ostream &os, const SimpleClass &cls);
public:
    SimpleClass(){}
    ~SimpleClass(){}

    static void staticFunc(){}
    void nonStaticFunc(){}

    int _nonStaticIntMember{44};
    bool _nonStaticBoolMember{false};
    short _nonStaticShortMember{3};
    static int staticIntMember;
};

  下面是类中所有成员的地址,因为构造函数和析构函数比较特殊我们无法直接获取其函数指针。只能间接的访问栈当前函数ESP上一帧存储的指针即能获得当前调用函数的EIP,再根据固定的偏移获取当前函数的具体地址。下面的代码只能再clang++或者g++编译器上成功,msvc要另写,但是道理一样。

void* fetchRunningFuncAddress(){
    //!TODO:此处的代码不可移植
    uint64_t rbp{};
    asm("\t movq 8(%%rbp),%0" : "=r"(rbp));
    return (void*)(rbp - 0x11);
}
Simple Class:
size of class(bytes):                             8
align of class(bytes):                            4
address of SimpleClass::SimpleClass:              0x555555555934
address of class:                                 0x7fffffffd8b8
member address _nonStaticIntMember:               0x7fffffffd8b8
member address _nonStaticBoolMember:              0x7fffffffd8bc
member address _nonStaticShortMember:             0x7fffffffd8be
static function address staticIntMember:          0x555555558080
function addres staticFunc:                       0x5555555559d0
function addres nonStaticFunc:                    0x555555555a10
address of SimpleClass::~SimpleClass:             0x555555555a90

  根据上面的地址我们大概能够画出下面的类结构图,可以看到非静态数据都存储在栈空间而且是连续的,除了成员本身占用的内存,其中还有内存对齐需要的内存。而函数都存储在代码段中,该部分代码在内存中的映射位置和堆很近(好像是废话,堆本身就很大)。
在这里插入图片描述

555555554000-555555555000 r--p 00000000 103:02 13918339                  /home/are/workspace/code/projs/CppObjectModelExperiment/build/src/cpp_object_model_experiment
555555555000-555555556000 r-xp 00001000 103:02 13918339                  /home/are/workspace/code/projs/CppObjectModelExperiment/build/src/cpp_object_model_experiment
555555556000-555555557000 r--p 00002000 103:02 13918339                  /home/are/workspace/code/projs/CppObjectModelExperiment/build/src/cpp_object_model_experiment
555555557000-555555558000 r--p 00002000 103:02 13918339                  /home/are/workspace/code/projs/CppObjectModelExperiment/build/src/cpp_object_model_experiment
555555558000-555555559000 rw-p 00003000 103:02 13918339                  /home/are/workspace/code/projs/CppObjectModelExperiment/build/src/cpp_object_model_experiment
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]

1.2 单继承简单对象

class SimpleSingleInherit : public SimpleClass{
public:
    SimpleSingleInherit(){
    }

    ~SimpleSingleInherit(){
    }

    bool _ssiNonStaticBoolMem{false};
};

  单继承比较简单直接继承上面的SimpleClass即可。上面说了函数地址就是固定存储在代码区的,后续除了虚函数我们就不关注这些内容了。

address of SimpleClass::SimpleClass:                        0x555555555444
address of SimpleSingleInherit::SimpleSingleInherit:        0x555555555a85
address of class SimpleClass:                               0x55555556b2c0
size of class(bytes):                                       8
align of class(bytes):                                      4
member address _nonStaticIntMember:                         0x55555556b2c0
member address _nonStaticBoolMember:                        0x55555556b2c4
member address _nonStaticShortMember:                       0x55555556b2c6
static function address staticIntMember:                    0x555555558090
address of class SimpleSingleInherit:                       0x55555556b2c0
size of class(bytes):                                       12
align of class(bytes):                                      4
member address _ssiNonStaticBoolMem:                        0x55555556b2c8
address of SimpleSingleInherit::~SimpleSingleInherit:       0x555555555ce8
address of SimpleClass::~SimpleClass:                       0x555555555770

  从上面的输出中能够看出来类和基类都有独立的空间,子类的padding依然会保留。
在这里插入图片描述

  但是实际上这个padding空间似乎只是指当前类本身拥有的成员的需要padding的空间,比如下面的例子_ssiNonStaticBoolMem本身只占用一个字节空间不需要padding,因此后续ssiNonStaticBoolMem2紧跟前者的内存是合理的,最后的padding是编译器根据基类SimpleClass的对齐填充的padding。

class SimpleSingleInherit2 : public SimpleSingleInherit{
public:
    bool _ssiNonStaticBoolMem2{false};
};
![在这里插入图片描述](https://img-blog.csdnimg.cn/25b90e7837c44b64b27dd1bec7c8d253.png)

&emsp;&emsp;对于此类的内存空间我们应该只能有个概念,没有必要过多苛求,cppreference中对对齐的描述只是要求编译器对齐而已,具体怎么对齐似乎并没有规定。实际使用过程中应该以生产环境中的编译器实现为准。

## 1.3 多继承简单对象
```c
class SimpleClass2{
public:
    bool _nonStaticBoolMember{};
};

class SimpleMultiInherit : public SimpleClass, public SimpleClass2{
public:
    bool _ssiNonStaticBoolMem{false};
};
address of SimpleClass::SimpleClass:                        0x5555555553f4
address of SimpleClass2::SimpleClass2:                      0x5555555566f7
address of SimpleMultiInherit::SimpleMultiInherit:          0x555555556397
address of class SimpleClass:                               0x55555556c2c0
size of class(bytes):                                       8
align of class(bytes):                                      4
member address _nonStaticIntMember:                         0x55555556c2c0
member address _nonStaticBoolMember:                        0x55555556c2c4
member address _nonStaticShortMember:                       0x55555556c2c6
static function address staticIntMember:                    0x555555559090
address of class SimpleClass2:                              0x55555556c2c8
size of class(bytes):                                       1
align of class(bytes):                                      1
member address _nonStaticBoolMember:                        0x55555556c2c8
address of class SimpleMultiInherit:                        0x55555556c2c0
size of class(bytes):                                       12
align of class(bytes):                                      4
member address _ssiNonStaticBoolMem:                        0x55555556c2c9
address of SimpleMultiInherit::~SimpleMultiInherit:         0x555555556628
address of SimpleClass2::~SimpleClass:                      0x555555556770
address of SimpleClass::~SimpleClass:                       0x555555555720

  多重继承的类结构比较简单。
在这里插入图片描述

1.4 菱形继承对象

class SimpleClassLeft : public SimpleClass{
public:
    bool _simpleLeftBool;
};

class SimpleClassRight : public SimpleClass{
public:
    bool _simpleRightBool;
};

class SimpleDiamandInherit : public SimpleClassLeft, public SimpleClassRight{
public:
    bool _simpleDiamandBool;
};
address of SimpleClass::SimpleClass:                        0x5555555553f4
address of SimpleClassLeft::SimpleClassLeft:                0x555555556e8d
address of SimpleClass::SimpleClass:                        0x5555555553f4
address of SimpleClassRight::SimpleClassRight:              0x555555556f6d
address of SimpleDiamandInherit::SimpleDiamandInherit:      0x555555556b1f
address of class SimpleClass:                               0x55555556e2c0
size of class(bytes):                                       8
align of class(bytes):                                      4
member address _nonStaticIntMember:                         0x55555556e2c0
member address _nonStaticBoolMember:                        0x55555556e2c4
member address _nonStaticShortMember:                       0x55555556e2c6
static function address staticIntMember:                    0x55555555b090
address of class SimpleClassLeft:                           0x55555556e2c0
size of class(bytes):                                       12
align of class(bytes):                                      4
member address _simpleLeftBool:                             0x55555556e2c8
address of class SimpleClass:                               0x55555556e2cc
size of class(bytes):                                       8
align of class(bytes):                                      4
member address _nonStaticIntMember:                         0x55555556e2cc
member address _nonStaticBoolMember:                        0x55555556e2d0
member address _nonStaticShortMember:                       0x55555556e2d2
static function address staticIntMember:                    0x55555555b090
address of class SimpleClassRight:                          0x55555556e2cc
size of class(bytes):                                       12
align of class(bytes):                                      4
member address _simpleRightBool:                            0x55555556e2d4
address of class SimpleDiamandInherit:                      0x55555556e2c0
size of class(bytes):                                       24
align of class(bytes):                                      4
member address _simpleDiamandBool:                          0x55555556e2d5
address of SimpleDiamandInherit::~SimpleDiamandInherit:     0x555555556db8
address of SimpleClassRight::~SimpleClassRight:             0x555555557048
address of SimpleClass::~SimpleClass:                       0x555555555720
address of SimpleClassLeft::~SimpleClassLeft:               0x555555557118
address of SimpleClass::~SimpleClass:                       0x555555555720

  菱形继承只是语义层面上时菱形的,但是实际上相同类型的基类完全是不同的对象,各自在内存中都有独立的副本。只是比较反直觉的是类SimpleDiamandInherit的成员复用了SimpleClassRight最后padding的内存。
在这里插入图片描述

2 带虚函数类

  涉及虚函数的类结构我们会重点关注虚函数表,其他成员的内存不再重点关注。

2.1 简单对象

class VirtualClass{
public:
    VirtualClass(){ }
    virtual ~VirtualClass(){ }
    virtual void vfunc1(){
        std::cout<<"Virtual Class virtual function 1"<<std::endl;
    }

    virtual void vfunc2(){
        std::cout<<"Virtual Class virtual function 2"<<std::endl;
    }
    bool _vcBoolVal{};
};

  类结构比较简单,我们不会主动调用类中的虚函数,而是通过虚函数表主动解析虚函数表拿到函数指针调用。具体如下:

address of VirtualClass::VirtualClass:                      0x5555555575e0
address of class VirtualClass:                              0x55555556e2c0
size of class(bytes):                                       16
align of class(bytes):                                      8
member address _vcBoolVal:                                  0x55555556e2c8
pares vtptr from this function
current class's typeinfo name:12VirtualClass
call virtual function from virtual pointer table
Virtual Class virtual function 1
Virtual Class virtual function 2
address of VirtualClass::~VirtualClass:                     0x555555557840

  需要注意的是虚函数表的实现跟编译器有关,不同编译器实现不同,msvc虽然和clang等的实现大体一致,但是表项中的内容依然有区别。因此上面的实现仅供参考。
  当类中有虚函数时,类起始位置会存储一个虚函数表指针,该指针指向一个表格即虚函数表。虚函数表中存储了不仅仅是虚函数还有RTTI的信息(这个和编译器有关,如果编译器实现本身不再虚函数表中存储RTTI信息也是正常的)。上面的例子中类起始位置就是一个8byte的指针,然后是具体的类成员。
  虚函数表中的表项的数量并不是虚函数的数量,还有一些额外的表项。VirtualClass中直观能看到的有三个虚函数,但是表项中有5个成员,除了-1位置的typeinfo信息,多出一个表项。索引为1处也是一个函数指针,该函数指针时编译器生成的虚析构函数的包装器,大概的实现如下:

VirtualClass::~VirtualClass(VirtualClass *cls){
    cls->~VirtualClass();
}

  简单总结下就是,虚函数表中存储的内容为:

  • -1项存储RTTI;
  • 0项存储虚析构函数;
  • 1项存储虚析构函数的wrapper;
  • 2项开始便是用户声明的析构函数的指针。
    在这里插入图片描述

2.2 单继承对象

class VirtualSingleInherit : public VirtualClass{
public:
    virtual void vfunc1() override{
        std::cout<<"VirtualSingleInhert Class virtual function 1"<<std::endl;
    }

    virtual void vfunc3(){
        std::cout<<"VirtualSingleInhert Class virtual function 3"<<std::endl;
    }

    bool _vsiBoolValue{};
};

  

address of VirtualClass::VirtualClass:                      0x555555557650
address of VirtualSingleInherit::VirtualSingleInherit:      0x555555557b19
address of class VirtualClass:                              0x55555556f2c0
size of class(bytes):                                       16
align of class(bytes):                                      8
member address _vcBoolVal:                                  0x55555556f2c8
pares vtptr from this function
current class's typeinfo name:20VirtualSingleInherit
call virtual function from virtual pointer table
VirtualSingleInhert Class virtual function 1
Virtual Class virtual function 2
address of class VirtualSingleInherit:                      0x55555556f2c0
size of class(bytes):                                       16
align of class(bytes):                                      8
member address _vsiBoolValue:                               0x55555556f2c9
pares vtptr from this function
current class's typeinfo name:20VirtualSingleInherit
call virtual function from virtual pointer table
VirtualSingleInhert Class virtual function 1
Virtual Class virtual function 2
VirtualSingleInhert Class virtual function 3
address of VirtualSingleInherit::~VirtualSingleInherit:     0x555555557d94
address of VirtualClass::~VirtualClass:                     0x5555555578b0

  单继承类类本身的内存结构和不带虚函数相同,虚函数表有些不同,子类中重写的虚函数虚函数表项中会替换为重写的函数指针,而新增的虚函数会添加到虚函数的表的末尾。这也是为什么我们能够通过RTTI调用子类实际实现的虚函数的原因。
在这里插入图片描述

2.3 多继承对象

  这里对VirtualClass,VirtualClass2进行了简单的改造增加了各自单独的虚函数,方便观察虚函数结构的不同。

class VirtualClass{
public:
    virtual void vfunc1(){
        std::cout<<"[**] Virtual Class virtual function 1"<<std::endl;
    }

    virtual void vfunc2(){
        std::cout<<"[**] Virtual Class virtual function 2"<<std::endl;
    }

    virtual void vc1func(){
        std::cout<<"[**] Virtual Class virtual function vc1func"<<std::endl;
    }
    bool _vcBoolVal{};
};


class VirtualClass2{
public:
    virtual void vfunc1(){
        std::cout<<"[**] Virtual Class virtual function 1"<<std::endl;
    }

    virtual void vfunc2(){
        std::cout<<"[**] Virtual Class virtual function 2"<<std::endl;
    }

    virtual void vc2func(){
        std::cout<<"[**] Virtual Class virtual function vc2func"<<std::endl;
    }

    bool _vcBoolVal2{};
};

class VirtualMultiInherit : public VirtualClass, public VirtualClass2{
public:
    virtual void vfunc1() override{
        std::cout<<"[**] VirtualMultiInherit Class virtual function 1"<<std::endl;
    }
    
    virtual void vc1func() override{
        std::cout<<"[**] VirtualMultiInherit virtual function vc1func"<<std::endl;
    }

    virtual void vc2func() override{
        std::cout<<"[**] VirtualMultiInherit Class virtual function vc2func"<<std::endl;
    }

    virtual void vmifunc(){
        std::cout<<"[**] VirtualMultiInherit Class virtual function vmifunc"<<std::endl;
    }
    bool _vmiBoolValue{};
};
address of VirtualClass::VirtualClass:                      0x555555558600
address of VirtualClass2::VirtualClass2:                    0x5555555592c0
address of class VirtualClass:                              0x5555555712e0
size of class(bytes):                                       16
align of class(bytes):                                      8
member address _vcBoolVal:                                  0x5555555712e8
pares vtptr from this function
current class's typeinfo name:19VirtualMultiInherit
call virtual function from virtual pointer table
[**] VirtualMultiInherit Class virtual function 1
[**] Virtual Class virtual function 2
[**] VirtualMultiInherit virtual function vc1func
[**] VirtualMultiInherit Class virtual function vc2func
[**] VirtualMultiInherit Class virtual function vmifunc
address of class VirtualClass2:                             0x5555555712f0
size of class(bytes):                                       16
align of class(bytes):                                      8
member address _vcBoolVal2:                                 0x5555555712f8
pares vtptr from this function
current class's typeinfo name:19VirtualMultiInherit
call virtual function from virtual pointer table
[**] VirtualMultiInherit Class virtual function 1
[**] VirtualClass2 Class virtual function 2
[**] VirtualMultiInherit Class virtual function vc2func
address of class VirtualMultiInherit:                       0x5555555712e0
size of class(bytes):                                       32
align of class(bytes):                                      8
member address _vmiBoolValue:                               0x5555555712f9
pares vtptr from this function
current class's typeinfo name:19VirtualMultiInherit
call virtual function from virtual pointer table
[**] VirtualMultiInherit Class virtual function 1
[**] Virtual Class virtual function 2
[**] VirtualMultiInherit virtual function vc1func
[**] VirtualMultiInherit Class virtual function vc2func
[**] VirtualMultiInherit Class virtual function vmifunc
address of VirtualClass2::~VirtualClass2:                   0x555555559570
address of VirtualClass::~VirtualClass:                     0x555555558860

  多继承的情况要稍微复杂一点儿,多继承的情况下一个类有多少个基类就有多少个虚函数指针,如示例所示有两个虚基类的话就有两个虚函数指针。而对应虚函数表的地址就是基类的首地址,但是二者的内容大不相同。对于基类的同名虚函数,二则的虚函数表中的表项都会被替换成子类实现的虚函数指针,而如果子类中没有的虚函数则会被添加到第一个虚函数末尾,但也只是添加到了虚函数表的末尾,语法上基类访问子类的函数就不允许。

在这里插入图片描述

2.4 菱形虚继承对象

  菱形继承如果不用虚继承的话就是和多继承相同,只不过多个类在不同基类中有多个副本而已。

class VirtualDiamandLeft : virtual public VirtualClass{
public:
    virtual void vfunc1() override{
        std::cout<<"[**] VirtualDiamandLeft Class virtual function 1"<<std::endl;
    }

    bool _vdlBoolValue{};
};

class VirtualDiamandRight : virtual public VirtualClass{
public:
    virtual void vfunc2() override{
        std::cout<<"[**] VirtualDiamandRight Class virtual function 2"<<std::endl;
    }

    bool _vdrBoolValue{};
};

class VirtualMultiDiamand : public VirtualDiamandLeft, public VirtualDiamandRight{
public:
    virtual void vc1func() override{
        std::cout<<"[**] VirtualMultiDiamand Class virtual function vc1func"<<std::endl;
    }
    bool vmdBoolValue{};
};

  菱形虚继承,相比于直接继承会复用同一个对象,不存在多个基类对象的副本。因此菱形继承中会有多个虚函数指针,首先是基类有一个虚函数表指针,该表格中存放的虚函数指针都是thunk,即改变this偏移再跳转到对应的处理函数:

0000000000006300 <_ZTv0_n48_N19VirtualMultiDiamand7vc1funcEv>:
    6300:	55                   	push   %rbp
    6301:	48 89 e5             	mov    %rsp,%rbp
    6304:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
    6308:	48 8b 7d f8          	mov    -0x8(%rbp),%rdi
    630c:	48 8b 07             	mov    (%rdi),%rax
    630f:	48 8b 40 d0          	mov    -0x30(%rax),%rax
    6313:	48 01 c7             	add    %rax,%rdi
    6316:	5d                   	pop    %rbp
    6317:	e9 24 ff ff ff       	jmp    6240 <_ZN19VirtualMultiDiamand7vc1funcEv>
    631c:	0f 1f 40 00          	nopl   0x0(%rax)

在这里插入图片描述

  另外除了基类的虚函数表中不仅仅存储了对应的虚函数指针还存储了当前虚函数表相对于基类虚函数表的偏移,该数据存储在vptr[-3]的位置。

参考文献

  • cppreference-align

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker优点简介和yum方式安装

一.docker简介 二.docker的优点 1.交付和部署速度快 2.高效虚拟化 3.迁移性和扩展性强 4.管理简单 三.docker的基本概念 1.镜像 2.容器 3.仓库 四.docker的安装部署 &#xff08;1&#xff09;点击容器 ​&#xff08;2&#xff09;选择docker-ce&#xff0c;根据相…

[C语言]分支语句和循环语句

[C语言]分支语句和循环语句 文章目录 [C语言]分支语句和循环语句C语言语句分类分支语句if语法结构else的匹配规则switch语句switch语句中的breakswitch语句中default 循环语句while循环while循环中的break和continuefor循环for循环中的break和continuefor循环的变种do while循环…

【腾讯云Cloud Studio实战训练营】用Vue+Vite快速构建完成交互式3D小故事

&#x1f440;前置了解&#xff1a;(官网 https://cloudstudio.net/) 什么是Cloud Studio&#xff1f; Cloud Studio 是基于浏览器的集成式开发环境&#xff08;IDE&#xff09;&#xff0c;为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装&#…

refresh大揽

注意在每一步大操作之前都有一个前期准备 prepareRefresh&#xff08;&#xff09; 设置spring启动的时间 设置spring关闭和开启的标志位 获取环境对象&#xff0c;并设置一些属性值&#xff0c;是系统环境的不是xml设置的 设置监听器&#xff0c;以及需要发布事件的集合。 Con…

浅析Java设计模式之四策略模式

title: 浅析Java设计模式之四策略模式 date: 2018-12-29 17:26:17 categories: 设计模式 description: 浅析Java设计模式之四策略模式 1. 目录 1. 目录2. 概念 2.1. 应用场景2.2. 优缺点 2.2.1. 优点2.2.2. 缺点 3. 模式结构4. 样例 4.1. 定义策略4.2. 定义具体策略4.3. 定义…

【自动电压调节器】无功功率控制的终端电压控制研究(Simulink)

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

【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树

一、驱动程序分离的思想 【IMX6ULL驱动开发学习】05.字符设备驱动开发模板&#xff08;包括读写函数、poll机制、异步通知、定时器、中断、自动创建设备节点和环形缓冲区&#xff09;_阿龙还在写代码的博客-CSDN博客 之前编写驱动程序的代码存在不少弊端&#xff1a;移植性差…

Redis从基础到进阶篇(一)

目录 一、了解NoSql 1.1 什么是Nosql 1.2 为什么要使用NoSql 1.3 NoSql数据库的优势 1.4 常见的NoSql产品 1.5 各产品的区别 二、Redis介绍 2.1什么是Redis 2.2 Redis优势 2.3 Redis应用场景 2.4 Redis下载 三、Linux下安装Redis 3.1 环境准备 3.2 Redis的…

广告牌安全传感器,实时监测事故隐患尽在掌握

在现代城市中&#xff0c;广告牌作为商业宣传的重要媒介&#xff0c;已然成为城市中一道独特的风景线。然而&#xff0c;随着城市迅速发展&#xff0c;广告牌的安全问题也引起了大众关注。广告招牌一般悬挂于建筑物高处&#xff0c;量大面大。由于设计、材料、施工方法的缺陷&a…

windows电脑系统自带的画图工具如何实现自由拼图

1.首先选中你要拼接的第一张图片&#xff0c;右键选着编辑&#xff0c;会自动打开自带的画图工具 然后就是打开第一张图片&#xff0c;如下图所示 接着就是将画布托大&#xff0c;如下图所示。 然后点击选择&#xff0c;选择下面的空白区域&#xff0c;选着区域的范围要比准备拼…

企微配置回调服务

1、企微配置可信域名 2、企微获取成员userID 3、企微获取用户敏感数据 4、企微配置回调服务 文章目录 一、简介1、概述2、相关文档地址 二、企微配置消息服务器1、配置消息接收参数2、参数解析3、参数拼接规则 三、代码编写—使用已有库1、代码下载2、代码修改3、服务代码编写 …

阿里云轻量应用服务器和云服务器有什么区别?2023更新

阿里云轻量应用服务器和云服务器ECS有什么区别&#xff1f;ECS是专业级云服务器&#xff0c;轻量应用服务器是轻量级服务器&#xff0c;轻量服务器使用门槛更低&#xff0c;适合个人开发者或中小企业新手使用&#xff0c;可视化运维&#xff0c;云服务器ECS适合集群类、高可用、…

gor工具http流量复制、流量回放,生产运维生气

gor是一款流量复制回放工具&#xff0c;gor工具的官网&#xff1a;https://goreplay.org/ 1、对某个端口的http流量进行打印 ./gor --input-raw :8000 --output-stdout 2、对流量实时转发&#xff0c;把81端口流量转发到192.168.3.221:80端口 ./gor --input-raw :81--output-ht…

Qt+Pyhton实现麒麟V10系统下word文档读写功能

目录 前言1.C调用python1.1 安装Python开发环境1.2 修改Qt工程配置1.3 初始化Python环境1.4 C 调用Python 函数1.5 常用的Python接口 2.python虚拟环境2.1Python虚拟环境简介2.2 virtualenv 安装及使用2.3 在C程序中配置virtualenv 虚拟环境 3.python-docx库的应用4.总结 前言 …

基于YOLOv8模型和PCB电子线路板缺陷目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型PCB电子线路板缺陷目标检测系统可用于日常生活中检测与定位PCB线路板瑕疵&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检…

panda3d加载模型复习和python面向对象编程属性学习

运行一个python示例&#xff1b;然后去除一些代码&#xff0c;只剩下加载模型相关&#xff0c;如下&#xff1b; from panda3d.core import loadPrcFileData # Configure the parallax mapping settings (these are just the defaults) loadPrcFileData("", "p…

Vue.js知识点学习的一点笔记

一、虚拟DOM 1、原生JS是命令式编程&#xff0c;当渲染在页面的数据发生一点点变化&#xff0c;需要整个重新渲染一编。vue.js渐进式框架有个虚拟DOM的概念&#xff0c;运用diff算法&#xff0c;比较新旧数据&#xff0c;相同的数据不变不重渲染&#xff0c;不同的部分新数据覆…

python 连接Redis 数据库

pip install redis python代码 import redis# 连接数据库 r redis.Redis(host192.168.56.15, port6379, db0)# 存储数据 #r.set(key, value) r.set(name, zaraNet)# 获取数据 value r.get(name) print(value)# 关闭连接&#xff08;可选&#xff09; r.close()

spring boot 整合mongodb

1、安装依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>2、配置数据库连接 spring:data:mongodb:host: localhostport: 27017username: xxxxxxp…

.fargo后缀勒索病毒|勒索病毒解密恢复|fargo勒索病毒解密|勒索病毒解密恢复|数据库恢复

fargo勒索病毒概述&#xff0c;fargo勒索病毒解密恢复及日常防护建议 目录&#xff1a; fargo勒索病毒介绍感染fargo勒索病毒后的表现fargo勒索病毒的感染形式如何恢复.fargo后缀勒索病毒fargo勒索病毒日常防护建议 简介&#xff1a; 河北某有限公司的财务系统&#xff0c;由…