【Visual C++】游戏开发笔记二十八 最精简的Direct3D11 Demo筋骨脉络全攻略

本文主要是介绍【Visual C++】游戏开发笔记二十八 最精简的Direct3D11 Demo筋骨脉络全攻略,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇文章将讲解如何通过我们在之前的文章里面已掌握的DirectX 11的知识,来一步一步创建一个基于Direct3D11的Blank Windows Demo,而我们在这节里面完成的这个demo,将在后面的文章里面作为一个模板,用于演示之后的各种新奇DirectX11的功能。


 

首先呢,为了代码的重用性着想,我们会写一个 DirectX11DemoBase,并借鉴在笔记二十六中的Win32风格的Blank Win32 Window Demo中的代码,然后通过派生的方式,以及重载一些必要的虚函数,进行整合,来创建我们的demo。


      



一、  关于代码书写风格的讨论


 

首先,我们提出一个问题,采用自问自答的形式来讨论:


在这个demo的设计过程中,我们是采用C语言式的全局变量与全局函数的搭档模式来完成,还是采用C++式的面向对象风格的类Class来编写呢?

 

答案是后者,采用面向对象的思想来完成至于这个问题的解释,答案就仁者见仁智者见智了。

浅墨之前看过一本C++界的名著《C++沉思录》,作者在文章的开篇举了一个例子,然后通过例子对比出来的效果,折射出了对C与C++的一个中肯的评价,是这样的一段话:


“C++鼓励采用类来表示类似于输出流的事物,而类就提供了一个理想的位置来存放状态信息。而C语言倾向于不存储状态信息,除非事先已经规划妥当。因此C程序员趋向于假设有这样一个“环境”:存在一个位置集合,他们可以在其中找到系统的当前状态。如果只有一个环境和一个系统,这样考虑毫无问题,但是,系统在不断增长的过程中往往需要引入某些独一无二的东西,并且创建更多这类东西。”


对这段话的解释,浅墨还是用自己的话来叙述吧:

通常我们采用一般的变量作为传递数据的容器,但是随着程序的复杂会导致数据量的加大,有太多的数据需要被传递,而且这些数据基本上都是需要传递到近乎是每一个函数当中的,这样我们就要创建很多的全局变量作为“容器”,如此下去我们的设计的程序只会越来越臃肿,越来越乱。别怕别怕,有绝招呢——我们可以创建一个类或者结构体来收容这些对象,使之显得不是那么乱,取而代之的是井井有条。


绕了这么大一圈子,一言以蔽之,就是运用全局变量是不太好的编程习惯,我们应当少用甚至不用,转而使用“类”来完成这些任务。

 

 



 

二、    Dx11DemoBase类的设计


 

作为目前来说,我们要求本节的demo做到以下几点功能:

 

▲初始化D3D

▲释放在启动过程中创建的Direct3D对象

▲为我们的D3D对象存储成员变量

▲提供一个装载demo的具体内容的方式

▲提供一个卸载demo的具体内容的方式

▲能够显示demo每帧的更新的具体内容

▲demo渲染内容的具体代码

 

由我们上面的清单来看,创建一个公共的初始化和卸载函数,用于装载和卸载内容功能的虚函数,以及渲染和更新游戏循环步骤的虚函数的基类是很有必要的。通过将这些函数设为虚函数,由基类派生出来的demo类能够实现他们自定义的逻辑和行为。

根据上面的这些叙述,我们可以写出下面的这段为Dx11DemoBase量身打造的代码:

 

 

代码段一  Dx11DemoBases类的头文件

 

[cpp] view plain copy print ?
  1. #ifndef _DEMO_BASE_H_  
  2. #define _DEMO_BASE_H_  
  3. #include<d3d11.h>  
  4. #include<d3dx11.h>  
  5. #include<DxErr.h>  
  6. class Dx11DemoBase  
  7. {  
  8. public:  
  9.    Dx11DemoBase();  
  10.    virtual ~Dx11DemoBase();  
  11.    bool Initialize( HINSTANCE hInstance, HWND hwnd );  
  12.    void Shutdown( );  
  13.    virtual bool LoadContent( );  
  14.    virtual void UnloadContent( );  
  15.    virtual void Update( float dt ) = 0;  
  16.    virtual void Render( ) = 0;  
  17.    protected:  
  18.    HINSTANCE hInstance_;  
  19.    HWND hwnd_;  
  20.    D3D_DRIVER_TYPE driverType_;  
  21.    D3D_FEATURE_LEVEL featureLevel_;  
  22.    ID3D11Device* d3dDevice_;  
  23.    ID3D11DeviceContext* d3dContext_;  
  24.    IDXGISwapChain* swapChain_;  
  25.    ID3D11RenderTargetView* backBufferTarget_;  
  26. };  
  27. #endif  




 

上面这段代码中我们可以看到最精简的D3D对象,以protected类成员的形式存在于类之中。在类体外初始化变量是比较好的编程习惯,而且效率比让先调用拷贝构造函数,再调用默认构造函数要高得多。

Dx11DemoBase类构造函数,析构函数,装载内容,卸载内容,shutdown函数定义如下:

 



代码段二 一些Dx11DemoBase 组成代码


[cpp] view plain copy print ?
  1. #include"Dx11DemoBase.h"  
  2.   
  3. Dx11DemoBase::Dx11DemoBase( ) : driverType_( D3D_DRIVER_TYPE_NULL),  
  4. featureLevel_( D3D_FEATURE_LEVEL_11_0 ), d3dDevice_( 0 ),d3dContext_( 0 ),  
  5. swapChain_( 0 ), backBufferTarget_( 0 )  
  6. {  
  7.   
  8. }  
  9.   
  10. void Dx11DemoBase::UnloadContent( )  
  11. {  
  12. //可以在此处进行重载,加入代码实现相关功能  
  13.   
  14.   
  15.   
  16. void Dx11DemoBase::Shutdown( )  
  17. {  
  18.    UnloadContent( );  
  19.    if( backBufferTarget_ ) backBufferTarget_->Release( );  
  20.    if( swapChain_ ) swapChain_->Release( );  
  21.    if( d3dContext_ ) d3dContext_->Release( );  
  22.    if( d3dDevice_ ) d3dDevice_->Release( );  
  23.    d3dDevice_ = 0;  
  24.    d3dContext_ = 0;  
  25.    swapChain_ = 0;  
  26.    backBufferTarget_ = 0;  
  27. }  



      

Dx11DemoBase类中的最后一个函数是Initialize函数。Initialize函数执行我们在这章中讲到的D3D初始化工作。这个函数开始声明我们的硬件,WARP或者软件的驱动类型,和我们的D3D11.0,10.1或者10.0的特征等级。代码的设定即尝试在D3D 11中创建一个硬件设备。如果创建失败,我们会尝试其他的驱动类型和特征等级直到我们找到一个合适的类型。这也意味着如果我们采用D3D10硬件我们可以也可以在硬件中运行这个demo,因为我们可以选择10.1或者10.0的特征等级。

下一步便是创建交换链的描述,以及使用这些信息来试着找到支持的设备类型和特征等级。如果成功的搜索到了我们需要的这些信息。接下来就是行云流水地创建渲染目标,创建视口,以及调用LoadContent方法加载特定的内容了。需要指出的是,LoadContent方法最好留着最后进行调用,以免出现不必要的错误。

下面便是DirectX11初始化的全过程:

 



代码段三 Dx11DemoBase类的初始化函数

 

[cpp] view plain copy print ?
  1. bool Dx11DemoBase::Initialize( HINSTANCE hInstance, HWND hwnd )  
  2. {  
  3.     hInstance_ =hInstance;  
  4.     hwnd_ = hwnd;  
  5.    
  6.     RECT dimensions;  
  7.     GetClientRect( hwnd,&dimensions );  
  8.    
  9.     unsigned int width =dimensions.right - dimensions.left;  
  10.     unsigned int height =dimensions.bottom - dimensions.top;  
  11.    
  12.     D3D_DRIVER_TYPEdriverTypes[] =  
  13.     {  
  14.        D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,  
  15.        D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_SOFTWARE  
  16.     };  
  17.    
  18.     unsigned inttotalDriverTypes = ARRAYSIZE( driverTypes );  
  19.    
  20.     D3D_FEATURE_LEVELfeatureLevels[] =  
  21.     {  
  22.        D3D_FEATURE_LEVEL_11_0,  
  23.        D3D_FEATURE_LEVEL_10_1,  
  24.        D3D_FEATURE_LEVEL_10_0  
  25.     };  
  26.    
  27.     unsigned inttotalFeatureLevels = ARRAYSIZE( featureLevels );  
  28.    
  29.     DXGI_SWAP_CHAIN_DESCswapChainDesc;  
  30.     ZeroMemory(&swapChainDesc, sizeof( swapChainDesc ) );  
  31.    swapChainDesc.BufferCount = 1;  
  32.    swapChainDesc.BufferDesc.Width = width;  
  33.    swapChainDesc.BufferDesc.Height = height;  
  34.    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;  
  35.    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;  
  36.    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;  
  37.    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;  
  38.    swapChainDesc.OutputWindow = hwnd;  
  39.     swapChainDesc.Windowed= true;  
  40.    swapChainDesc.SampleDesc.Count = 1;  
  41.     swapChainDesc.SampleDesc.Quality= 0;  
  42.    
  43.     unsigned intcreationFlags = 0;  
  44.    
  45. #ifdef _DEBUG  
  46.     creationFlags |=D3D11_CREATE_DEVICE_DEBUG;  
  47. #endif  
  48.    
  49.     HRESULT result;  
  50.     unsigned int driver =0;  
  51.    
  52.     for( driver = 0;driver < totalDriverTypes; ++driver )  
  53.     {  
  54.         result =D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver], 0, creationFlags,  
  55.                                                featureLevels, totalFeatureLevels,  
  56.                                                D3D11_SDK_VERSION, &swapChainDesc, &swapChain_,  
  57.                                                &d3dDevice_, &featureLevel_, &d3dContext_ );  
  58.    
  59.         if( SUCCEEDED(result ) )  
  60.         {  
  61.             driverType_ =driverTypes[driver];  
  62.             break;  
  63.         }  
  64.     }  
  65.    
  66.     if( FAILED( result ) )  
  67.     {  
  68.         DXTRACE_MSG("创建 Direct3D 设备失败!" );  
  69.         return false;  
  70.     }  
  71.    
  72.     ID3D11Texture2D*backBufferTexture;  
  73.    
  74.     result =swapChain_->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID*)&backBufferTexture );  
  75.    
  76.     if( FAILED( result ) )  
  77.     {  
  78.         DXTRACE_MSG("获取交换链后台缓存失败!" );  
  79.         return false;  
  80.     }  
  81.    
  82.     result =d3dDevice_->CreateRenderTargetView( backBufferTexture, 0,&backBufferTarget_ );  
  83.    
  84.     if( backBufferTexture)  
  85.        backBufferTexture->Release( );  
  86.    
  87.     if( FAILED( result ) )  
  88.     {  
  89.         DXTRACE_MSG("创建渲染目标视图失败!" );  
  90.         return false;  
  91.     }  
  92.    
  93.    d3dContext_->OMSetRenderTargets( 1, &backBufferTarget_, 0 );  
  94.    
  95.     D3D11_VIEWPORTviewport;  
  96.     viewport.Width =static_cast<float>(width);  
  97.     viewport.Height =static_cast<float>(height);  
  98.     viewport.MinDepth =0.0f;  
  99.     viewport.MaxDepth =1.0f;  
  100.     viewport.TopLeftX =0.0f;  
  101.     viewport.TopLeftY =0.0f;  
  102.    
  103.    d3dContext_->RSSetViewports( 1, &viewport );  
  104.    
  105.     return LoadContent( );  
  106. }  
  107.    
  108.    
  109. bool Dx11DemoBase::LoadContent( )  
  110. {  
  111.     //可以进行重载来丰富相关功能  
  112.     return true;  
  113. }  
  114.    
  115.    
  116. void Dx11DemoBase::UnloadContent( )  
  117. {  
  118.     //可以进行重载来丰富相关功能  
  119. }  
  120.    
  121.    
  122. void Dx11DemoBase::Shutdown( )  
  123. {  
  124.     UnloadContent( );  
  125.    
  126.     if( backBufferTarget_) backBufferTarget_->Release( );  
  127.     if( swapChain_ )swapChain_->Release( );  
  128.     if( d3dContext_ )d3dContext_->Release( );  
  129.     if( d3dDevice_ )d3dDevice_->Release( );     
  130.    
  131.     backBufferTarget_ = 0;  
  132.     swapChain_ = 0;  
  133.     d3dContext_ = 0;  
  134.     d3dDevice_ = 0;  
  135. }  






三、BlankDx11Demo类的设计



 

万事具备,只欠东风。

下面我们便从上面写的Dx11DemoBase类里派生出一个叫BlankDx11Demo的类。

以下就是 BlankDx11Demo类头文件的代码:


代码段四 BlankDx11Demo 类的头文件

 

[cpp] view plain copy print ?
  1. #ifndef _BLANK_DEMO_H_  
  2. #define _BLANK_DEMO_H_  
  3. #include"Dx11DemoBase.h"  
  4.   
  5. class BlankDx11Demo : public Dx11DemoBase  
  6. {  
  7. public:  
  8.     BlankDx11Demo( );  
  9.     virtual ~BlankDx11Demo( );  
  10.     bool LoadContent( );  
  11.     void UnloadContent( );  
  12.     void Update( float dt );  
  13.     void Render( );  
  14. };  
  15. #endif  





这段代码中可以看到。叫做Update的函数中取了一个叫做dt的变量,后面将更详细地剖析这个变量,目前我们按这样理解就好了:在游戏程序中我们经常需要进行实时的游戏逻辑更新,而dt用于代表最后一帧的时间到当前时间的时间差,这个时间差记录我们用dt记录了下来,便于我们的基于时间的更新操作。

由于这个只是一个骨架式的空DirectXDemo,以尽量精简易懂作为此Demo的宗旨,以便于大家更容易地理解一个DirectX 11 Demo的筋骨脉络,所以在这里只是只进行了一个清屏的操作,且所有的函数重载都是空的。Render函数中我们也就调用了两个Direct3D的函数:ClearRenderTargetView函数用于清除屏幕上指定的颜色,Present函数用于显示新渲染的场景。

 



代码段五BlankDx11Demo类的源文件

 

[cpp] view plain copy print ?
  1. #include"BlankDx11Demo.h"  
  2.   
  3. BlankDx11Demo::BlankDx11Demo( )  
  4. {  
  5. }  
  6.   
  7. BlankDx11Demo::~BlankDx11Demo( )  
  8. {  
  9. }  
  10.   
  11. bool BlankDx11Demo::LoadContent( )  
  12. {  
  13.     return true;  
  14. }  
  15.   
  16. void BlankDx11Demo::UnloadContent( )  
  17. {  
  18. }  
  19.   
  20. void BlankDx11Demo::Update( float dt )  
  21. {  
  22. }  
  23.   
  24. void BlankDx11Demo::Render( )  
  25. {  
  26. if( d3dContext_ == 0 )  
  27. return;  
  28. float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };  
  29. d3dContext_->ClearRenderTargetView( backBufferTarget_,clearColor );  
  30. swapChain_->Present( 0, 0 );  
  31. }  







 

四、  赋予程序生命——wWinMain函数的书写



 

之前我们创建的这些类都只是一个躯壳,并没有生命,而现在我们会将今天我们创建的这个主角赋予生命。最后一步就是在工程中修改并添加我们在笔记二十六中提出的Blank Win32 Window demo中的wWinMain函数以及余下的功能函数,使我们今天设计出的这个demo浑然一体。以下就是最后需要的源码:




代码段六 main.cpp的完整源代码

  

[cpp] view plain copy print ?
  1. #include<Windows.h>  
  2. #include<memory>  
  3. #include"BlankDx11demo.h"  
  4.    
  5.    
  6. LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAMwParam, LPARAM lParam );  
  7.    
  8.    
  9. int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE prevInstance,LPWSTR cmdLine, int cmdShow )  
  10. {  
  11.    UNREFERENCED_PARAMETER( prevInstance );  
  12.    UNREFERENCED_PARAMETER( cmdLine );  
  13.    
  14.     WNDCLASSEX wndClass ={ 0 };  
  15.     wndClass.cbSize =sizeof( WNDCLASSEX ) ;  
  16.     wndClass.style =CS_HREDRAW | CS_VREDRAW;  
  17.     wndClass.lpfnWndProc =WndProc;  
  18.     wndClass.hInstance =hInstance;  
  19.     wndClass.hCursor =LoadCursor( NULL, IDC_ARROW );  
  20.     wndClass.hbrBackground= ( HBRUSH )( COLOR_WINDOW + 1 );  
  21.     wndClass.lpszMenuName= NULL;  
  22.     wndClass.lpszClassName= "DX11BookWindowClass";  
  23.    
  24.     if( !RegisterClassEx(&wndClass ) )  
  25.         return -1;  
  26.    
  27.     RECT rc = { 0, 0, 640,480 };  
  28.     AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE );  
  29.    
  30.     HWND hwnd =CreateWindowA( "DX11BookWindowClass""Blank Direct3D 11 Window演示程序", WS_OVERLAPPEDWINDOW,  
  31.                                CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,  
  32.                                NULL, NULL, hInstance, NULL );  
  33.    
  34.     if( !hwnd )  
  35.         return -1;  
  36.    
  37.     ShowWindow( hwnd,cmdShow );  
  38.    
  39.    std::auto_ptr<Dx11DemoBase>demo( new BlankDemo( ) ); //使用智能指针  
  40.    
  41.     // Demo初始化工作  
  42.     bool result =demo.Initialize( hInstance, hwnd );  
  43.    
  44.     if( result == false )  
  45.         return -1;  
  46.    
  47.     MSG msg = { 0 };  
  48.    
  49.     while( msg.message !=WM_QUIT )  
  50.     {  
  51.         if( PeekMessage(&msg, 0, 0, 0, PM_REMOVE ) )  
  52.         {  
  53.            TranslateMessage( &msg );  
  54.            DispatchMessage( &msg );  
  55.         }  
  56.    
  57.         // 更新以及绘制图形  
  58.         demo.Update( 0.0f);  
  59.         demo.Render( );  
  60.     }  
  61.    
  62.     // Demo开始卸载  
  63.     demo.Shutdown( );  
  64.    
  65.     returnstatic_cast<int>( msg.wParam );  
  66. }  
  67.    
  68.    
  69. LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAMwParam, LPARAM lParam )  
  70. {  
  71.     PAINTSTRUCTpaintStruct;  
  72.     HDC hDC;  
  73.    
  74.     switch( message )  
  75.     {  
  76.         case WM_PAINT:  
  77.             hDC =BeginPaint( hwnd, &paintStruct );  
  78.             EndPaint( hwnd,&paintStruct );  
  79.             break;  
  80.    
  81.         case WM_DESTROY:  
  82.            PostQuitMessage( 0 );  
  83.             break;  
  84.    
  85.         default:  
  86.             returnDefWindowProc( hwnd, message, wParam, lParam );  
  87.     }  
  88.    
  89.     return 0;  
  90. }  


 

 

笔记二十六里的demo的基础上,我们在wWinMain函数中加了7行代码。首先我们运用了C++中的标准智能指针auto_ptr<>。

auto_ptr<>智能指针会在指向的内容结束或者此指针的作用域指向其他的智能指针时自动释放内存。这个作用域可以是,一个if语句,一个内循环,或者在一对大括号里面随意摆放来创建一个新的作用域。

这样做的好处是非常舒服的——我们并不需要手动删除分配的数据,而且使用auto_ptr<>是非常安全环保的。即使出现了异常或者bug,应用程序停止运行了,auto_ptr<>在堆栈展开过程中依然会释放其数据。这样的话,即使运用程序崩溃了,依然会做到没有内存的泄露。若我们手动删除这个指针,且执行没有停止的话,就会留下泄露的内存。采用类似auto_ptr<>的内存对象有很多好处。

不是很熟悉这些内容的朋友,最好是阅读一些智能指针和其他新潮的C++编程语言的书籍进行了解和提高,掌握最新标准的C++(C++0x)。

在wWinMain函数中的最后一件事情是要注意,我们正在返回MSG对象的wParam成员,来返回应用程序的退出代码。由于wWinMain函数返回一个整型,我们把整个对象用C++标准运算符static_cast<>进行强制类型转换,转换为整型。




Blank Direct3D Window的截图可以在下面看到。采用深蓝色来清屏。






之后的demo的创建,我们只需要从Dx11DemoBsae里面派生出新类然后重载LoadContent,UnloadContent, Update, 以及Render,进行特殊的逻辑实现即可。




本节的知识就介绍到这里。



本篇文章配套的源代码请点击这里下载: 【Visual C++】Note_Code_28








在这里公告一下,【Visual C++】游戏开发笔记系列文章现在是定在每周的周一进行更新,但是由于浅墨近期得办理回国相关

手续及收拾行李坐飞机回国,事情比较多比较杂,笔记系列估计得“停播”一周。觉得更新速度不够快,看得不过瘾的读者们

可以参看下我在笔记一中提到一些游戏开发相关的书籍。下下个周一,我们不见不散~~


感谢一直支持【Visual C++】游戏开发笔记系列专栏的朋友们。

【Visual C++】游戏开发 系列文章才刚刚展开一点而已,因为游戏世界实在是太博大精深了~

但我们不能着急,得慢慢打好基础。做学问最忌好高骛远,不是吗?

 

浅墨希望看到大家的留言,希望与大家共同交流,希望得到睿智的评论(即使是批评)。

你们的支持是我写下去的动力~

 

精通游戏开发的路还很长很长,非常希望能和大家一起交流,共同学习,共同进步。

大家看过后觉得值得一看的话,可以顶一下这篇文章,你们的支持是我继续写下去的动力~

如果文章中有什么疏漏的地方,也请大家指正。也希望大家可以多留言来和我探讨相关的问题。

最后,谢谢你们一直的支持~~~

                                               

 

                                                  ——————————浅墨于2012年7月8日

这篇关于【Visual C++】游戏开发笔记二十八 最精简的Direct3D11 Demo筋骨脉络全攻略的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/927497

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名