<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>LYsnowQの博客小屋</title>
  
  
  <link href="https://www.lysnowq.cn/atom.xml" rel="self"/>
  
  <link href="https://www.lysnowq.cn/"/>
  <updated>2026-01-30T09:17:34.534Z</updated>
  <id>https://www.lysnowq.cn/</id>
  
  <author>
    <name>LYsnowQ</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>CMake使用</title>
    <link href="https://www.lysnowq.cn/posts/11606146.html"/>
    <id>https://www.lysnowq.cn/posts/11606146.html</id>
    <published>2026-01-28T02:44:00.894Z</published>
    <updated>2026-01-30T09:17:34.534Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-CMake使用"><a href="#1-CMake使用" class="headerlink" title="1. CMake使用"></a>1. CMake使用</h1><h2 id="1-1注释"><a href="#1-1注释" class="headerlink" title="1.1注释"></a>1.1注释</h2><h3 id="1-1-1-注释行"><a href="#1-1-1-注释行" class="headerlink" title="1.1.1 注释行"></a>1.1.1 注释行</h3><p><code>Cmake</code>使用<code>#</code>进行行注释，可以放在任何位置</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#这是一个CMake行注释</span></span><br><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">4.0</span>.<span class="number">0</span>)</span><br></pre></td></tr></table></figure><h3 id="1-1-2注释块"><a href="#1-1-2注释块" class="headerlink" title="1.1.2注释块"></a>1.1.2注释块</h3><p><code>Cmake</code>使用<code>#[[]]</code>进行块注释</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#[[注释第一行</span></span><br><span class="line"><span class="comment">注释第二行</span></span><br><span class="line"><span class="comment">注释第三行]]</span></span><br><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">4.0</span>.<span class="number">0</span>)</span><br></pre></td></tr></table></figure><h2 id="1-2-CMake两种基本用法"><a href="#1-2-CMake两种基本用法" class="headerlink" title="1.2 CMake两种基本用法"></a>1.2 CMake两种基本用法</h2><h3 id="1-2-1-所有文件共处一个目录下"><a href="#1-2-1-所有文件共处一个目录下" class="headerlink" title="1.2.1 所有文件共处一个目录下"></a>1.2.1 所有文件共处一个目录下</h3><ol><li></li><li></li><li>添加<code>CMakeLists.txt</code>文件：</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.5</span>.<span class="number">0</span>)</span><br><span class="line"><span class="keyword">project</span>(<span class="keyword">Test</span>)</span><br><span class="line"><span class="keyword">add_executable</span>(<span class="keyword">Test</span> main.cpp)</span><br></pre></td></tr></table></figure><ul><li><code>cmake_minimum_required</code>（可选）：指定使用的最低版本，如果不写可能会有警告</li><li><code>project</code>：定义工程名称，并且可以指定工程的版本、工程描述、web主页地址、支持的语言，其中工程名称是必填项，其余的为选填</li></ul><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#project命令格式</span></span><br><span class="line"><span class="keyword">project</span>(&lt;Project_name&gt; [&lt;language_name&gt;])</span><br><span class="line"><span class="keyword">project</span>(&lt;project_name&gt;</span><br><span class="line">[VSERSION &lt;major&gt;[.&lt;minor&gt;[.&lt;patch&gt;[.&lt;tweak&gt;]]]]</span><br><span class="line">[DESCRIPTION] &lt;<span class="keyword">project</span>-description-<span class="keyword">string</span>&gt;</span><br><span class="line">[HOMEPAGE_URL &lt;url-<span class="keyword">string</span>&gt;]</span><br><span class="line">[LANGUAGES] &lt;language-name&gt;...)</span><br></pre></td></tr></table></figure><ul><li><code>add_executable</code>:定义工程生成一个可执行程序</li></ul><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">add_executable</span>(可执行程序名称 源文件名称...)</span><br></pre></td></tr></table></figure><p>这里的可执行程序名称只是最后生成的exe文件名称，与项目名称是两码事，源文件名可以有多个，多个使用空格或者分号来处理</p><ol><li>执行<code>CMake</code>命令：</li></ol><p>在执行cmake命令时要确保执行命令所在的路径在项目根目录中即<code>CMakeLists.txt</code>所在目录中</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">#执行以下指令进行生成对应文件</span><br><span class="line">cmake &lt;CMakeLists所在目录&gt;#生成文件的目的目录就是执行指令所在目录</span><br><span class="line"></span><br><span class="line">#在windows下如果没有生成Makefile文件那么需要写</span><br><span class="line">cmake &lt;CMakeLists所在目录&gt; -G &quot;Unix Makefiles&quot;</span><br><span class="line"></span><br><span class="line">#在MinGW下要写</span><br><span class="line">cmake &lt;CMakeLists所在目录&gt; -G &quot;MinGW Makefiles&quot;</span><br><span class="line"></span><br><span class="line">#执行完毕后再执行以下指令生成对应文件</span><br><span class="line">make</span><br></pre></td></tr></table></figure><hr><h2 id="1-3基本配置"><a href="#1-3基本配置" class="headerlink" title="1.3基本配置"></a>1.3基本配置</h2><h3 id="1-3-1变量"><a href="#1-3-1变量" class="headerlink" title="1.3.1变量"></a>1.3.1变量</h3><p>在CMake中很多时候我们也可以使用变脸来管理一些参数，比如上文中的<code>add_executable</code>中如果有多个文件那么就需要使用变量来管理</p><ul><li>设置变量 <code>set</code></li></ul><p>具体语法如下：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#set语法</span></span><br><span class="line"><span class="comment">#[]中的参数为可选项，不需要可以不写</span></span><br><span class="line"><span class="keyword">set</span>(var [VALUE] [CACHE TYPE DOCSTRING [FORCE]])</span><br></pre></td></tr></table></figure><ul><li><code>var</code>:变量名</li><li><code>VALUE</code>：变量值，一个变量可以存储多个值且所有的值都属于字符串形式存储，当有多个时打印出来的会显示为一整行，在存储时会用分号隔开</li></ul><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#set设置变量</span></span><br><span class="line"><span class="keyword">set</span>(src_list main.cpp add.cpp)</span><br><span class="line"><span class="comment">#在引用时需要使用$&#123;&#125;</span></span><br><span class="line"><span class="keyword">add_executable</span>(app <span class="variable">$&#123;src_list&#125;</span>)</span><br></pre></td></tr></table></figure><h3 id="1-3-2确立语言标准"><a href="#1-3-2确立语言标准" class="headerlink" title="1.3.2确立语言标准"></a>1.3.2确立语言标准</h3><p>在编写c++时，可能会用到c++11、c++14、c++17、c++20等版本指定，那么就需要在编译时指定要使用那个版本去编译cpp程序</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">g++ *.cpp -std=c++11 -o app</span><br></pre></td></tr></table></figure><p>在上述例子中通过参数<code>-std=c++11</code>我们指定了需要使用c++11来编译文件，c++标准对应有个宏叫<code>DCMAKE_CXX_STANDARD</code>。在CMake中规定cpp有两种方式：</p><ol><li>使用CMakeLists直接规定：</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#使用c++11标准</span></span><br><span class="line"><span class="keyword">set</span>(CMAKE_CXX_STANDARD <span class="number">11</span>)</span><br><span class="line"><span class="comment">#使用c++14标准</span></span><br><span class="line"><span class="keyword">set</span>(CMAKE_CXX_STANDARD <span class="number">14</span>)</span><br><span class="line"><span class="comment">#使用c++17标准</span></span><br><span class="line"><span class="keyword">set</span>(CMAKE_CXX_STANDARD <span class="number">17</span>)</span><br><span class="line"><span class="comment">#使用c++20标准</span></span><br><span class="line"><span class="keyword">set</span>(CMAKE_CXX_STANDARD <span class="number">20</span>)</span><br></pre></td></tr></table></figure><ol><li>在执行cmake时就指定了标准：</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">#使用c++11标准</span><br><span class="line">cmake &lt;CMakeLists.txt文件路径&gt; -DCMAKE_CXX_STANDARD=11</span><br><span class="line">#使用c++14标准</span><br><span class="line">cmake &lt;CMakeLists.txt文件路径&gt; -DCMAKE_CXX_STANDARD=14</span><br><span class="line">#使用c++17标准</span><br><span class="line">cmake &lt;CMakeLists.txt文件路径&gt; -DCMAKE_CXX_STANDARD=17</span><br><span class="line">#使用c++20标准</span><br><span class="line">cmake &lt;CMakeLists.txt文件路径&gt; -DCMAKE_CXX_STANDARD=20</span><br></pre></td></tr></table></figure><h3 id="1-3-3指定输出路径"><a href="#1-3-3指定输出路径" class="headerlink" title="1.3.3指定输出路径"></a>1.3.3指定输出路径</h3><p>在CMake中输出路径也是由一个宏来存储的叫做<code>EXECUTABLE_OUTPUT_PATH</code>，它的值通过set进行设置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#假定设置好了set(HOME &lt;项目目录&gt;)</span></span><br><span class="line"><span class="built_in">set</span>(EXECUTABLE_OUTPUT_PATH <span class="variable">$&#123;HOME&#125;</span>/bin)</span><br></pre></td></tr></table></figure><p>这里的演示是绝对路径，但是在具体项目中建议使用相对路径。</p><p>以及如果<code>EXECUTABLE_OUTPUT_PATH</code>对应目录不存在CMake会自动帮我们创建</p><h2 id="1-4搜索文件"><a href="#1-4搜索文件" class="headerlink" title="1.4搜索文件"></a>1.4搜索文件</h2><p>在上文中我们使用了变量来管理文件，但是当我们项目文件数很多时，使用变量去记录罗列同样也很麻烦，所以我们需要使用cmake中给我们提供的文件搜索功能来统一管理</p><h3 id="方式1：使用aux-source-directory"><a href="#方式1：使用aux-source-directory" class="headerlink" title="方式1：使用aux_source_directory"></a>方式1：使用aux_source_directory</h3><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">aux_source_directory</span>(&lt;dir&gt; &lt;virable&gt;)</span><br></pre></td></tr></table></figure><ul><li><code>dir</code>：要搜索的目录</li><li><code>virable</code>：将从<code>dir</code>目录下搜索到的文件存储到这个变量中</li></ul><p>使用示例：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.3</span>.<span class="number">0</span>)</span><br><span class="line"><span class="keyword">project</span>(<span class="keyword">Test</span>)</span><br><span class="line"><span class="keyword">include_directories</span>(<span class="variable">$&#123;PROJECT_SOURCE_DIR&#125;</span>/<span class="keyword">include</span>)</span><br><span class="line"><span class="comment">#搜索源文件</span></span><br><span class="line"><span class="keyword">aux_source_directory</span>(<span class="variable">$&#123;PROJECT_SOURCE_DIR/src SRC_LIST&#125;</span>)</span><br><span class="line"><span class="keyword">add_executable</span>(app SRC_LIST)</span><br></pre></td></tr></table></figure><ul><li><code>PROJECT_SOURCE_DIR</code>这个宏是用户在输入时的路径，如果用户不输入就是默认执行CMakeLists所在的路径，大多是根目录下的顶层CMakeLists.txt所在目录</li></ul><h3 id="方式2：使用file"><a href="#方式2：使用file" class="headerlink" title="方式2：使用file"></a>方式2：使用file</h3><p>在一个项目里源文件很多可以使用<code>file</code>来搜索（但是其功能不局限于搜索）</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">file</span>(GLOB/GLOB_RECURSE 变量名 要搜索的文件类型)</span><br></pre></td></tr></table></figure><ul><li><code>GLOB</code>：将指定目录下的搜到的满足条件的文件名生成一个列表来存储</li><li><code>GLOB_RECURSE</code>：递归搜索指定目录，将搜索道德满足条件的文件名生成一个列表来存储</li></ul><p>使用示例：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">file</span>(GLOB MAIN_SRC <span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/src/*.cpp)</span><br><span class="line"><span class="keyword">file</span>(GLOB MAIN_HEAD <span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/<span class="keyword">include</span>/*.h)</span><br></pre></td></tr></table></figure><ul><li><code>CMAKE_CURRENT_SOURCE_DIR</code>：这个宏表示当前访问的CMakeLists文件所在路径</li><li>其中路径参数可以加双引号也可以不加</li></ul><h2 id="1-5包含头文件"><a href="#1-5包含头文件" class="headerlink" title="1.5包含头文件"></a>1.5包含头文件</h2><p>在项目编译源文件时，很多时候需要将源文件对应头文件路径指定出来，这样才能保证编译过程中编译器能够找到这些头文件，顺利通过编译。在CMake中引入头文件使用<code>include_directories</code>:</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">include_directories</span>(&lt;headpath&gt;)</span><br></pre></td></tr></table></figure><p>使用示例:</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">├── CMakeLists.txt</span><br><span class="line">├── include</span><br><span class="line">│   ├── add.h</span><br><span class="line">│   ├── div.h</span><br><span class="line">│   ├── mult.h</span><br><span class="line">│   └── sub.h</span><br><span class="line">├── main.cpp</span><br><span class="line">└── src</span><br><span class="line">    ├── add.cpp</span><br><span class="line">    ├── div.cpp</span><br><span class="line">    ├── mult.cpp</span><br><span class="line">    └── sub.cpp</span><br><span class="line"></span><br><span class="line">2 directories, 10 files</span><br></pre></td></tr></table></figure><h2 id="1-6库制作"><a href="#1-6库制作" class="headerlink" title="1.6库制作"></a>1.6库制作</h2><p>有些时候我们并不需要将他们编译为可执行程序，而是生成一些动态/静态库分享给第三方使用。</p><p>如果要制作静态/动态库，需要使用的命令如下：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">add_library</span>(库名称 STATIC 源文件<span class="number">1</span> [源文件<span class="number">2</span>]...)<span class="comment">#静态库</span></span><br><span class="line"><span class="keyword">add_library</span>(库名称 SHARED 源文件<span class="number">1</span> [源文件<span class="number">2</span>]...)<span class="comment">#动态库</span></span><br></pre></td></tr></table></figure><p>在linux中，静态库分为三个部分：<code>lib</code>+<code>库名字</code>+<code>.a</code>,在生成之后会自动填充剩余部分，我们只需要指定名字即可，在windows下同样只需要给出名字即可，如果是动态库则是<code>.os</code>结尾windows下则是<code>.dll</code></p><p>同样使用以下目录结构：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">├── CMakeLists.txt</span><br><span class="line">├── include</span><br><span class="line">│   ├── add.h</span><br><span class="line">│   ├── div.h</span><br><span class="line">│   ├── mult.h</span><br><span class="line">│   └── sub.h</span><br><span class="line">├── main.cpp</span><br><span class="line">└── src</span><br><span class="line">    ├── add.cpp</span><br><span class="line">    ├── div.cpp</span><br><span class="line">    ├── mult.cpp</span><br><span class="line">    └── sub.cpp</span><br><span class="line"></span><br><span class="line">2 directories, 10 files</span><br></pre></td></tr></table></figure><p>这次我们只需要库泽CMakeLists内容如下：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.10</span>.<span class="number">0</span>)</span><br><span class="line"><span class="keyword">project</span>(<span class="keyword">Test</span> CXX)</span><br><span class="line"></span><br><span class="line"><span class="keyword">aux_source_directory</span>(<span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/src src_list)</span><br><span class="line"><span class="keyword">include_directories</span>(<span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/<span class="keyword">include</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 制作库时不需要exe文件所以不需要此方法</span></span><br><span class="line"><span class="comment"># add_executable(Test main.cpp $&#123;src_list&#125;)</span></span><br><span class="line"><span class="keyword">add_library</span>(<span class="keyword">Test</span> STATIC <span class="variable">$&#123;src_list&#125;</span>)</span><br><span class="line"><span class="comment">#动态库</span></span><br><span class="line"><span class="comment">#add_library(Test SHARED $&#123;src_list&#125;)</span></span><br></pre></td></tr></table></figure><ul><li>如果要指定生成库的路径则需要改变<code>LIBRARY_OUTPUT_PATH</code>（对于静态库和动态库都适用）</li><li>指定生成可执行的文件路径使用<code>EXECUTABLE_OUTPUT_PATH</code>,（动态库也属于可执行文件但是静态库不属于，所以无法使用于静态库）</li></ul><p>使用方式：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span>(LIBRARY_OUTPUT_PATH ..)</span><br><span class="line"><span class="keyword">set</span>(EXECUTABLE_OUTPUT_PATH ..)</span><br></pre></td></tr></table></figure><ul><li>在分享给别人使用时需要给出库文件和头文件，只需要这两者即可</li></ul><h2 id="1-7库链接"><a href="#1-7库链接" class="headerlink" title="1.7库链接"></a>1.7库链接</h2><p>再使用第三方库时cmake也为我们提供了链接第三方库的方法，在cmake中链接静态/动态库使用：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">link_libraries</span>(&lt;lib/libname&gt; [&lt;static lib&gt;]...)<span class="comment">#（不推荐）</span></span><br></pre></td></tr></table></figure><ul><li>参数：想要链接的静态库的名字，可以写完整名字如<code>libText.a</code>也可以写掐头去尾的名字如<code>Text</code></li></ul><p>上方的<code>link_libraries</code>不推荐使用,因为其使用时是相当于全局载入会污染全局，在新版cmake中推荐使用<code>target_link_libraries</code></p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">target_link_libraries</span>(&lt;<span class="keyword">target</span>&gt; [PUBLIC/PRIVATE/INTERFACE] &lt;lib/lib_name&gt;)</span><br></pre></td></tr></table></figure><ul><li>target：指定链接的对象<ul><li>这个对象可能是源文件</li><li>这个对象可能是动态库文件</li><li>这个对象可能是一个可执行文件</li></ul></li><li>PUBLIC/PRIVATE/INTERFACE：指定动态库的访问权限，默认为<code>PUBLIC</code><ul><li>如果各个库之间没有依赖关系，无需做任何设置，三者没有区别，一般无需指定，使用默认的<code>PUBLIC</code>即可</li><li><code>动态库的链接具有传递性</code>,如果之前有动态库A、B、C链接，然后又有动态库D链接了动态库A则相当于D也连接了B和C（注意，只有在PUBLIC模式下才有传递性中间有任何一个使用了PRIVATE的话则对应库无法被传递出去）</li><li>三个选择的区别：<ul><li>PUBLIC：此模式下对应链接的库会被连接到target中，并且符号也会被导出</li><li>PRIVATE：此模式下对应的库仅仅被链接到target中，并且终止，第三方无法感知你调用了什么库</li><li>INTERFACE：这个模式下对应的库不会被链接到target中，只会导出符号</li></ul></li></ul></li><li>lib/lib_name：指定链接的库</li><li>注意：参数2和3一般是一个整体的参数，可以有多个，并且使用<code>target_link_libraries</code>时应该在<code>add_executable</code>之后</li></ul><p>如果该库不是系统提供的（自己制作或者第三方提供的）可能出现库找不到的现象，此时可以将库的路径也指定出来。使用（也同样适用动态库）：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">link_directories</span>(&lt;lib_path&gt; [&lt;lib_path&gt;])<span class="comment">#这里可以指定多个路径</span></span><br></pre></td></tr></table></figure><p>项目结构为：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── CMakeLists.txt</span><br><span class="line">├── a</span><br><span class="line">│   └── libTest.a</span><br><span class="line">├── compile_commands.json -&gt; build/compile_commands.json</span><br><span class="line">├── include</span><br><span class="line">│   ├── add.h</span><br><span class="line">│   ├── div.h</span><br><span class="line">│   ├── mult.h</span><br><span class="line">│   └── sub.h</span><br><span class="line">├── main.cpp</span><br><span class="line">├── so</span><br><span class="line">│   └── libTest.so</span><br><span class="line">└── src</span><br><span class="line">    ├── add.cpp</span><br><span class="line">    ├── div.cpp</span><br><span class="line">    ├── mult.cpp</span><br><span class="line">    └── sub.cpp</span><br></pre></td></tr></table></figure><p>这样我们的CMakeLists代码为：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.10</span>.<span class="number">0</span>)</span><br><span class="line"><span class="keyword">project</span>(<span class="keyword">Test</span> CXX)</span><br><span class="line"></span><br><span class="line"><span class="comment"># aux_source_directory($&#123;CMAKE_CURRENT_SOURCE_DIR&#125;/src src_list)</span></span><br><span class="line"><span class="comment">#此时不需要其他的cpp文件只需要main文件即可</span></span><br><span class="line"><span class="keyword">set</span>(src_list <span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/src/main.cpp)</span><br><span class="line"><span class="comment">#指定头文件</span></span><br><span class="line"><span class="keyword">include_directories</span>(<span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/<span class="keyword">include</span>)</span><br><span class="line"><span class="comment">#设置库位置</span></span><br><span class="line"><span class="keyword">link_directories</span>(<span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/a)</span><br><span class="line"><span class="comment">#链接静态库</span></span><br><span class="line"><span class="keyword">link_libraries</span>(<span class="keyword">Test</span>)</span><br><span class="line"><span class="keyword">add_executable</span>(app <span class="variable">$&#123;src_list&#125;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="1-8日志"><a href="#1-8日志" class="headerlink" title="1.8日志"></a>1.8日志</h2><p>在CMake中提供了显示指令<code>message</code>：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">message</span>([STATUS/WARNING/AUTHOR_WARNING/SEND_ERROR/FATAL_ERROR] <span class="string">&quot;message info&quot;</span>)</span><br></pre></td></tr></table></figure><ul><li>（无）：重要消息</li><li><code>STATUS</code>：非重要消息</li><li><code>WARNING</code>：CMake警告，但是会继续执行</li><li><code>AUTHOR_WARNING</code>：CMake警告(dev),会继续执行</li><li><code>SEND_ERROR</code>：CMake错误，继续执行，但是会跳过生成步骤</li><li><code>FATAL_ERROR</code>：CMake错误，终止所有处理过程</li></ul><p>使用示例</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#输出一般信息</span></span><br><span class="line"><span class="keyword">message</span>(STATUS <span class="string">&quot;project_path:$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;&quot;</span>)</span><br><span class="line"><span class="comment">#输出警告信息</span></span><br><span class="line"><span class="keyword">message</span>(WARNING <span class="string">&quot;project_path:$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;&quot;</span>)</span><br><span class="line"><span class="comment">#输出严重错误信息</span></span><br><span class="line"><span class="keyword">message</span>(FATAL_ERROR <span class="string">&quot;project_path:$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="1-9变量操作"><a href="#1-9变量操作" class="headerlink" title="1.9变量操作"></a>1.9变量操作</h2><h3 id="1-9-1字符串的追加"><a href="#1-9-1字符串的追加" class="headerlink" title="1.9.1字符串的追加"></a>1.9.1字符串的追加</h3><p>有时候在项目中源文件并不一定都在同一个文件夹中，但是文件最终需要一起来编译最终可执行文件或者库文件，使用file 对各个路径进行搜索后还需要一个字符串拼接操作，字符串拼接操作可以用<code>set</code>进行也可以使用<code>list</code>进行</p><ul><li>使用<code>set</code></li></ul><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span>(变量名 <span class="variable">$&#123;变量名1&#125;</span> <span class="variable">$&#123;变量名2&#125;</span> ...)</span><br></pre></td></tr></table></figure><p>上述方式是从变量名1开始依次向后拼接，最后放到一个新的变量中去如果这个新的变量名有数据那么就会被覆盖掉</p><ul><li>使用<code>list</code>：</li></ul><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(APPEND &lt;<span class="keyword">list</span>&gt; [&lt;element&gt; ...])</span><br></pre></td></tr></table></figure><p><code>list</code>的功能比<code>set</code>强大，字符串拼接只是其中一功能，他需要在第一个位置指定出我们要做的操作，<code>APPEND</code>表示数据追加，后面的参数就和set一样了。</p><p>注意：在cmake底层管理时这些字符串是通过;来分隔管理的，但是一般输出时看的是一个完整的字符串</p><h3 id="1-9-2字符串的移除"><a href="#1-9-2字符串的移除" class="headerlink" title="1.9.2字符串的移除"></a>1.9.2字符串的移除</h3><p>在通过<code>file</code>搜索某个目录时，就得到了该目录下所有的文件，但是有时候我们需要排除其部分文件则需要使用字符串移除的操作还是使用<code>list</code></p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(REMOVE_ITEM &lt;<span class="keyword">list</span>&gt; [&lt;element&gt; ...])</span><br></pre></td></tr></table></figure><p>与上面的追加一样的格式，但是是将第一个参数改为了<code>REMOVE_ITEM</code></p><p>注意：由于搜索文件后实际上是存储的对应文件的完整路径，那么在剔除某个特定文件时实际上应该是剔除这个完整路径元素</p><h3 id="1-9-3list的其余操作"><a href="#1-9-3list的其余操作" class="headerlink" title="1.9.3list的其余操作"></a>1.9.3list的其余操作</h3><ol><li>获取<code>list</code>长度</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(LENGTH &lt;<span class="keyword">list</span>&gt; &lt;output_virable&gt;)</span><br></pre></td></tr></table></figure><ul><li><code>LENGTH</code>：命令参数，用于获取读列表长度</li><li><code>&lt;list&gt;</code>：当前操作的列表</li><li><code>&lt;output_variable&gt;</code>：新创建的变量用来存储结果</li></ul><ol><li>读取列表中指定索引的元素，可以指定多个索引</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(GET &lt;<span class="keyword">list</span>&gt; &lt;element index&gt; [&lt;element index&gt; ...] &lt;output_variable&gt;)</span><br></pre></td></tr></table></figure><ul><li><code>&lt;list&gt;</code></li><li><code>&lt;element index&gt;</code>：列表元素的索引<ul><li>从0开始编号，索引0的元素是列表第一个元素</li><li>索引也可以是负数，例如-1表示最后一个数，-2表示倒数第二个数</li><li>当索引长度超过列表长度就会报错（不论正负）</li></ul></li><li><code>&lt;output variable&gt;</code>：创建新变量存储结果</li></ul><ol><li>将列表中元素用连接符（字符串）连接起来组成一个字符串</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(JOIN &lt;<span class="keyword">list</span>&gt; &lt;value&gt; &lt;output variable&gt;)</span><br></pre></td></tr></table></figure><ul><li><code>&lt;list&gt;</code>：当前操作列表</li><li><code>&lt;value&gt;</code>：指定字符串</li><li><code>&lt;output variable&gt;</code>：创建新变量存储结果</li></ul><ol><li>查找列表是否存在指定元素，若未找到，返回-1</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(FIND &lt;<span class="keyword">list</span>&gt; &lt;value&gt; &lt;output variable&gt;)</span><br></pre></td></tr></table></figure><ul><li><code>&lt;list&gt;</code>：当前操作列表</li><li><code>&lt;value&gt;</code>：指定需要查询的内容</li><li><code>&lt;output variable&gt;</code>：创建新变量存储结果<ul><li>如果<code>&lt;list&gt;</code>中存在<code>&lt;value&gt;</code>就返回下标，否则就返回-1</li></ul></li></ul><ol><li>将元素追加到列表中</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">list(APPEND <span class="tag">&lt;<span class="name">list</span>&gt;</span> [<span class="tag">&lt;<span class="name">element</span>&gt;</span> ...] <span class="tag">&lt;<span class="name">output</span>&gt;</span>)</span><br></pre></td></tr></table></figure><ol><li>在指定位置插入若干个元素</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(INSERT &lt;<span class="keyword">list</span>&gt; [&lt;element&gt; ...] &lt;output&gt;)</span><br></pre></td></tr></table></figure><ol><li>将元素插入到列表0索引位置</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(PREPEND &lt;<span class="keyword">list</span>&gt; [&lt;element&gt; ...])</span><br></pre></td></tr></table></figure><ol><li>将列表中最后一个元素移除</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(POP_BACK &lt;<span class="keyword">list</span>&gt; [&lt;out_var&gt; ...])</span><br></pre></td></tr></table></figure><ol><li>将列表第一个元素移除</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(POP_FRONT &lt;<span class="keyword">list</span>&gt; [&lt;out_var&gt; ...])<span class="comment">#如果指定了变量就会存入变量，否则不存储</span></span><br></pre></td></tr></table></figure><ol><li>将指定元素从列表中移除</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(REMOVE_ITEM &lt;<span class="keyword">list</span>&gt; &lt;value&gt; [&lt;value&gt; ...])<span class="comment">#如果指定了变量就会存入变量，否则不存储</span></span><br></pre></td></tr></table></figure><ol><li>将指定索引位置元素从列表中移除</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(REMOVE_AT &lt;<span class="keyword">list</span>&gt; &lt;index&gt; [&lt;index&gt; ...])<span class="comment">#如果指定了变量就会存入变量，否则不存储</span></span><br></pre></td></tr></table></figure><ol><li>移除列表中重复元素</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(REMOVE_DUPLICATES &lt;<span class="keyword">list</span>&gt;)</span><br></pre></td></tr></table></figure><ol><li>列表翻转</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(REVERSE &lt;<span class="keyword">list</span>&gt;)</span><br></pre></td></tr></table></figure><ol><li>列表排序</li></ol><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">list</span>(SORT &lt;<span class="keyword">list</span>&gt; [COMPARE &lt;compare&gt;] [CASE &lt;case&gt;] [ORDER &lt;order&gt;])</span><br></pre></td></tr></table></figure><ul><li><p><code>COMPARE</code>：指定排序方法</p><ul><li><code>STRING</code>：按照字母顺序进行排序（为默认排序方法）</li><li><code>FILE_BASENAME</code>：如果是一系列路径名，会使用basename进行排名</li><li><code>NATURAL</code>：使用自然数排序</li></ul></li><li><p><code>CASE</code>：指明是否大小写敏感</p><ul><li><code>SENSITIVE</code>：按照大小写敏感的方法进行排序（为默认方法）</li><li><code>INSENSITIVE</code>：按照大小写不敏感的方法进行排序</li></ul></li><li><code>ORDER</code>：指明排序顺序<ul><li><code>ASCENDING</code>：按照升序排列（为默认方法）</li><li><code>DESCENDING</code>：按照降序排列</li></ul></li></ul><hr><h2 id="1-10宏定义"><a href="#1-10宏定义" class="headerlink" title="1.10宏定义"></a>1.10宏定义</h2><p>在进行代码编写时我们有时候需要一个宏变量来控制程序，如：</p><p>test.cpp:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> a =<span class="number">10</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> DEBUG</span></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;这是DEBUG模式&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;程序运行完成&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在程序第五行中，如果这个宏被定义了则就会输出这个消息，如果没定义就不会输出，在代码中我们明显看到没有定义，实际运行时就不会出现输出</p><p>为了让测试更灵活我们可以不在代码中明确定义，在测试时去定义，一种方式就是在<code>gcc/g++</code>运行时去定义 如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc test.cpp -DDEBUG -o app</span><br></pre></td></tr></table></figure><p>在<code>gcc/g++</code>命令中我们通过使用<code>-D</code>参数来定义宏名字.</p><p>在cmake中我们也可以做类似的事情，对应的命令叫：<code>add_definitions</code>：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">add_definitions</span>(-D宏名称)</span><br></pre></td></tr></table></figure><p>具体：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">add_definitions</span>(-DDEBUG)</span><br></pre></td></tr></table></figure><hr><h1 id="2-CMake嵌套"><a href="#2-CMake嵌套" class="headerlink" title="2. CMake嵌套"></a>2. CMake嵌套</h1><p>如果项目很大，或者项目中有很多源码目录，在通过CMake管理项目的时候如果只使用一个<code>CMakeLists.txt</code>，那么这个文件相对会比较复杂，有一种化繁为简的方式就是给每一层目录都加一个<code>CMakeLists.txt</code>文件（头文件目录不需要），这样每个文件都不会太复杂，而且更灵活，更容易维护。</p><p>如果想使用子节点的<code>CMakeLists.txt</code>则需要将子节点加入到父节点的<code>CMakeLists.txt</code>中，CMake为我们提供了<code>add_subdirectory</code></p><p>注意：父节点中的<code>CMakeLists</code>中的变量可以为子节点所用，但是子节点的不可为父节点所用</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">add_subdirectory</span>(source_dir [binary_dir] [EXCLUDE_FROM_ALL])</span><br></pre></td></tr></table></figure><ul><li><code>source_dir</code>：指定了<code>CMakeLists.txt</code>和源代码的位置，就是指定子目录</li><li><code>binary_dir</code>：指定了输出文件的路径，一般不需要指定，忽略即可</li><li><code>EXCLUDE_FROM_ALL</code>：在子目录下的目标默认不会被包含到父路径的<code>ALL</code>目标里，并且也会被安排在IDE工程文件之外。用户必须显式构建在子路径下的目标，基本不用</li></ul><p>使用演示</p><p>项目目录如下：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── CMakeLists.txt</span><br><span class="line">├── include</span><br><span class="line">│   ├── add.h</span><br><span class="line">│   ├── div.h</span><br><span class="line">│   ├── mult.h</span><br><span class="line">│   └── sub.h</span><br><span class="line">├── src</span><br><span class="line">│   ├── CMakeLists.txt</span><br><span class="line">│   ├── add.cpp</span><br><span class="line">│   ├── div.cpp</span><br><span class="line">│   ├── mult.cpp</span><br><span class="line">│   └── sub.cpp</span><br><span class="line">└── test</span><br><span class="line">    ├── CMakeLists.txt</span><br><span class="line">    └── main.cpp</span><br></pre></td></tr></table></figure><p><code>根目录下CMakeLists.txt</code>:</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.10</span>.<span class="number">0</span>)</span><br><span class="line"><span class="keyword">project</span>(<span class="keyword">Test</span> CXX)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义变量</span></span><br><span class="line"><span class="comment"># 静态库生成路径</span></span><br><span class="line"><span class="keyword">set</span>(LIBPATH <span class="variable">$&#123;PROJECT_SOURCE_DIR&#125;</span>/lib)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可执行程序存储目录</span></span><br><span class="line"><span class="keyword">set</span>(EXEPATH <span class="variable">$&#123;PROJECT_SOURCE_DIR&#125;</span>/bin)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 头文件路径</span></span><br><span class="line"><span class="keyword">set</span>(HEADPATH <span class="variable">$&#123;PROJECT_SOURCE_DIR&#125;</span>/<span class="keyword">include</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 库文件名字</span></span><br><span class="line"><span class="keyword">set</span>(TESTLIB <span class="keyword">Test</span>)</span><br><span class="line"><span class="keyword">set</span>(SRCLIB Src)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可执行程序名字</span></span><br><span class="line"><span class="keyword">set</span>(APPNAME app)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加子目录</span></span><br><span class="line"><span class="keyword">add_subdirectory</span>(src)</span><br><span class="line"><span class="keyword">add_subdirectory</span>(<span class="keyword">test</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>src/CMakeLists.txt</code></p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 搜索源文件</span></span><br><span class="line"><span class="keyword">aux_source_directory</span>(./ SRC)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定头文件的位置</span></span><br><span class="line"><span class="keyword">include_directories</span>(<span class="variable">$&#123;HEADPATH&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定静态库的生成</span></span><br><span class="line"><span class="keyword">set</span>(LIBRARY_OUTPUT_PATH <span class="variable">$&#123;LIBPATH&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">add_library</span>(<span class="variable">$&#123;SRCLIB&#125;</span> STATIC <span class="variable">$&#123;SRC&#125;</span>)</span><br></pre></td></tr></table></figure><p><code>test/CmakeLists.txt</code></p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 搜索源文件</span></span><br><span class="line"><span class="keyword">aux_source_directory</span>(./ SRC)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定头文件的位置</span></span><br><span class="line"><span class="keyword">include_directories</span>(<span class="variable">$&#123;HEADPATH&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(EXECUTABLE_OUTPUT_PATH <span class="variable">$&#123;EXEPATH&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成可执行文件</span></span><br><span class="line"><span class="keyword">add_executable</span>(<span class="variable">$&#123;APPNAME&#125;</span> <span class="variable">$&#123;SRC&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定链接库位置</span></span><br><span class="line"><span class="keyword">link_directories</span>(<span class="variable">$&#123;LIBPATH&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 链接库</span></span><br><span class="line"><span class="keyword">target_link_libraries</span>(<span class="variable">$&#123;APPNAME&#125;</span> <span class="variable">$&#123;SRCLIB&#125;</span>)</span><br></pre></td></tr></table></figure><hr><h1 id="3-CMake语法"><a href="#3-CMake语法" class="headerlink" title="3. CMake语法"></a>3. CMake语法</h1><h2 id="3-1-流程控制if、loop"><a href="#3-1-流程控制if、loop" class="headerlink" title="3.1 流程控制if、loop"></a>3.1 流程控制if、loop</h2><h3 id="3-1-1判断语句"><a href="#3-1-1判断语句" class="headerlink" title="3.1.1判断语句"></a>3.1.1判断语句</h3><ul><li>if语法：</li></ul><p>if(\<condition>)</p><p>\<commands></p><p>elseif(\<condition>)</p><p>\<commands></p><p>else()</p><p>\<commands></p><p>endif()</p><p>用法演示：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span>(varbool <span class="keyword">TRUE</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(varbool)</span><br><span class="line">    <span class="keyword">message</span>(<span class="keyword">TRUE</span>)</span><br><span class="line"><span class="keyword">else</span>()</span><br><span class="line">    <span class="keyword">message</span>(<span class="keyword">FALSE</span>)</span><br><span class="line"><span class="keyword">endif</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">NOT</span> varbool <span class="keyword">OR</span> varbool)</span><br><span class="line">    <span class="keyword">message</span>(<span class="keyword">TRUE</span>)</span><br><span class="line"><span class="keyword">else</span>()</span><br><span class="line">    <span class="keyword">message</span>(<span class="keyword">FALSE</span>)</span><br><span class="line"><span class="keyword">endif</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">NOT</span> varbool <span class="keyword">AND</span> varbool)</span><br><span class="line">    <span class="keyword">message</span>(<span class="keyword">TRUE</span>)</span><br><span class="line"><span class="keyword">else</span>()</span><br><span class="line">    <span class="keyword">message</span>(<span class="keyword">FALSE</span>)</span><br><span class="line"><span class="keyword">endif</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="number">1</span> <span class="keyword">LESS</span> <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;1 LESS 2&quot;</span>)</span><br><span class="line"><span class="keyword">endif</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="string">&quot;ok&quot;</span> <span class="keyword">LESS</span> <span class="number">233</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;ok LESS&quot;</span>)</span><br><span class="line"><span class="keyword">endif</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="string">&quot;2&quot;</span> <span class="keyword">EQUAL</span> <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;EQUAL&quot;</span>)</span><br><span class="line"><span class="keyword">endif</span>()</span><br></pre></td></tr></table></figure><h3 id="3-1-2-循环语句"><a href="#3-1-2-循环语句" class="headerlink" title="3.1.2 循环语句"></a>3.1.2 循环语句</h3><ul><li>for语法:</li></ul><p>foreach(\<loop_var> RANGE \<max>)</p><p>​    \<commands></p><p>enfforeach()</p><p>foreach(\<loop_var> RANGE \<min> \<max> [\<step>])</p><p>foreach(\<loop_variable> IN [LISTS \<lists>] [ITEMS \<items>])</p><p>foreach(\<loop_var> IN ZIP_LISTS \<list1> \<list2>…)</p><p>使用ZIP<em>LISTS时取用时${num</em>\<list_number>}</p><ul><li>while语法：</li></ul><p>while(\<condition>)</p><p>\<commands></p><p>endwhile()</p><p>使用演示：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">4.00</span>.<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">foreach</span>(var RANGE <span class="number">3</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="variable">$&#123;var&#125;</span>)</span><br><span class="line"><span class="keyword">endforeach</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(my_list <span class="number">1</span> <span class="number">2</span> <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">foreach</span>(var IN LISTS my_list ITEMS <span class="number">4</span> f)</span><br><span class="line">    <span class="keyword">message</span>(<span class="variable">$&#123;var&#125;</span>)</span><br><span class="line"><span class="keyword">endforeach</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">#zip</span></span><br><span class="line"><span class="keyword">set</span>(L1 one two three four)</span><br><span class="line"><span class="keyword">set</span>(L2 <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">foreach</span>(num IN ZIP_LISTS L1 L2)</span><br><span class="line">    <span class="keyword">message</span>(word = <span class="variable">$&#123;num_0&#125;</span>,num = <span class="variable">$&#123;num_1&#125;</span>)</span><br><span class="line"><span class="keyword">endforeach</span>()</span><br></pre></td></tr></table></figure><p>运行结果如下：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">4</span></span><br><span class="line">f</span><br><span class="line">word=one,num=<span class="number">1</span></span><br><span class="line">word=two,num=<span class="number">2</span></span><br><span class="line">word=three,num=<span class="number">3</span></span><br><span class="line">word=four,num=<span class="number">4</span></span><br></pre></td></tr></table></figure><h2 id="3-2-函数"><a href="#3-2-函数" class="headerlink" title="3.2 函数"></a>3.2 函数</h2><p>语法:</p><p>function(\<function_name> [\<arguement>…])</p><p>\<commands></p><p>endfunction()</p><p>使用示例：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">4.00</span>.<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span>(MyFunc firstArg)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;my func name: $&#123;CMAKE_CURRENT_FUNCTION&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;FirstArg $&#123;firstArg&#125;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">set</span>(firstArg <span class="string">&quot;New value&quot;</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;New firstArg:$&#123;firstArg&#125;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;ARGV0:$&#123;ARGV0&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;ARGV1:$&#123;ARGV1&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;ARGV2:$&#123;ARGV2&#125;&quot;</span>)</span><br><span class="line"><span class="keyword">endfunction</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(var <span class="string">&quot;first value&quot;</span>)</span><br><span class="line">MyFunc(<span class="variable">$&#123;var&#125;</span> <span class="string">&quot;value&quot;</span>)</span><br></pre></td></tr></table></figure><p>运行后得到：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">my func name: MyFunc</span><br><span class="line">FirstArg first value</span><br><span class="line">New firstArg:New value</span><br><span class="line">ARGV0:first value</span><br><span class="line">ARGV1:value</span><br><span class="line">ARGV2:</span><br></pre></td></tr></table></figure><p>实际上虽然我们确定了是一个参数，但是依旧可以传递多个参数，在这里函数依旧可以捕捉到多余的参数。</p><h2 id="3-3-cmake作用域"><a href="#3-3-cmake作用域" class="headerlink" title="3.3 cmake作用域"></a>3.3 cmake作用域</h2><p>cmake有两个作用域;</p><ul><li>function scope函数作用域</li><li>Directory scope 当从add_subdirectory()命令执行嵌套目录中的CMakeLists.txt列表文件时，注意父CMakeLists.txt其中的变量可以被子CMakeLists.txt使用</li></ul><p>示例：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">4.00</span>.<span class="number">0</span>)</span><br><span class="line"><span class="keyword">project</span>(scope)</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span>(OutFunc var)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;----------enter OutFunc----------&quot;</span>)</span><br><span class="line">    <span class="keyword">set</span>(var <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;var: $&#123;var&#125;&quot;</span>)</span><br><span class="line">    InFunc(<span class="variable">$&#123;var&#125;</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;var: $&#123;var&#125;&quot;</span>)</span><br><span class="line"><span class="keyword">endfunction</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span>(InFunc var)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;----------enter InFunc----------&quot;</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;var: $&#123;var&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">set</span>(var <span class="number">3</span>)</span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;var: $&#123;var&#125;&quot;</span>)</span><br><span class="line"><span class="keyword">endfunction</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(var1 <span class="number">1</span>)</span><br><span class="line"><span class="keyword">message</span>(<span class="variable">$&#123;var1&#125;</span>)</span><br><span class="line">OutFunc(<span class="variable">$&#123;var1&#125;</span>)</span><br><span class="line"><span class="keyword">message</span>(<span class="variable">$&#123;var1&#125;</span>)</span><br></pre></td></tr></table></figure><p>运行：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">cmake -B build</span><br><span class="line">运行结果</span><br><span class="line"><span class="number">1</span></span><br><span class="line">----------enter OutFunc----------</span><br><span class="line">var: <span class="number">2</span></span><br><span class="line">----------enter InFunc----------</span><br><span class="line">var: <span class="number">2</span></span><br><span class="line">var: <span class="number">3</span></span><br><span class="line">var: <span class="number">2</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line">-- Configuring done (<span class="number">0.0</span>s)</span><br><span class="line">-- Generating done (<span class="number">0.0</span>s)</span><br><span class="line">-- Build files have been written to: /home/ghl/<span class="keyword">project</span>/cmake_learn/cmakefiles/scope/build</span><br></pre></td></tr></table></figure><h2 id="3-4-宏"><a href="#3-4-宏" class="headerlink" title="3.4 宏"></a>3.4 宏</h2><p>语法</p><p>macro(\<name> [\<arguement>…])</p><p>\<commands></p><p>endmacro()</p><p>注意：尽量不要写宏，会读就行</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">4.00</span>.<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">macro</span>(my_macro var)</span><br><span class="line">    <span class="keyword">set</span>(var <span class="string">&quot;new value&quot;</span>) <span class="comment"># 创建了一个新的var变量</span></span><br><span class="line">    <span class="keyword">message</span>(<span class="string">&quot;argment: $&#123;var&#125;&quot;</span>)</span><br><span class="line"><span class="keyword">endmacro</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(var <span class="string">&quot;first value&quot;</span>)</span><br><span class="line"><span class="keyword">message</span>(<span class="string">&quot;var:$&#123;var&#125;&quot;</span>)</span><br><span class="line">my_macro(<span class="string">&quot;value&quot;</span>)</span><br><span class="line"><span class="keyword">message</span>(<span class="string">&quot;var:$&#123;var&#125;&quot;</span>)</span><br></pre></td></tr></table></figure><p>运行结果：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">var:first value</span><br><span class="line">argment: value</span><br><span class="line">var:new value</span><br></pre></td></tr></table></figure><p>注意，当使用了宏以后内部赋值会改动外部同名变量的内容，起传入的参数在内部使用，宏生效后同名外部变量会被改变</p>]]></content>
    
    
    <summary type="html">CMake完整的使用语法</summary>
    
    
    
    <category term="CMake学习" scheme="https://www.lysnowq.cn/categories/CMake%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="CMake" scheme="https://www.lysnowq.cn/tags/CMake/"/>
    
  </entry>
  
  <entry>
    <title>cpp网络编程基础（五）</title>
    <link href="https://www.lysnowq.cn/posts/e20ab061.html"/>
    <id>https://www.lysnowq.cn/posts/e20ab061.html</id>
    <published>2025-12-06T09:37:07.270Z</published>
    <updated>2025-12-10T15:35:09.477Z</updated>
    
    <content type="html"><![CDATA[<hr><hr><h1 id="阻塞-amp-非阻塞IO"><a href="#阻塞-amp-非阻塞IO" class="headerlink" title="阻塞&amp;非阻塞IO"></a>阻塞&amp;非阻塞IO</h1><p>基本概念：</p><ul><li>阻塞：在进/线程中，发起一个调用时，在调用返回前，进/线程会被阻塞等待，等待中进/线程让出cpu的使用权。</li><li>非阻塞：在进/线程中，发起一个调用时，会立刻返回。</li><li>会阻塞的四个函数：<code>connect()、accept()、send()、recv()</code>。</li></ul><p>应用场景：</p><ul><li>传统的网络服务端中（每连接每线程/进程），采用阻塞IO。</li><li>在IO复用模型中，事件循环不能被阻塞在任何环节，所以应该采用非阻塞IO。</li></ul><h2 id="非阻塞IO-connect"><a href="#非阻塞IO-connect" class="headerlink" title="非阻塞IO-connect()"></a>非阻塞IO-connect()</h2><ul><li>对非阻塞的IO调用connect()函数会返回失败，errno ==EINPROGRESS。</li><li>对非阻塞的IO调用connect()函数后，如果socket的状态是可写的，证明连接是成功的，否则是失败的。</li></ul><p>设置socket为非阻塞：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">setnonblocking</span><span class="params">(<span class="type">int</span> fd)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> flags;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//获取fd状态</span></span><br><span class="line">    <span class="keyword">if</span>((flags = <span class="built_in">fcntl</span>(fd,F_GETFL,<span class="number">0</span>))==<span class="number">-1</span>)</span><br><span class="line">        flags = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">fcntl</span>(fd,F_SETFL,flags|O_NONBLOCK);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//在客户端中创建了socket后设置为非阻塞</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> sockfd</span></span><br><span class="line"><span class="function"><span class="title">if</span><span class="params">((sockfd =socket(AF_INET,SOCK_STREAM,<span class="number">0</span>))&lt;<span class="number">0</span>)</span></span>&#123;<span class="built_in">printf</span>(<span class="string">&quot;socket()failed\n&quot;</span>);<span class="keyword">return</span> <span class="number">-1</span>;&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">setnonblocking</span>(sockfd);</span><br></pre></td></tr></table></figure><hr><h2 id="非阻塞IO-accept"><a href="#非阻塞IO-accept" class="headerlink" title="非阻塞IO-accept()"></a>非阻塞IO-accept()</h2><ul><li>对非阻塞的IO调用accept()，如果已连接队列中没有socket,函数立即返回失败，errno == EAGAIN</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">setnonblocking</span><span class="params">(<span class="type">int</span> fd)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> flags;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//获取fd状态</span></span><br><span class="line">    <span class="keyword">if</span>((flags = <span class="built_in">fcntl</span>(fd,F_GETFL,<span class="number">0</span>))==<span class="number">-1</span>)</span><br><span class="line">        flags = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">fcntl</span>(fd,F_SETFL,flags|O_NONBLOCK);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//在客户端中创建了socket后设置为非阻塞</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> listenfd = <span class="built_in">initserver</span>(IP)</span><br><span class="line"></span><br><span class="line"><span class="built_in">setnonblocking</span>(listenfd);</span><br><span class="line"><span class="comment">//对于非阻塞的accept返回失败要判断错误代码</span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">accept</span>(listensock,<span class="number">0</span>,<span class="number">0</span>)==<span class="number">-1</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span>(errno != EAGAIN)&#123;</span><br><span class="line">        <span class="built_in">perror</span>(<span class="string">&quot;accept()&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="非阻塞IO-recv和send"><a href="#非阻塞IO-recv和send" class="headerlink" title="非阻塞IO-recv和send"></a>非阻塞IO-recv和send</h2><ul><li><p>对于非阻塞的IO调用<code>recv()</code>如果没有数据可以读（接收缓存区为空），函数立即返回失败，errno==EAGAIN</p></li><li><p>对于非阻塞IO调用<code>send()</code>，如果socket不可写（发送缓冲区已满），函数立即返回失败，errno==EAGAIN</p></li></ul><hr><h1 id="水平触发-amp-边缘触发"><a href="#水平触发-amp-边缘触发" class="headerlink" title="水平触发&amp;边缘触发"></a>水平触发&amp;边缘触发</h1><h2 id="水平触发"><a href="#水平触发" class="headerlink" title="水平触发"></a>水平触发</h2><ul><li>读事件：如果epoll_wait触发了读事件，表示数据可读，如果程序没有把数据读完，再次调用了epoll_wait,将立即再次触发读事件</li><li>写事件：如果发送的缓冲区没有满，表示可以写入数据，只要缓冲区没有被写满，再次调用epoll_wait的时候将立即再次触发写事件。</li></ul><hr><h2 id="边缘触发"><a href="#边缘触发" class="headerlink" title="边缘触发"></a>边缘触发</h2><ul><li>读事件：epoll_wait触发读事件后，不管程序有没有处理读事件，epoll_wait都不会再触发读时间，只有当新数据到达时，才再次触发读事件。</li><li>写事件：epoll_wait触发写事件之后，如果缓冲区仍可以写（发送缓冲区没有满），epoll_wait不会再次触发写事件，只有当发送缓存区由满变成不满时，才再次触发写事件。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//默认情况下epoll是水平触即设置事件时只需要设置为EPOLLIN/EPOLLOUT即可进行水平触发</span></span><br><span class="line">epollevent ev;</span><br><span class="line">ev.data.fd = listensock;</span><br><span class="line">ev.events = EPOLLIN;</span><br><span class="line"></span><br><span class="line"><span class="comment">//若进行边缘触发则</span></span><br><span class="line">ev.events = EPOLLIN|EPOLLET;<span class="comment">//即可切换为边缘触发</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="示例代码："><a href="#示例代码：" class="headerlink" title="示例代码："></a>示例代码：</h2><p>在网络编程（四）中我们这里的使用如下</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">if</span>(evs[ii].data.fd == listensock)&#123;</span><br><span class="line">   <span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> client;</span><br><span class="line">    <span class="type">socklen_t</span> len = <span class="built_in">sizeof</span>(client);</span><br><span class="line">    <span class="type">int</span> clientsock = <span class="built_in">accept</span>(listensock,(<span class="keyword">struct</span> sockaddr*)&amp;client,&amp;len);</span><br><span class="line">   </span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">&quot;accept client(socket = %d)ok.\n&quot;</span>,clientsock);</span><br><span class="line"></span><br><span class="line">   ev.data.fd =clientsock;</span><br><span class="line">   ev.events = EPOLLIN;</span><br><span class="line">   <span class="built_in">epoll_ctl</span>(epollfd,EPOLL_CTL_ADD,clientsock,&amp;ev);</span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="type">char</span> buffer[<span class="number">1024</span>];</span><br><span class="line">       <span class="built_in">memset</span>(buffer,<span class="number">0</span>,<span class="built_in">sizeof</span>(buffer));</span><br><span class="line">       <span class="keyword">if</span>(<span class="built_in">recv</span>(evs[ii].data.fd,buffer,<span class="built_in">sizeof</span>(buffer),<span class="number">0</span>)&lt;=<span class="number">0</span>)</span><br><span class="line">       &#123;</span><br><span class="line">           <span class="built_in">printf</span>(<span class="string">&quot;client(eventfd = %d) disconnected.\n&quot;</span>,evs[ii].data.fd);</span><br><span class="line">           <span class="built_in">close</span>(evs[ii].data.fd); </span><br><span class="line">       &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">           <span class="built_in">printf</span>(<span class="string">&quot;recv(eventfd = %d):%S\n&quot;</span>,evs[ii].data.fd，buffer);</span><br><span class="line">           <span class="built_in">send</span>(evs[i].data.fd，buffer,<span class="built_in">strlen</span>(buffer),<span class="number">0</span>);</span><br><span class="line">       &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上代码是我们在前置条件为水平触发时对连接处理的 代码，那么切换至边缘触发后以上的多个客户端的连接处理过程中会造成连接丢失的情况所以需要使用循环，那么如果要使用循环就需要将accept使用非阻塞的模式，以防止阻塞循环的进行,同理recv</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">setnonblocking</span><span class="params">(<span class="type">int</span> fd)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> flags;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>((flags = <span class="built_in">fcntl</span>(fd,F_GETFL,<span class="number">0</span>))==<span class="number">-1</span>)</span><br><span class="line">        flags = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">fcntl</span>(fd,F_SETFL,flags|O_NONBLOCK);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="built_in">setnonblocking</span>(listensock);<span class="comment">//将监听设置为非阻塞</span></span><br><span class="line">...</span><br><span class="line">    epoll_event ev;</span><br><span class="line">    ev.data.fd = listensock;</span><br><span class="line">    ev.events = EPOLLIN|EPOLLET</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(evs[ii].data.fd == listensock)&#123;</span><br><span class="line">    <span class="keyword">while</span>(<span class="literal">true</span>)&#123;  </span><br><span class="line">       <span class="keyword">struct</span> sockaddr_in client;</span><br><span class="line">    <span class="type">socklen_t</span> len = <span class="built_in">sizeof</span>(client);</span><br><span class="line">    <span class="type">int</span> clientsock = <span class="built_in">accept</span>(listensock,(<span class="keyword">struct</span> sockaddr*)&amp;client,&amp;len);</span><br><span class="line">        </span><br><span class="line">            <span class="keyword">if</span>((clientsock&lt;<span class="number">0</span>)&amp;&amp;(errno == EAGAIN)) <span class="keyword">break</span>;<span class="comment">//此时才是真正的没有了连接可以退出循环</span></span><br><span class="line">            </span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;accept client(socket = %d)ok.\n&quot;</span>,clientsock);</span><br><span class="line"></span><br><span class="line">        ev.data.fd =clientsock;</span><br><span class="line">        ev.events = EPOLLIN;</span><br><span class="line">        <span class="built_in">epoll_ctl</span>(epollfd,EPOLL_CTL_ADD,clientsock,&amp;ev);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="type">char</span> buffer[<span class="number">1024</span>];</span><br><span class="line">        <span class="built_in">memset</span>(buffer,<span class="number">0</span>,<span class="built_in">sizeof</span>(buffer));</span><br><span class="line">        <span class="type">int</span> readn;<span class="comment">//每次调用recv的返回值</span></span><br><span class="line">        <span class="type">char</span>* ptr = buffer;<span class="comment">//buffer指针的位置</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">recv</span>(evs[ii].data.fd,buffer,<span class="built_in">sizeof</span>(buffer),<span class="number">0</span>)&lt;=<span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>((readn&lt;=<span class="number">0</span>)&amp;&amp; (errno== EAGAIN))</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;recv(eventfd = %d):%S\n&quot;</span>,evs[ii].data.fd，buffer);</span><br><span class="line">                <span class="built_in">send</span>(evs[i].data.fd，buffer,<span class="built_in">strlen</span>(buffer),<span class="number">0</span>);</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;<span class="comment">//其他情况均是连接丢失</span></span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;client(eventfd = %d) disconnected.\n&quot;</span>,evs[ii].data.fd);</span><br><span class="line">                <span class="built_in">close</span>(evs[ii].data.fd); </span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                ptr = ptr+readn;<span class="comment">//buffer的报文结尾位置后移</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>注意：以上代码中，边缘触发的代码再水平触发模式可以使用，但是水平出发的代码场景无法给边缘触发使用。</p>]]></content>
    
    
    <summary type="html">阻塞&amp;非阻塞以及触发方式</summary>
    
    
    
    <category term="c++进阶" scheme="https://www.lysnowq.cn/categories/c-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>cpp网络编程基础（四）</title>
    <link href="https://www.lysnowq.cn/posts/8d947526.html"/>
    <id>https://www.lysnowq.cn/posts/8d947526.html</id>
    <published>2025-12-02T15:25:33.620Z</published>
    <updated>2025-12-06T12:52:13.739Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="IO多路复用模型"><a href="#IO多路复用模型" class="headerlink" title="IO多路复用模型"></a>IO多路复用模型</h1><p>IO多路复用就是用一个线程/进程处理多个TCP以减少系统开销。</p><p>IO多路复用模型有三种：select(1024)，poll(数千)，epoll(百万)。</p><p>重点概念：</p><ul><li><p>网络通讯的读事件：</p><ol><li>已连接的队列中已经有准备好的socket(有新的客户端连接上来)</li><li>接收缓存中有数据可以读（对端发送的报文已到达）</li><li>tcp连接已断开（对端调用close()函数关闭了连接）</li></ol></li><li><p>网络通讯的写事件：</p><p>发送缓冲区没有满，可以写入数据（可以向对端发送报文）</p></li></ul><h2 id="1-select模型"><a href="#1-select模型" class="headerlink" title="1. select模型"></a>1. select模型</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//select模型需要调用select函数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">select</span><span class="params">(<span class="type">int</span> fds,fd_set *readfds, fd_set *writefds,fd_set* exceptfds,<span class="keyword">struct</span> timeval*timeout)</span></span>;</span><br></pre></td></tr></table></figure><p><code>select()</code>:允许程序同时监视多个文件描述符，检测他们的变化（如数据可读可写），从而高效的管理多个I/O操作，而不需要单独为每个操作创建独立的线程或进程，同时也支持非阻塞IO。</p><ul><li><p><code>fd_set</code>:这个结构体是用来存储链接的集合，本质是一个32位的整形数组，所以共有$4<em>8</em>32=1024$bitmap位</p><p>其中提供了四个宏来操作位图：</p><ol><li><code>void FD_CLR(int fd,fd_set *set)</code>:将对应socket从位图中删除</li><li><code>int FD_ISSET(int fd,fd_set *set)</code>:判断socket是否在位图中，如果不在返回0，如果在返回大于0的值</li><li><code>void FD_SET(int fd,fd_set *set)</code>:将socket加入到对应位图中</li><li><code>FD_ZERO(fd_set* set)</code>:初始化位图，将其中1024个位置都置为0</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">fd_set readfds; <span class="comment">//需要监视的读事件的socket集合，大小为16字节（1024位）的bitmap</span></span><br><span class="line"><span class="built_in">FD_ZERO</span>(&amp;readfds); <span class="comment">//初始化readfds</span></span><br><span class="line"><span class="built_in">FD_SET</span>(listensock,&amp;readfds);<span class="comment">//把服务端监听的socket加入bitmap</span></span><br></pre></td></tr></table></figure></li><li><p><code>timeval</code>:是超时时间的结构体：</p><p>成员有：<code>tv_sec</code>和<code>tv_usec</code>分别是超时的秒和微秒</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">timeval</span> timeout;</span><br><span class="line">timeout.tv_sec = <span class="number">10</span>;<span class="comment">//秒</span></span><br><span class="line">timeout.tv_usec = <span class="number">0</span>;<span class="comment">//微秒</span></span><br></pre></td></tr></table></figure></li><li><p><code>fds</code>:实际操作的bitmap的大小+1，告诉要操作的bitmap</p></li><li><p><code>readfds</code>：需要监视的读的bitmap</p></li><li><p><code>writefds</code>：需要监视的写的bitmap</p></li><li><p><code>exceptfds</code>：需要监视的异常的bitmap，select也可以监视普通IO,在监视普通IO时多用网络编程中不常用。</p></li><li><p><code>timeout</code>：超时时间阈值设置</p><ol><li>如果为NULL，那么select会无限等待直到至少有一个文件描述符就绪</li><li>如果两个参数都设置为0，select会立即返回用于轮询</li><li>设置具体时间后，在这段时间内select会等待文件描述符就绪，时间结束会返回超时报错。</li></ol></li></ul><p>使用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> maxfd = listensock;<span class="comment">//获取readfds中实际的最大值</span></span><br><span class="line">fd_set tmpfds = readfds;<span class="comment">//因为在select中位图会被操作修改所以这里要复制一份再传入</span></span><br><span class="line"><span class="type">int</span> infds = <span class="built_in">select</span>(maxfd+<span class="number">1</span>,&amp;tmpfds,<span class="literal">NULL</span>,<span class="literal">NULL</span>,&amp;timeout);</span><br><span class="line"><span class="comment">//tmpfds即告诉需要监视的读的bitmap，这里会修改bitmap所以需要复制一份</span></span><br></pre></td></tr></table></figure><p>select返回值：</p><ul><li><p>如果是小于零则调用失败</p></li><li><p>如果是等于0则是超时</p></li><li><p>如果成功调用则是返回成功发生事件的个数</p><p>具体使用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="type">int</span> eventfd=<span class="number">0</span>;eventfd&lt;=maxfd;eventfd++)&#123;<span class="comment">//循环遍历bitmap</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">FD_ISSET</span>(eventfd,&amp;tmpfds)==<span class="number">0</span>)<span class="keyword">continue</span>;<span class="comment">//如果当前位置为0则没任何事件</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">//如果是listen发生了事件则是说明有客户端连接上来了（已经有准备好的socket）</span></span><br><span class="line">    <span class="keyword">if</span>(eventfd == listensock)&#123;</span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> client;</span><br><span class="line">        <span class="type">socklen_t</span> len =<span class="built_in">sizof</span>(client);</span><br><span class="line">        <span class="type">int</span> clientsock = <span class="built_in">accept</span>(listensock,(<span class="keyword">struct</span> sockaddr*)&amp;client,&amp;len);</span><br><span class="line">        <span class="keyword">if</span>(listensock &lt; <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="built_in">perror</span>(<span class="string">&quot;accept() faild&quot;</span>);</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;accept client(socket =&quot;</span>&lt;&lt; clientsock&lt;&lt;<span class="string">&quot;) ok&quot;</span>&lt;&lt;std::endl;</span><br><span class="line">        <span class="built_in">FD_SET</span>(clientsock,&amp;readfds); <span class="comment">//把新连接上来的客户端标志位设置为1</span></span><br><span class="line">        <span class="keyword">if</span>(maxfd &lt; clientsock) maxfd = clientsock;<span class="comment">//更新maxfd</span></span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;<span class="comment">//如果是客户端连接上的socket有事件，表示接收缓存中有数据可以读，或者有客户端断开连接</span></span><br><span class="line">        <span class="type">char</span> buffer[<span class="number">1024</span>];</span><br><span class="line">        <span class="built_in">memset</span>(buffer,<span class="number">0</span>,<span class="built_in">sizeof</span>(buffer));</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">recv</span>(eventfd,buffer,<span class="built_in">sizeof</span>(buffer),<span class="number">0</span>)&lt;=<span class="number">0</span>)&#123;</span><br><span class="line">            std::cout &lt;&lt; <span class="string">&quot;client(eventfd = &quot;</span>&lt;&lt; eventfd&lt;&lt;<span class="string">&quot;)disconnected&quot;</span>&lt;&lt;std::endl;</span><br><span class="line">            <span class="built_in">close</span>(eventfd);</span><br><span class="line">            <span class="built_in">FD_CLR</span>(eventfd,&amp;readfds);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span>(eventfd == maxfd)</span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> ii =maxfd;ii&gt;<span class="number">0</span>;ii--)</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">FD_ISSET</span>(ii,&amp;readfds))&#123;</span><br><span class="line">                        maxfd = ii;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">//如果有报文发送过来</span></span><br><span class="line">            std::cout &lt;&lt; <span class="string">&quot;recv(eventfd=&quot;</span>&lt;&lt; eventfd&lt;&lt;<span class="string">&quot;):&quot;</span>&lt;&lt;buffer&lt;&lt;std::endl;</span><br><span class="line">            <span class="built_in">send</span>(eventfd,buffer,<span class="built_in">strlen</span>(buffer),<span class="number">0</span>);<span class="comment">//暂时把报文发送回去</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><ul><li>select-写事件：<ul><li>如果tcp发送缓存区没有满，那么，socket连接是可写的。</li><li>一般来说，发送缓冲区不容易被填满。</li><li>如果发送数据量太大，或者网络带宽不够，发送缓冲区有填满的可能性。</li><li>综上所述，一般业务中不需要关心监听写事件</li></ul></li><li><p>select-水平触发：</p><ul><li><code>select()</code>监视的socket如果发生了事件，<code>select()</code>会返回（通知应用程序处理事件）。</li><li>如果事件没有被处理，再次调用<code>select()</code>的时候会立即再通知。</li></ul></li><li><p>select-压力测试：大概可以处理12w个事务</p></li><li>select-存在的问题：<ul><li>采用轮询方式扫描bitmap，性能会随着socket数量增多而下降</li><li>每次调用<code>select()</code>，需要拷贝bitmap</li><li>bitmap的大小（单个进程/线程打开的socket数量）由<code>FD_SETSIZE</code>宏设置，默认是1024个，可以修改，但是效率将更低。</li></ul></li></ul><hr><h2 id="2-poll模型"><a href="#2-poll模型" class="headerlink" title="2. poll模型"></a>2. poll模型</h2><p>poll模型大体与select有些相似，再poll模型中要使用pollfd来存放需要监视的socket</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">pollfd fds[<span class="number">1024</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">//pollfd结构体具体如下:</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">pollfd</span>&#123;</span><br><span class="line">    <span class="type">int</span> fd;<span class="comment">//要监视的socket</span></span><br><span class="line">    <span class="type">short</span> events; <span class="comment">//</span></span><br><span class="line">    <span class="type">short</span> revents; <span class="comment">//当有事件发生时会修改此成员</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//初始化使用以下方法：</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>;i&lt;<span class="number">1024</span>;i++)</span><br><span class="line">    fds[i].fd = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//让poll监视事件</span></span><br><span class="line">fds[listensock].fd = listensock;</span><br><span class="line">fds[listensock].events = POLLIN;<span class="comment">//读时间</span></span><br><span class="line"><span class="comment">/*fds[listensock].events = </span></span><br><span class="line"><span class="comment">基本只用得到：</span></span><br><span class="line"><span class="comment">POLLIN   读事件</span></span><br><span class="line"><span class="comment">POLLOUT  写事件</span></span><br><span class="line"><span class="comment">   如果既需要读事件也需要写事件，可以如下使用;</span></span><br><span class="line"><span class="comment">   fds[listensock].events = POLLIN|POLLOUT;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    详解：</span></span><br><span class="line"><span class="comment">    events和revents共同可用：</span></span><br><span class="line"><span class="comment">    读事件：</span></span><br><span class="line"><span class="comment">    POLLIN     读普通或优先级数据</span></span><br><span class="line"><span class="comment">    POLLRDNORM 读普通数据</span></span><br><span class="line"><span class="comment">    POLLRDBAND 读优先级数据</span></span><br><span class="line"><span class="comment">    POLLPRI    读高优先级数据</span></span><br><span class="line"><span class="comment">    写事件</span></span><br><span class="line"><span class="comment">    POLLOUT    写普通数据</span></span><br><span class="line"><span class="comment">    POLLWRNORM 写普通数据</span></span><br><span class="line"><span class="comment">    POLLWRBAND 写优先级数据</span></span><br><span class="line"><span class="comment">    </span></span><br><span class="line"><span class="comment">    仅revents可用：</span></span><br><span class="line"><span class="comment">    异常：</span></span><br><span class="line"><span class="comment">    POLLERR     错误</span></span><br><span class="line"><span class="comment">    POLLHUP     挂起</span></span><br><span class="line"><span class="comment">    POLLNAV     文件描述符未打开</span></span><br><span class="line"><span class="comment">*/</span>  </span><br></pre></td></tr></table></figure><p>poll的具体使用</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">poll</span><span class="params">(<span class="keyword">struct</span> pollfd* nfds,<span class="type">nfds_t</span>,<span class="type">int</span> timeout)</span></span>;</span><br></pre></td></tr></table></figure><ul><li><code>nfds</code>：结构体数组地址</li><li><code>nfds_t</code>：结构体最大数+1</li><li><code>timeout</code>：超时时间（ms）</li></ul><p>具体代码：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//初始化服务器监听端口获取listensock</span></span><br><span class="line"></span><br><span class="line">pollfd fds[<span class="number">1024</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化数组</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i &lt;=<span class="number">1024</span>;i++)</span><br><span class="line">    fds[i].fd=<span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//让poll监视listensock事件此处位置可以自由选择，也可以让监听事件顶格排布，也可以直接按数值位置排取决于自己</span></span><br><span class="line">fds[listensock].fd = listensock;</span><br><span class="line">fds[listensock].events = POLLIN;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> maxfds = listensock; <span class="comment">//需要监视sock的实际大小</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">    <span class="type">int</span> infds = <span class="built_in">poll</span>(fds,maxfd+<span class="number">1</span>,<span class="number">10000</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//如果infds &lt; 0，表示调用失败</span></span><br><span class="line">    <span class="keyword">if</span>(infds &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">perror</span>(<span class="string">&quot;poll()&quot;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//如果infds = 0为超时</span></span><br><span class="line">    <span class="keyword">if</span>(infds == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">perror</span>(<span class="string">&quot;poll(),timeout&quot;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//大于0时是有事件发生</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> eventfd = <span class="number">0</span>;eventfd&lt;=maxfd;eventfd++)&#123;</span><br><span class="line">        <span class="keyword">if</span>(fds[eventfd].fd &lt; <span class="number">0</span>)<span class="keyword">continue</span>;<span class="comment">//忽略负fd</span></span><br><span class="line">        <span class="keyword">if</span>(fds[eventfd].events &amp; POLLIN == <span class="number">0</span>)<span class="keyword">continue</span>;<span class="comment">//没有读事件，跳过</span></span><br><span class="line">        <span class="keyword">if</span>(eventfd == listensock)&#123;</span><br><span class="line">            <span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> client;</span><br><span class="line">            <span class="type">socklen_t</span> len = <span class="built_in">sizeof</span>(client);</span><br><span class="line">            <span class="type">int</span> clientsock = <span class="built_in">accept</span>(listensock,(<span class="keyword">struct</span> sockaddr*)&amp;client,&amp;len);</span><br><span class="line">            <span class="keyword">if</span>(clientsock &lt; <span class="number">0</span>)&#123;<span class="built_in">perror</span>(<span class="string">&quot;accept() failed&quot;</span>);<span class="keyword">continue</span>;&#125;</span><br><span class="line">            </span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;accept client(socket = %d)ok.\n&quot;</span>,clientsock);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">//修改clientsock位置元素</span></span><br><span class="line">            fds[clientsock].fd =clientsock;</span><br><span class="line">            fds[clientsock].events = POLLIN;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span>(maxfd&lt;clientsock) maxfd = clientsock;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">         </span><br><span class="line">            <span class="type">char</span> buffer[<span class="number">1024</span>];</span><br><span class="line">            <span class="built_in">memset</span>(buffer,<span class="number">0</span>,<span class="built_in">sizof</span>(buffer));</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">recv</span>(eventfd,buffer,<span class="built_in">sizeof</span>(buffer),<span class="number">0</span>)&lt;=<span class="number">0</span>)&#123;</span><br><span class="line">                <span class="comment">//如果客户端连接已断开</span></span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;client(eventfd= %d) disconnected.\n&quot;</span>,eventfd);</span><br><span class="line">              </span><br><span class="line">                <span class="built_in">close</span>(eventfd);</span><br><span class="line">                fds[eventfd].fd=<span class="number">-1</span>;</span><br><span class="line">                <span class="comment">//重新计算maxfd</span></span><br><span class="line">                <span class="keyword">if</span>(eventfd == maxfd)</span><br><span class="line">                    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&gt;<span class="number">0</span>;i--)</span><br><span class="line">                        <span class="keyword">if</span>(fds[i].fd!=<span class="number">-1</span>)</span><br><span class="line">                        &#123;</span><br><span class="line">                        maxfd = i;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="comment">//如果有报文</span></span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;recv(eventfd = %d):%s\n&quot;</span>,eventfd,buffer);</span><br><span class="line">                <span class="built_in">send</span>(eventfd,buffer,<span class="built_in">strlen</span>(buffer),<span class="number">0</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>poll模型缺点：<ul><li>在程序中，poll的数据结构是数组，传入内核后转换成了链表</li><li>每调用一次<code>select()</code>需要拷贝两次bitmap，poll拷贝一次结构体数组</li><li>poll监视没有1024的显示，但是同样是遍历的方式，所以依旧是监视的socket越多，效率越低</li></ul></li></ul><hr><h2 id="3-epoll模型"><a href="#3-epoll模型" class="headerlink" title="3. epoll模型"></a>3. epoll模型</h2><p>epoll模型解决了上面两个模型的痛点，再效率方面有了极大提升，根据以下介绍顺序逐步解释并且使用epoll</p><p>epoll的使用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">epoll_create</span><span class="params">()</span></span>; <span class="comment">//创建epoll句柄</span></span><br></pre></td></tr></table></figure><blockquote><p>epoll_create()在linux2.6.8版本之前有一个int size的参数，在此之后就没有了被忽略掉了，填任意大于0的数即可</p></blockquote><p>epoll的数据结构<code>epoll_event</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">epoll_event</span>&#123;</span><br><span class="line">    <span class="type">uint32_t</span> events; <span class="comment">//事件</span></span><br><span class="line">    <span class="type">epoll_data_t</span> data;<span class="comment">//用户数据变量</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">epoll的events与poll类似有：</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">EPOLLIN：可读事件。当接收缓冲区有数据可读时触发，例如套接字有新数据或管道写端关闭。</span></span><br><span class="line"><span class="comment">EPOLLOUT：可写事件。当发送缓冲区有空间可写时触发，适用于非阻塞连接完成或数据发送。</span></span><br><span class="line"><span class="comment">实际开发中以上两个常用，下列不常用</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">EPOLLERR：错误事件。文件描述符发生错误时自动触发。</span></span><br><span class="line"><span class="comment">EPOLLHUP：挂起事件。通常表示连接被对端关闭。</span></span><br><span class="line"><span class="comment">EPOLLRDHUP：对端关闭事件。用于检测 TCP 半关闭状态。</span></span><br><span class="line"><span class="comment">EPOLLPRI：紧急数据事件。适用于带外数据（如 TCP 紧急数据）。</span></span><br><span class="line"><span class="comment">EPOLLET：边缘触发模式，仅在状态变化时触发，适合高性能场景。</span></span><br><span class="line"><span class="comment">EPOLLONESHOT：单次触发模式，事件触发后需手动重新注册。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">/*用户数据变量的具体定义如下</span></span><br><span class="line"><span class="comment">typedef union epoll_data&#123;</span></span><br><span class="line"><span class="comment">void  *ptr;</span></span><br><span class="line"><span class="comment">int   fd;</span></span><br><span class="line"><span class="comment">uint32_t u32;</span></span><br><span class="line"><span class="comment">uint64_t u64;</span></span><br><span class="line"><span class="comment">&#125;epoll_data_t;</span></span><br><span class="line"><span class="comment">这里使用了哪种数据类型后续使用时就需要使用哪种</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p><code>epoll_event</code>的使用:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//为服务端的listensock准备可读事件</span></span><br><span class="line">epoll_event ev; <span class="comment">//声明事件的数据结构</span></span><br><span class="line">ev.data.fd = listensock;<span class="comment">//指定事件的自定义数据，会随着epoll_event一起返回。</span></span><br><span class="line">ev.events =EPOLLIN;<span class="comment">//让epoll监视listensock事件</span></span><br></pre></td></tr></table></figure><p><code>epoll_ctl</code>的使用</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">epoll_ctl</span><span class="params">(<span class="type">int</span> epfd, <span class="type">int</span> op, <span class="type">int</span> fd, <span class="keyword">struct</span> epoll_event *event)</span></span>;<span class="comment">//epoll_ctl定义</span></span><br><span class="line"><span class="comment">//oll_ctl的使用</span></span><br><span class="line"><span class="built_in">epoll_ctl</span>(epollfd,EPOLL_CTL_ADD,listensock,&amp;ev);<span class="comment">//把需要监视的socket加入epollfd</span></span><br></pre></td></tr></table></figure><ul><li><code>epfd</code>:为创建的epoll句柄</li><li><code>op</code>：为操作类型：<ul><li><code>EPOLL_CTL_ADD</code>：添加</li><li><code>EPOLL_CTL_DEL</code>：删除</li><li><code>EPOLL_CTL_MOD</code>：修改</li></ul></li><li><code>fd</code>：需要操作的目标文件符</li><li><code>event</code>：需要监控的事件类型</li></ul><p>最后声明一组epoll_events来接收返回事件：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">epoll_event evs[<span class="number">10</span>];<span class="comment">//存放epoll返回的事件</span></span><br></pre></td></tr></table></figure><p>后续使用如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">    <span class="type">int</span> infds = <span class="built_in">epoll_wait</span>(epollfd,evs,<span class="number">10</span>,<span class="number">-1</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//返回失败</span></span><br><span class="line">    <span class="keyword">if</span>(infds &lt; <span class="number">0</span> )&#123;</span><br><span class="line">        <span class="built_in">perror</span>(<span class="string">&quot;epoll()failed&quot;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//超时</span></span><br><span class="line">    <span class="keyword">if</span>(infds == <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="built_in">perror</span>(<span class="string">&quot;epoll() timeout&quot;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> ii =<span class="number">0</span>;ii&lt;=infds;ii++)&#123;<span class="comment">//遍历返回数组</span></span><br><span class="line">      <span class="keyword">if</span>(evs[ii].data.fd == listensock)&#123;</span><br><span class="line">           </span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> client;</span><br><span class="line">        <span class="type">socklen_t</span> len = <span class="built_in">sizeof</span>(client);</span><br><span class="line">        <span class="type">int</span> clientsock = <span class="built_in">accept</span>(listensock,(<span class="keyword">struct</span> sockaddr*)&amp;client,&amp;len);</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;accept client(socket = %d)ok.\n&quot;</span>,clientsock);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//为新客户端准备可读事件</span></span><br><span class="line">        ev.data.fd =clientsock;</span><br><span class="line">        ev.events = EPOLLIN;</span><br><span class="line">        <span class="built_in">epoll_ctl</span>(epollfd,EPOLL_CTL_ADD,clientsock,&amp;ev);</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">//客户端有事件</span></span><br><span class="line">        <span class="type">char</span> buffer[<span class="number">1024</span>];</span><br><span class="line">            <span class="built_in">memset</span>(buffer,<span class="number">0</span>,<span class="built_in">sizeof</span>(buffer));</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">recv</span>(evs[ii].data.fd,buffer,<span class="built_in">sizeof</span>(buffer),<span class="number">0</span>)&lt;=<span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;client(eventfd = %d) disconnected.\n&quot;</span>,evs[ii].data.fd);</span><br><span class="line">                <span class="built_in">close</span>(evs[ii].data.fd); <span class="comment">//关闭客户端的socket</span></span><br><span class="line">                <span class="comment">//如果socket被关闭了会自动从epoll句柄中被删除，以下代码不必使用</span></span><br><span class="line">                <span class="comment">//epoll_ctl(epollfd,EPOLL_CTL_DEL,evs[ii].data.fd,0);</span></span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;recv(eventfd = %d):%S\n&quot;</span>,evs[ii].data.fd，buffer);</span><br><span class="line">                <span class="built_in">send</span>(evs[i].data.fd，buffer,<span class="built_in">strlen</span>(buffer),<span class="number">0</span>);</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">IO多路复用模型</summary>
    
    
    
    <category term="c++进阶" scheme="https://www.lysnowq.cn/categories/c-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++网络编程基础（一）</title>
    <link href="https://www.lysnowq.cn/posts/c54d478a.html"/>
    <id>https://www.lysnowq.cn/posts/c54d478a.html</id>
    <published>2025-11-19T09:17:27.987Z</published>
    <updated>2025-12-02T16:09:41.739Z</updated>
    
    <content type="html"><![CDATA[<h1 id="TCP和UDP"><a href="#TCP和UDP" class="headerlink" title="TCP和UDP"></a>TCP和UDP</h1><p>tcp和udp是传输的两种协议他们各有不同，大部分情况下使用的是tcp协议，还有一部分使用的是udp协议</p><h2 id="1-TCP和UDP的区别"><a href="#1-TCP和UDP的区别" class="headerlink" title="1.TCP和UDP的区别"></a>1.TCP和UDP的区别</h2><ul><li>TCP</li></ul><ol><li>TCP面向连接，通过三次握手连接和四次握手断开连接；</li><li>TCP是可靠的通信方式，通过超时重传、数据校验等方式来确保信息无差错，不丢失，不重复。并且按序到达；</li><li>TCP把数据当成字节流，当网络出现波动时，连接可能出现响应延迟的问题；</li><li>TCP只支持点对点通信；</li><li>TCP报文首部比较大有20字节；</li></ol><ul><li>UDP</li></ul><ol><li>无连接，发送前不握手，收后不断开；</li><li>不保证可靠，无重传，无确认，无序；</li><li>面向报文，一次发一个完整报文，不合并，不拆分；</li><li>支持1-&gt;1、1-&gt;N、N-&gt;N；</li><li>首部仅8字节，开销小；</li><li>无流量控制和拥塞控制，应用层自己限速；</li></ol><h2 id="2-TCP保证自身可靠性的方式"><a href="#2-TCP保证自身可靠性的方式" class="headerlink" title="2.TCP保证自身可靠性的方式"></a>2.TCP保证自身可靠性的方式</h2><ul><li>分片重组与序号管理：发送端按 MSS 分片、接收端按序号重组，天然解决失序/重复问题。</li><li>到达确认：接收端接收到分片数据时，根据分片的序号对端回复一个确认包（ACK）；</li><li>超时重发：发送方在发送分片后开始计时，若超时却没有收到对端的确认包（ACK），将会重发分片,可选SACK只重传丢失的块;</li><li>滑动窗口：TCP中采用滑动窗口来进行传输控制，发送方可以通过滑动窗口大小来确定应该发送多少字节的数据。当窗口为0时，发送方不会再发送数据；</li><li>重复处理：如果传输的分片出现重复，TCP的接收端会抛弃重复的数据；</li><li>数据校验：首部与数据反码和校验，出错即丢。</li><li>拥塞控制：慢启动，拥塞避免，快重传，快恢复，动态调节发送频率；</li><li>保活计时器：长时间无数据则发探活包，防止‘’死连接‘’占用资源</li></ul><h2 id="3-UDP不可靠的原因"><a href="#3-UDP不可靠的原因" class="headerlink" title="3.UDP不可靠的原因"></a>3.UDP不可靠的原因</h2><ul><li>无连接状态，不跟踪报文是否到达；</li><li>无序号与确认，无法判断丢包或乱序；</li><li>无超时重传，发送后不管；</li><li>无流量/拥塞控制，网络拥塞时仍按原速率发送，加剧丢包；</li><li>仅可选校验和，出错直接丢弃，不通知发送方。</li></ul><h2 id="4-TCP和UDP的使用场景"><a href="#4-TCP和UDP的使用场景" class="headerlink" title="4.TCP和UDP的使用场景"></a>4.TCP和UDP的使用场景</h2><ul><li>TCP使用场景：</li></ul><p>TCP实现了数据传输过程中的各种控制，适合对可靠性有要求的场景</p><p>如：文件传输、远程登录、邮件、数据库、web等</p><ul><li>UDP使用场景：</li></ul><p>可以容忍数据丢失的场景</p><p>如：DNS查询、DHCP广播、实时游戏同步</p>]]></content>
    
    
    <summary type="html">网络架构以及TCP和UDP</summary>
    
    
    
    <category term="c++进阶" scheme="https://www.lysnowq.cn/categories/c-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++网络编程基础（二）</title>
    <link href="https://www.lysnowq.cn/posts/b8450800.html"/>
    <id>https://www.lysnowq.cn/posts/b8450800.html</id>
    <published>2025-11-16T09:32:24.105Z</published>
    <updated>2025-12-02T15:20:44.940Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="一-大端序小端序"><a href="#一-大端序小端序" class="headerlink" title="一.大端序小端序"></a>一.大端序小端序</h1><p>如果数据字节占用内存大小超过1字节，那么CPU数据存储在内存中有两种方式：</p><p>大端序（Big Endian）：低位字节存放在高位，高位字节存放在低位；</p><p>小端序（Little Endian）：低位字节存放在低位，高位字节存放在高位；</p><p>假设从内存0x00000001开始存储十六位进制数0x12345678那么</p><p>大端序：</p><p><code>0x00000001        0x12</code></p><p><code>0x00000002        0x34</code></p><p><code>0x00000003        0x56</code></p><p><code>0x00000004        0x78</code></p><p>小端序：</p><p><code>0x00000001        0x78</code></p><p><code>0x00000002        0x56</code></p><p><code>0x00000003        0x34</code></p><p><code>0x00000004        0x12</code></p><p>在我们平常中正常是使用大端序来对齐大多为英特尔cpu。</p><p>操作文件的本质是把内存中的数据写入磁盘，在网络编程中，传输数据的本质也是把数据写入文件。</p><p>大小端序主要是在不同设备之间传输数据可能会造成问题。</p><p>在网络编程中，数据收发时有自动转化机制，不需要程序员手动转换，只有向sockaddr_in结构体成员变量填充数据时，才需要考虑字节问题。</p><hr><h2 id="二-网络字节序"><a href="#二-网络字节序" class="headerlink" title="二.网络字节序"></a>二.网络字节序</h2><p>为了解决不同设备之间的数据传输问题，采用网络字节序（大端序）。</p><p>c语言提供了四个函数，用于在主机字节序和网络字节序之间进行切换：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">uint16_t</span> <span class="title function_">htons</span><span class="params">(<span class="type">uint16_t</span> hostshort)</span>; <span class="comment">//2字节整数</span></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">htonl</span><span class="params">(<span class="type">uint32_t</span> hostlong)</span>;<span class="comment">//4字节整数</span></span><br><span class="line"><span class="type">uint16_t</span> <span class="title function_">ntohs</span><span class="params">(<span class="type">uint16_t</span> netshort)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">ntohl</span><span class="params">(<span class="type">uint32_t</span> netslong)</span>;</span><br></pre></td></tr></table></figure><p><code>h</code>:host （主机）</p><p><code>to</code>:转换</p><p><code>n</code>:network （网络）</p><p><code>s</code>:short (2字节 16位的整数)</p><p><code>l</code>:long (4字节 32位整数)</p><hr><h1 id="三-IP地址和通讯端口"><a href="#三-IP地址和通讯端口" class="headerlink" title="三.IP地址和通讯端口"></a>三.IP地址和通讯端口</h1><p>在计算机中，IPv4的地址用4字节的整数存放，通讯端口用2字节的整数（0-65535）存放。IP地址最高为：255.255.255.255</p><p>例如：192.168.190.134    –转化为整数–&gt;  3232284294转化为整数时占用4字节，不转化占用15字节</p><p>​            192    168    190    134</p><p>大端：11000000    10101000    10111110    10000110</p><p>小段：10000110    10111110    10101000    11000000</p><hr><h1 id="四-结构体"><a href="#四-结构体" class="headerlink" title="四.结构体"></a>四.结构体</h1><ul><li><code>sockaddr</code>结构体：</li></ul><p>存放协议族、端口和地址信息，客户端和<code>connect()</code>函数以及服务器的<code>bind()</code>函数需要这个结构体</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">sockaddr</span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">short</span> sa_family;<span class="comment">//协议族，与socket()函数第一个参数相同，填AF_INET</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> sa_data[<span class="number">14</span>];<span class="comment">//14字节端口和地址。</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>sockaddr_in</code>结构体：</li></ul><p><code>sockaddr</code>结构体是为了统一地址结构表示方法，统一接口函数，但是操作不方便所以定义了等价的<code>sockarrd_in</code>的结构体，他的大小与<code>sockaddr</code>相同，可以强制转化成<code>sockaddr</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">sockaddr_in</span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">short</span> sin_family;<span class="comment">//协议族，与socket()函数第一个参数相同，填AF_INET。</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">short</span> sin_port;<span class="comment">//16位端口号，大端序使用htons（整数端口）转换。</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">in_addr</span> sin_addr; <span class="comment">//ip地址结构体</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> sin_zero[<span class="number">8</span>];<span class="comment">//未使用的部分，保持与sockaddr一样的长度</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">in_addr</span>&#123; </span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> s_addr; <span class="comment">//32位ip地址，大端序</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中获取32位IP地址大端序获取有如下方案：</p><blockquote><p>以下API在vs2015即被标记为废弃，不建议在新的代码中使用</p><p>新版代码位置:</p></blockquote><p><a href="#addrinfo">新API</a></p><p>使用<code>gethostbyname</code>函数：</p><p>使用域名/主机名/字符串ip获取大端序ip。通常用于客户端</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">hostent</span> *<span class="built_in">gethostbyname</span>(<span class="type">const</span> <span class="type">char</span> *name);</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">hostent</span> &#123;</span><br><span class="line">    <span class="type">char</span> *h_name;<span class="comment">//主机名</span></span><br><span class="line">    <span class="type">char</span>**h_aliases;<span class="comment">//主机所有别名构成的字符串数组，同一个IP可以绑定多个域名。</span></span><br><span class="line">    <span class="type">short</span> h_addrtype;<span class="comment">//主机IP地址类型，例如IPv4（AF_INET）还是IPv6（AF_INET6）</span></span><br><span class="line">    <span class="type">short</span> h_length;<span class="comment">//主机IP地址长度，IPv4地址为4，IPv6地址为16。</span></span><br><span class="line">    <span class="type">char</span>**h_addr_list;<span class="comment">//主机的ip地址，以网络字节序存储。</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> h_addr h_addr_list[0]</span></span><br></pre></td></tr></table></figure><p>转换后将大端序地址代码复制到结构体<code>sockaddr_in</code>中的<code>sin_addr</code>成员中</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> servaddr;</span><br><span class="line"><span class="built_in">memset</span>(&amp;servaddr,<span class="number">0</span>,<span class="built_in">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">servaddr.sin_port = <span class="built_in">htons</span>(port_values);<span class="comment">//此处填写服务端通信端口</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">hostent</span>* h;</span><br><span class="line"><span class="keyword">if</span>((h=<span class="built_in">gethostbyname</span>(<span class="comment">/*域名/主机名/字符串格式的ip*/</span>))==<span class="literal">nullptr</span>)</span><br><span class="line">&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&#x27;failed to get gethost&#x27;</span> &lt;&lt;std:;endl;</span><br><span class="line">    <span class="built_in">close</span>(socket);<span class="comment">//socket指的是连接</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;   </span><br><span class="line"><span class="built_in">memcpy</span>(&amp;servaddr.sin_addr,h-&gt;addr,h-&gt;h_length);<span class="comment">//指定服务端的ip</span></span><br></pre></td></tr></table></figure><p><span id = "addrinfo"></span></p><p>使用<code>getaddrinfo</code>代替<code>gethostbyname</code>和<code>gethostbyaddr</code>:</p><p><code>getaddrinfo</code>相比于其他，参数设计非常灵活，支持IPv4/IPv6，多种套接字类型和协议，并且也是线程安全的并且同时也支持跨平台，在Linux中也可以放心替换。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getaddrinfo</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *hostname,</span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="type">const</span> <span class="type">char</span> *service,</span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="type">const</span> <span class="keyword">struct</span> addrinfo *hints,</span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="keyword">struct</span> addrinfo **res)</span></span>;</span><br></pre></td></tr></table></figure><ul><li><code>hostname</code>：一个主机名或者地址串（IPv4的点分十进制串或者IPv6的十六进制串）</li><li><code>service</code>:一个服务名称或者10进制端口号数串</li><li><code>hints</code>：可以为空指针，也可以为一个指向某<code>addrinfo</code>结构的指针，调用者在这个结构中填入关于期望返回信息的暗示。如果调用者的服务器支持TCP和UDP，那么调用者可以在这个参数的<code>addrinfo</code>中指定成员<code>ai_socktype</code>设置为SOCK_STREAM使得返回的仅仅适用于数据报套接口的信息。</li><li><p><code>result</code>：返回的一个指向<code>addrinfo</code>结构表数据指针，其定义在头文件<code>netdb.h</code></p></li><li><p><code>addrinfo</code>结构体</p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">addrinfo</span> &#123;</span><br><span class="line">    <span class="type">int</span>      ai_flags;      <span class="comment">// 标志位（AI_PASSIVE 等）</span></span><br><span class="line">    <span class="type">int</span>      ai_family;     <span class="comment">// AF_INET / AF_INET6 / AF_UNSPEC</span></span><br><span class="line">    <span class="type">int</span>      ai_socktype;   <span class="comment">// SOCK_STREAM / SOCK_DGRAM</span></span><br><span class="line">    <span class="type">int</span>      ai_protocol;   <span class="comment">// 0 表示自动</span></span><br><span class="line">    <span class="type">size_t</span>   ai_addrlen;    <span class="comment">// 地址长度</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">sockaddr</span> *ai_addr; <span class="comment">// 指向 sockaddr_in/in6</span></span><br><span class="line">    <span class="type">char</span>    *ai_canonname;  <span class="comment">// 正式主机名</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">addrinfo</span> *ai_next; <span class="comment">// 链表下一节点</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><blockquote><p>注意：在使用完addrinfo结构体中一定要手动释放</p><p>使用freeaddrinfo(res);</p></blockquote><p>完整示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 向服务器发送连接请求</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">addrinfo</span> server_addr &#123;&#125;,*res = <span class="literal">nullptr</span>;</span><br><span class="line">server_addr.ai_family = AF_INET;</span><br><span class="line">server_addr.ai_socktype = SOCK_STREAM;</span><br><span class="line"></span><br><span class="line">std::string port_str = std::<span class="built_in">to_string</span>(server_port);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">getaddrinfo</span>(server_ip.<span class="built_in">c_str</span>(),port_str.<span class="built_in">c_str</span>(), &amp;server_addr, &amp;res)) &#123;</span><br><span class="line">    ::<span class="built_in">closesocket</span>(m_clientfd);</span><br><span class="line">    m_clientfd = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (::<span class="built_in">connect</span>(m_clientfd,res-&gt;ai_addr,(<span class="type">int</span>)res-&gt;ai_addrlen) == <span class="number">-1</span>) &#123;</span><br><span class="line">    ::<span class="built_in">closesocket</span>(m_clientfd);</span><br><span class="line">    m_clientfd = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">freeaddrinfo</span>(res);</span><br></pre></td></tr></table></figure><hr><h1 id="五-字符串IP与大端序IP转换"><a href="#五-字符串IP与大端序IP转换" class="headerlink" title="五.字符串IP与大端序IP转换"></a>五.字符串IP与大端序IP转换</h1><p>把字符串IP转化为大端序IP,用于网络通讯的服务端中：</p><p>c语言提供了几个库函数，用于字符串格式的打IP和大端序IP的相互转化，用于网络通讯服务端程序中。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">int</span> <span class="type">int_addr_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//把转换后的ip赋值给in_addr.s_addr。</span></span><br><span class="line"><span class="function"><span class="type">in_addr_t</span> <span class="title">inet_addr</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* cp)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//在函数中，把转换后的ip填充到in_addr.s_addr成员中。</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">inet_aton</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* cp,<span class="keyword">struct</span> in_addr *inp)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//把大端序IP转化成字符串IP,用于在服务端中解析客户端数据ip地址。</span></span><br><span class="line"><span class="function"><span class="type">char</span> *<span class="title">int_ntoa</span><span class="params">(<span class="keyword">struct</span> in_addr in)</span></span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>服务端用于通信的IP和端口绑定到socket上</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> servaddr;</span><br><span class="line"><span class="built_in">memset</span>(&amp;servaddr,<span class="number">0</span>,<span class="built_in">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">servaddr.sin_port = <span class="built_in">htons</span>(<span class="comment">/*指定服务端口*/</span>);</span><br><span class="line"><span class="comment">//如果操作系统有多个ipsuoyouip都用于通信</span></span><br><span class="line"><span class="comment">//只有一个ip则将INADDR_ANY换成ip地址即可</span></span><br><span class="line"><span class="comment">//只有一个地址servaddr.sin_addr.s_addr = inet_addr(&quot;192.168.101.138&quot;); 字符串地址转化</span></span><br><span class="line"><span class="comment">//以上形式只支持IP地址不支持主机名，域名</span></span><br><span class="line">servaddr.sin_addr.s_addr = <span class="built_in">htonl</span>(INADDR_ANY);</span><br><span class="line"></span><br><span class="line"><span class="comment">//绑定服务端得IP和端口(listenfd)</span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">bind</span>(listendfd,(<span class="keyword">struct</span> sockaddr*)&amp;seraddr<span class="comment">/*强制转化为sockaddr*/</span>,<span class="built_in">sizeof</span>(seraddr))==<span class="number">-1</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">perror</span>(<span class="string">&quot;bind&quot;</span>);</span><br><span class="line">    <span class="built_in">close</span>(listenfd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//socket设置为可连接（监听）</span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">listen</span>(listendfd,<span class="number">5</span>)==<span class="number">-1</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">perror</span>(<span class="string">&quot;listen&quot;</span>);</span><br><span class="line">    <span class="built_in">close</span>(listenfd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>相较于<code>gethostbyname</code>通过主机名，域名.<code>inet_addr</code>是直接指定IP不支持主机名和域名获取IP。所以多用于服务端，<code>gethostbyname</code>而在客户端使用。</p><p>在以上工作完成之后客户端可以连接服务端发送请求</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//客户端</span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">connect</span>(sockfd,(<span class="keyword">struct</span> sockaddr*)servaddr,<span class="built_in">sizeof</span>(servaddr))==<span class="number">-1</span>)&#123;</span><br><span class="line">    <span class="built_in">perror</span>(<span class="string">&quot;connect&quot;</span>);</span><br><span class="line">    <span class="built_in">close</span>(sockfd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">网络字节序和主机字节序以及结构体的介绍和作用</summary>
    
    
    
    <category term="c++进阶" scheme="https://www.lysnowq.cn/categories/c-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>cpp网络编程基础（三）</title>
    <link href="https://www.lysnowq.cn/posts/77a7cf11.html"/>
    <id>https://www.lysnowq.cn/posts/77a7cf11.html</id>
    <published>2025-11-15T11:40:34.479Z</published>
    <updated>2025-12-03T11:21:01.235Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="三次握手和4次挥手"><a href="#三次握手和4次挥手" class="headerlink" title="三次握手和4次挥手"></a>三次握手和4次挥手</h1><h2 id="一-三次握手"><a href="#一-三次握手" class="headerlink" title="一. 三次握手"></a>一. 三次握手</h2><p>tcp是可靠的连接，在tcp连接时需要进行3次对话（握手）</p><p>在服务端进行监听时<code>listen</code>客户端就可以向服务端发送请求<code>connect</code>进行连接，这是首次进行对话，在这个过程中，当客户端调用<code>connect</code>时就会触发三次握手。</p><p>当三次握手完成后，客户端和服务端就会建立起一个双向的传输通道。</p><p>具体图示如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20251202165010157.png" alt="image-20251202165010157"></p><p>注意：</p><ul><li><p>客户端的socket也有端口号，但是不需要关心，所以客户端的socket是随机分配的。</p></li><li><p>服务端的<code>bind()</code>函数，普通用户权限只能使用1024端口以上的端口，而root权限用户则可以使用所有端口。</p></li><li><p><code>listen()</code>函数的第二个参数+1为已连接的队列（<code>ESTABLISHED</code>状态）的大小。超过这个数量的客户端连接到同一个socket进程后的状态会显示为<code>SYN_RECV</code>状态，也称之为半连接状态。</p></li><li><p>上方的图中的状态<code>CLOSED</code>状态是不存在的假想状态。</p></li></ul><hr><h2 id="二-四次挥手"><a href="#二-四次挥手" class="headerlink" title="二. 四次挥手"></a>二. 四次挥手</h2><p>断开一个TCP连接时，客户端和服务端需要相互总共发送四个包以确认连接的断开。在socket编程中，这一过程由客户端或服务端任意一方执行<code>close()</code>触发</p><p>具体流程图如下：<img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20251202172859539.png" alt="image-20251202172859539"></p><p>注意：</p><ul><li><p>主动断开端在四次挥手后，socket的状态为<code>TIME_WAIT</code>,该状态将持续2MSL(30s/1min/2min)。MSL（Maximum Segment Lifetime）报文在网络上存在的最长时间，超过这个时间报文将被废弃。</p></li><li><p>如果是客户端主动断开，<code>TIME_WAIT</code>的状态几乎不会造成危害,原因如下：</p><ol><li>客户端程序的socket程序很少。</li><li>客户端的端口是随机分配的，不存在重用的问题。</li></ol></li><li><p>如果是服务端主动断开，有两方面危害：</p><ol><li>socket没有立即释放。</li><li>端口号只能在2MSL后才能继续使用。</li></ol><p>在服务端程序中，用<code>setsockopt()</code>函数设置socket属性（一定要放在<code>bind()</code>之前）</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> opt = <span class="number">1</span>;</span><br><span class="line"><span class="built_in">setsockopt</span>(m_listenfid,</span><br><span class="line">           SOL_SOCKET,</span><br><span class="line">           SO_REUSERADDR,</span><br><span class="line">           &amp;opt,</span><br><span class="line">           <span class="built_in">sizeof</span>(opt));</span><br></pre></td></tr></table></figure></li></ul><hr><h1 id="socket函数有关参数解释"><a href="#socket函数有关参数解释" class="headerlink" title="socket函数有关参数解释"></a>socket函数有关参数解释</h1><p>在cpp中我们的网络组件都是由以下几个关键接口组成：</p><p>服务端：</p><ul><li><code>socket</code>:网络通信对象</li><li><p><code>bind</code>:绑定IP和端口，作为通信入口</p></li><li><p><code>listen</code>:开启监听，等待客户端请求</p></li><li><code>accept</code>:接收客户端连接请求</li><li><code>send</code>:发送数据</li><li><code>recv</code>:接收数据</li><li><code>close</code>:关闭连接</li></ul><p>客户端：</p><ul><li><code>socket</code>:网络通信对象</li><li><code>connect</code>:连接服务器</li><li><code>send</code>:发送数据</li><li><code>recv</code>:接收数据</li><li><code>close</code>:关闭连接</li></ul><hr><h2 id="一-socket函数参数"><a href="#一-socket函数参数" class="headerlink" title="一. socket函数参数"></a>一. socket函数参数</h2><p><code>socket</code>创建：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">socket</span><span class="params">(<span class="type">int</span> domain,<span class="type">int</span> type,<span class="type">int</span> protocol)</span></span>; <span class="comment">//Linux</span></span><br><span class="line"><span class="function">SOCKET <span class="title">socket</span><span class="params">(<span class="type">int</span> domain,<span class="type">int</span> type,<span class="type">int</span> protocol)</span></span>; <span class="comment">//Windows</span></span><br></pre></td></tr></table></figure><p>创建成功返回一个有效的socket，失败返回<code>-1</code>，<code>errno</code>被设置。</p><p>参数不错的情况下基本不会失败。</p><p>单个进程中创建的socket数量与受系统参数<code>open files</code>的限制（socket本质也是文件描述符）</p><ul><li><p><code>domain</code>家族:</p><ol><li><code>AF_INET</code>：IPv4互联网协议族</li><li><code>AF_INT6</code>：IPv6互联网协议族</li><li><code>AF_LOCAL</code>：本地通信协议族</li><li><code>AF_PACKET</code>：内核底层协议族</li><li><code>AF_IPX</code>：IPX Novell协议族</li></ol></li><li><p><code>type</code>数据传输类型：</p><ol><li><code>SOCK_STREAM</code>面向连接的socket：<ul><li>数据不会丢失</li><li>数据顺序不会错乱</li><li>双向通道</li></ul></li><li><code>SOCK_DGRAM</code>无连接的socket:<ul><li>数据可能会丢失</li><li>数据顺序可能会错乱</li><li>传输效率更高</li></ul></li></ol></li><li><p><code>protocol</code>最终使用协议：</p></li></ul><p>IPv4网络家族协议中，数据传输方式为<code>SOCK_STREAM</code>的协议只有<code>IPPROTO_TCP</code>，数据传输方式为<code>SOCK_DGRAM</code>协议的只有<code>IPPROTO_UDP</code>。</p><p>此处参数也可以填0编译器可自动识别</p><p>TCP和UDP</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">socket</span>(PF_INET,SOCK_STREAM,IPPROTO_TCP);</span><br><span class="line"><span class="built_in">socket</span>(PF_INET,SOCK_DGRAM,IPPROTO_UDP);</span><br></pre></td></tr></table></figure><hr><h2 id="二-bind函数参数"><a href="#二-bind函数参数" class="headerlink" title="二. bind函数参数"></a>二. bind函数参数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">bind</span><span class="params">(<span class="type">int</span> sockfd,<span class="type">const</span> <span class="keyword">struct</span> sockaddr *addr,<span class="type">socklen_t</span> addrlen)</span></span>;<span class="comment">//Linux</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">bind</span><span class="params">(SOCKET sockfd,<span class="type">const</span> <span class="keyword">struct</span> sockaddr* addr,<span class="type">socklen_t</span> addrlen)</span></span>;<span class="comment">//windows</span></span><br></pre></td></tr></table></figure><p>给socket绑定一个地址，这样client对这个地址收发相应的数据就能和socket关联如果绑定失败会返回<code>-1</code>,<code>erron</code>被设置</p><p>在服务端中必须要进行调用，在客户端中不需要调用，也可以调用，如果不调用则由系统自动绑定本机地址和随机分配端口进行连接</p><ul><li><code>sockfd</code>:socket文件描述符</li><li><code>addr</code>:构建sockaddr的结构体包含IP端口等信息</li><li><code>addrlen</code>:结构体addr的参数长度</li></ul><hr><h2 id="三-listen函数参数"><a href="#三-listen函数参数" class="headerlink" title="三. listen函数参数"></a>三. listen函数参数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">listen</span><span class="params">(<span class="type">int</span> sockfd,<span class="type">int</span> backlog)</span></span>;<span class="comment">//Linux</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">listen</span><span class="params">(SOCKET sockfd,<span class="type">int</span> backlog)</span></span>; <span class="comment">//windows</span></span><br></pre></td></tr></table></figure><p>当服务器开启监听后会等待客户端的连接请求，如果开启失败会返回<code>-1</code>,<code>erron</code>被设置</p><p>同样在服务器中必须要被调用以用来接收请求</p><ul><li><code>sockfd</code>:socket文件描述符</li><li><code>backlog</code>:指定了服务器排队的最大连接数，当客户端发起请求时，服务器需要时间来处理请求，因此会有一个队列来存储这些暂时不能处理的请求。如果并发量小一般可以设置为10-20。如果将其设置为<code>SOMAXCONN</code>则是由系统来决定请求队列长度，这个值一般比较大，可能是几百甚至更多。当队列满后不再接收新的请求。</li></ul><hr><h2 id="四-accept函数参数"><a href="#四-accept函数参数" class="headerlink" title="四. accept函数参数"></a>四. accept函数参数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">accept</span><span class="params">(<span class="type">int</span> sock,<span class="keyword">struct</span> sockaddr* addr,<span class="type">socklen_t</span> *addrlen)</span></span>;<span class="comment">//Linux</span></span><br><span class="line"><span class="function">SOCKET <span class="title">accept</span><span class="params">(SOCKET sock,<span class="keyword">struct</span> sockaddr* addr ,<span class="type">socklen_t</span> *addrlen)</span></span>; <span class="comment">//windows</span></span><br></pre></td></tr></table></figure><p>accpet是专门用来接收客户端请求用的，与listen配套使用</p><ul><li><code>sock</code>：<strong>服务器</strong>套接字（这里是服务器的而不是客户端的）</li><li><code>addr</code>：保存的是客户端的相关信息。后续要使用addr进行与客户端的通讯</li><li><code>addrlen</code>：结构体的大小。</li></ul><p>跟listen进行区分，在listen只是开启了监听，而accept才是真正进行接收工作。accept会阻塞程序运行，直到由新的请求</p><hr><h2 id="五-send-recv函数参数"><a href="#五-send-recv函数参数" class="headerlink" title="五. send/recv函数参数"></a>五. send/recv函数参数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Linux</span></span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">send</span><span class="params">(<span class="type">int</span> sockfd,<span class="type">const</span> <span class="type">void</span>* sendbuffer,<span class="type">size_t</span> nbytes,<span class="type">int</span> flag)</span></span>;</span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">recv</span><span class="params">(<span class="type">int</span> sockfd,<span class="type">void</span>* recvbuffer,<span class="type">size_t</span> nbytes,<span class="type">int</span> flag)</span></span>;</span><br><span class="line"><span class="comment">//Windows</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">send</span><span class="params">(SOCKET sockfd,<span class="type">const</span> <span class="type">char</span>* sendbuffer ,<span class="type">int</span> nbytes,<span class="type">int</span> flag)</span></span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">recv</span><span class="params">(<span class="type">int</span> sockfd,<span class="type">char</span>* recvbuffer,<span class="type">size_t</span> nbytes,<span class="type">int</span> flag)</span></span>;</span><br></pre></td></tr></table></figure><p><code>send</code>和<code>recv</code>都是用来对字节流的传输和接收，其中Linux中还有<code>read()</code>和<code>write()</code>的使用如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//仅限Linux</span></span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">write</span><span class="params">(<span class="type">int</span> sockfd,<span class="type">const</span> <span class="type">void</span>* sendbuffer,<span class="type">size_t</span> nbytes)</span></span>;</span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">read</span><span class="params">(<span class="type">int</span> sockfd ,<span class="type">void</span>* recvbuffer,<span class="type">size_t</span> nbytes)</span></span>;</span><br></pre></td></tr></table></figure><p>两者明显的区别是<code>write</code>和<code>read</code>没有flag参数，然而在使用时基本使用<code>send</code>和<code>recv</code>因为功能更全面且两个平台均支持，所以兼容性也更好。</p><ul><li><code>sockfd</code>:目标端套接字</li><li><code>sendbuffer</code>:要发送的消息（win端要转化为字符数组类型地址，Linux可以直接发送结构体）</li><li><p><code>recvbuffer</code>:要发送的消息（win端要转化为字符数组类型地址，Linux可以直接发送结构体）</p></li><li><p><code>nbytes</code>:要发送消息的字节长度</p></li></ul><hr><h2 id="六-close函数"><a href="#六-close函数" class="headerlink" title="六. close函数"></a>六. close函数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">close</span>(<span class="type">int</span> sockfd);<span class="comment">//Linux</span></span><br><span class="line"><span class="built_in">closesocket</span>(SOCKET sockfd);<span class="comment">//Windows</span></span><br></pre></td></tr></table></figure><p>用来关闭连接使用，关闭自身socket对象。</p><hr><h1 id="TCP缓存"><a href="#TCP缓存" class="headerlink" title="TCP缓存"></a>TCP缓存</h1><blockquote><p>系统为每个socket创建了发送缓冲区和接收缓冲区，应用程序调用<code>send()</code>或者<code>write()</code>函数发送数据的时候，内核吧数据从应用进程拷贝socket的发送缓冲区中；应用程序调用<code>recv()</code>或者<code>read()</code>函数接收数据的时候，内核把数据从socket的接收缓冲区拷贝进应用进程中。</p></blockquote><p>发送数据即把数据放入发送缓存区。</p><p>接收数据即从接收缓冲区中取数据。</p><p>查看socket缓存大小:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> bufsize = <span class="number">0</span>;</span><br><span class="line"><span class="type">socklen_t</span> optlen = <span class="built_in">sizeof</span>(bufsize);</span><br><span class="line"></span><br><span class="line"><span class="comment">//发送缓冲区大小</span></span><br><span class="line"><span class="built_in">getsockopt</span>(sockfd ,SOL_SOCKET,SO_SNDBUF,&amp;bufsize,&amp;optlen);</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;send bufsize = &quot;</span> &lt;&lt; bufsize &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line"><span class="comment">//接收缓冲区大小</span></span><br><span class="line"><span class="built_in">getsockopt</span>(sockfd ,SOL_SOCKET,SO_RCVBUF,&amp;bufsize,&amp;optlen);</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;recv bufsize = &quot;</span> &lt;&lt; bufsize &lt;&lt; std::endl;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>注意：</p><p><code>send()</code>:函数在发送缓存区或者对端的接收缓存区满了时会阻塞。</p><ul><li>Nagle算法</li></ul><blockquote><p>在TCP协议中无论发送多少数据，都要在数据前面加上协议头，同时对方接收到数据后也要回复ACK表示确认，为了尽可能的利用网络带宽，TCP每次希望都能够以MSS(Maximum Segment Size,最大报文长度)，的数据块来发送数据。</p></blockquote><p>Nagle 算法就是为了尽可能发送大块数据，避免网络中充斥着小数据块</p><p>Nagle 算法的定义是：任意时刻，最多只有一个违背确认的小段，小段是指小于MSS的数据块，违背确认是指一个数据块发送出去后，没有收到对端回复的ACK。</p><ul><li>ACK延迟机制</li></ul><p>TCP协议中不仅仅有Nagle算法，还有一个ACK延迟机制：</p><blockquote><p>当接收端收到数据后，并不会马上向发送端回复ACK,而是延迟40ms后再回复，它希望再40ms内接收端会向发送端回复应答数据，这养ACK可以和应答数据一起发送，把ACK稍带过去。</p></blockquote><p>如果TCP连接的一段启用了Nagle算法，另一端启用了ACK延迟机制，而发送的数据包又比较小，则可能出现这样的情况：发送端在等待上一个包的ACK，而接收端正好延迟了此ACK，那么这个正要发送的包就被延迟了40ms。</p><p><strong>解决方案</strong></p><p>开启<code>TCP_NODELAY</code>,开启后就是禁用Nagle算法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/tcp.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> opt = <span class="number">1</span>;</span><br><span class="line"><span class="built_in">setsockopt</span>(sockfd,IPPROTO_TCP,TCP_NODELAY,&amp;opt,<span class="built_in">sizeof</span>(opt));</span><br></pre></td></tr></table></figure><p>对时效要求高的系统中，如联机游戏就会禁用Nagle算法。</p>]]></content>
    
    
    <summary type="html">握手机制与socket函数详解</summary>
    
    
    
    <category term="c++进阶" scheme="https://www.lysnowq.cn/categories/c-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>线程、锁，原子操作使用以及无定义行为解读和处理</title>
    <link href="https://www.lysnowq.cn/posts/ad1af8d4.html"/>
    <id>https://www.lysnowq.cn/posts/ad1af8d4.html</id>
    <published>2025-11-09T10:48:20.175Z</published>
    <updated>2025-11-13T14:16:30.793Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="线程的创建和使用"><a href="#线程的创建和使用" class="headerlink" title="线程的创建和使用"></a>线程的创建和使用</h1><p>​        线程库中创建多线程的库是thread库,基本的线程创建是使用std::thread,如果创建了线程但是没有等待线程结束就会报错，此时有两种方式，第一种使用join（）方法让主线程等待子线程完成工作。第二个就是分离子线程使其单独在后台运行，方法为detach（）。<strong>当整个进程结束时整个子线程的资源才会被跟着回收</strong>。</p><p>​        其中每个线程只能使用一次join，如果多次使用就会抛出异常，其中方法joinable()就是判断是否可以进行join或者detach（）</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printStr</span><span class="params">(std::string str)</span> </span>&#123;</span><br><span class="line">std::cout &lt;&lt;  str &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建线程 </span></span><br><span class="line"><span class="function">std::thread <span class="title">thread1</span><span class="params">(printStr,<span class="string">&quot;Hello_world&quot;</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//分离线程(主进程结束后子线程不会报错但是也会被强制结束)</span></span><br><span class="line"><span class="comment">//thread1.detach();</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//主线程等待子线程结束</span></span><br><span class="line"><span class="comment">//thread1.join();</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//返回bool判断是否可以调用join或者detach</span></span><br><span class="line"><span class="comment">//join只能使用一次，再次使用会报错</span></span><br><span class="line"><span class="type">bool</span> isjoin = thread1.<span class="built_in">joinable</span>();</span><br><span class="line"><span class="keyword">if</span> (isjoin)</span><br><span class="line">thread1.<span class="built_in">join</span>();<span class="comment">//或者thread1.detach();</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="线程中的无定义行为（Undefined-Behavior，UB）"><a href="#线程中的无定义行为（Undefined-Behavior，UB）" class="headerlink" title="线程中的无定义行为（Undefined Behavior，UB）"></a>线程中的无定义行为（Undefined Behavior，UB）</h1><p>cpp程序中无定义行为(UB)是一种非常严重的程序错误其中主要是两个方面的原因：</p><pre><code>1. 对象生命周期的规划1. 指针失效</code></pre><p>第一点中其中对象生命周期的规划主要是作用域中体现，如当一个局部变量在所在作用域执行完后其生命周期就结束了，结束后其原来的占用地址内存被回收，那一块内存就不再是原来的内容了。</p><p>第二点中是指针的失效，即原来的指针在解引用后如果再次访问的话就造成了UB，此时会有以下几个情况</p><ul><li>程序继续运行，但是读取的内容是垃圾值无效数值。</li><li>触发系统保护，抛出SIGSEGV。</li><li>彻底优化掉本次访问，直接相当于略过。</li></ul><p>那么再加上线程运行后会把这个问题放大，当主线程执行完毕后很多资源被释放掉后子线程就及其容易进入UB，本身在线程中执行时间执行级别会被放大，其问题发生概率也会被直接放大。</p><p>在整个程序周期中有以下几点可以尽量避免程序触发UB:</p><ol><li>延长生命周期</li><li>主线程等待，尽量使用join避免使用或者少使用detach</li><li>将需要使用的内容直接拷贝而非直接引用</li></ol><p>示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func1</span><span class="params">(<span class="type">int</span>&amp; x)</span></span>&#123;</span><br><span class="line">    x+=<span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func2</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func1,std::ref(a))</span></span>;<span class="comment">//同样触发UB，在线程启动后a的资源就被释放了</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func3</span><span class="params">(<span class="type">int</span>*x)</span></span>&#123;</span><br><span class="line">    std::cout &lt;&lt; *x &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    std:;<span class="function">thread <span class="title">t1</span><span class="params">(func1,a)</span></span>;<span class="comment">//这里会报错或者会出</span></span><br><span class="line">    <span class="comment">//如果想传递这个参数需要使用std::ref打包，如下：</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(func1,std::ref)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    这个示例现实的值并非我们传入的1，也可能崩溃，也有概率会输出1，看是否是在delete之前就把指令执行完了</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="type">int</span>* s = <span class="keyword">new</span> <span class="built_in">int</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(func3,s)</span></span>;</span><br><span class="line">    <span class="keyword">delete</span> s;</span><br><span class="line">    </span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类对象UB示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>&#123;</span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span>&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&#x27;Thread&#x27;</span> &lt;&lt;std::this_thread::<span class="built_in">get_id</span>()</span><br><span class="line">            &lt;&lt; <span class="string">&quot;shared&quot;</span> &lt;, std::endl;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Thread&quot;</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>()</span><br><span class="line">            &lt;&lt;<span class="string">&quot;finished&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">        </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    std::shared_ptr&lt;MyClass&gt; obj = std::<span class="built_in">make_shared</span>&lt;MyClass&gt;();</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(&amp;MyClass::func,*obj)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();<span class="comment">//与上述例子其实都差不多</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以使用智能指针</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cls</span>&#123;</span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span>&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Hello&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    std::shared_ptr&lt;A&gt; a = std::<span class="built_in">make_shared</span>&lt;A&gt;();</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(&amp;a::foo,a)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">    </span><br></pre></td></tr></table></figure><hr><h1 id="互斥锁（量）解决多线程共享问题"><a href="#互斥锁（量）解决多线程共享问题" class="headerlink" title="互斥锁（量）解决多线程共享问题"></a>互斥锁（量）解决多线程共享问题</h1><h2 id="数据共享问题"><a href="#数据共享问题" class="headerlink" title="数据共享问题"></a>数据共享问题</h2><p>​        在多个线程中共享数据时需要注意线程安全问题，如果多个线程同时访问一个变量并且至少其中一个线程对其进行了写操作，那么就会出现数据竞争问题，数据竞争问题可能会导致程序崩溃、产生未定义结果、或者得到错误的结果。</p><p>​        为了避免数据竞争问题，需要有同步机制来确保多个线程之间共享数据的安全。常见的同步机制包括互斥量，条件变量，原子操作等。</p><p>以下为简单示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">funcadd</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">a += <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(funcadd)</span></span>;</span><br><span class="line"><span class="function">std::thread <span class="title">t2</span><span class="params">(funcadd)</span></span>;</span><br><span class="line"></span><br><span class="line">t1.<span class="built_in">join</span>();</span><br><span class="line">t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; a &lt;&lt; std::endl;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行以上代码后基本不会得到预期的2000数值且每次运行结果都很可能会不一样</p><p>正确写法是在<code>funcadd</code>中加一个锁，具体如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line"><span class="type">int</span> a = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">funcadd</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">mtx.<span class="built_in">lock</span>();</span><br><span class="line">        a += <span class="number">1</span>;</span><br><span class="line">        mtx.<span class="built_in">unlock</span>();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根据运行结果可知，只要当多线程运行的数值结果能与单线程运行时得到相同的结果时这个多线程运行就是安全的。</p><hr><h2 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h2><p>值得注意的是在锁的操作中还有个死锁的情况</p><p>场景：</p><ul><li>线程A 需要操作lk1 和lk2</li><li>线程B需要操作lk2 和lk1</li></ul><p>操作：</p><p>​        让程序直接运行这两个线程就会出现死锁的状态。</p><p>原因：</p><p>​        当线程A,B执行后由于两者是并行运行，所以会有线程A拿到lk1的所有权，线程B拿到lk2的所有权，当线程A准备申请lk2的所有权时因为在B手中所以陷入等待。同样，线程B也在等待线程A,于是两者就永远等待下去了。就是死锁。</p><p>场景复现：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#incldue <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::mutex m1,m2;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func1</span><span class="params">()</span> </span>&#123;</span><br><span class="line">m1.<span class="built_in">lock</span>();</span><br><span class="line">std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">2</span>));</span><br><span class="line">m2.<span class="built_in">lock</span>();</span><br><span class="line">m2.<span class="built_in">unlock</span>();</span><br><span class="line">m1.<span class="built_in">unlock</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func2</span><span class="params">()</span> </span>&#123;</span><br><span class="line">m2.<span class="built_in">lock</span>();</span><br><span class="line">std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">2</span>));</span><br><span class="line">m1.<span class="built_in">lock</span>();</span><br><span class="line">m1.<span class="built_in">unlock</span>();</span><br><span class="line">m2.<span class="built_in">unlock</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(func1)</span></span>;</span><br><span class="line"><span class="function">std::thread <span class="title">t2</span><span class="params">(func2)</span></span>;</span><br><span class="line"></span><br><span class="line">t1.<span class="built_in">join</span>();</span><br><span class="line">t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;执行完成&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在以上代码中两个锁永远没有释放，双方一直在等待对方解开锁就造成了死锁</p><hr><h1 id="模板锁的用法"><a href="#模板锁的用法" class="headerlink" title="模板锁的用法"></a>模板锁的用法</h1><p>有两个主要的模板锁分别是<code>std::lock_guard</code>和<code>std::unique_lock</code>这两者各有自己的不同点主要为：</p><ul><li><code>lock_guard</code>：用于保护数据共享防止多个线程同时访问同一资源而导致的数据竞争问题。</li></ul><ol><li>当构造函数被调用时，该互斥锁会被自动锁定。</li><li>当析构函数被调用时，该互斥锁会自动解除锁定。</li><li><code>std::lock_guard</code>对象不能被复制或者移动，因此他只能在局部作用域中使用。</li></ol><p>用法示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line"><span class="type">int</span> sum =<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i =<span class="number">0</span> ;i&lt;<span class="number">50</span>;i++)&#123;</span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx)</span></span>;</span><br><span class="line">        sum++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(func)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(func)</span></span>;</span><br><span class="line">   t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>unique_lock</code>:用于在多线程程序中对互斥量进行加锁和解锁。主要是可以对互斥量进行更为灵活的管理，包括延迟加载、条件变量、超时等。</li></ul><p>其提供了几个成员方法：</p><ol><li><code>lock()</code>：尝试对互斥量进行加锁操作，如果当前互斥量已经被其他线程持有，则当前线程会被阻塞，直到成功加锁</li><li><code>try_lock()</code>：尝试对互斥量进行加锁操作，如果当前互斥量已经被其他线程所持有，则立即返回<code>false</code>否则返回<code>true</code></li><li><code>try_lock_for(const std::chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</code>：尝试对互斥量进行加锁，入股哦当前互斥量已经被其他线程所持有，则当前线程会被阻塞，直到互斥锁加锁成功或超过了指定的时间。</li><li><code>unlock()</code>：对互斥锁进行解锁操作。</li></ol><p>其中还提供了一些构造函数：</p><ol><li><code>unique_lock()</code>noexcept =default 默认构造函数，创建一个未关联任何互斥量的<code>std::unique_lock</code>对象。</li><li><code>explicit unique_lock(mutex_type&amp; m)</code>构造函数，使用给定的互斥量m进行初始化，并对该互斥量进行加锁。</li><li><code>unique_lock(mutex_type&amp; m,defer_lock_t) noexcept</code>构造函数，使用给定的互斥量m进行初始化，但不对该互斥量进行加锁操作。</li><li><code>unique_lock(mutex_type&amp; m,try_to_lock_t) noexcept</code>构造函数，使用给定的互斥量m进行初始化，并尝试对该互斥量进行加锁操作，如果加锁失败，则创建的std::unique_lock对象不与任何互斥量关联。</li><li><code>unique_lock(mutex&amp; m,adopt_lock_t) noexcept</code>构造函数，使用给定的互斥量m进行初始化并且假定该互斥量已经呗当前线程成功加锁。 </li></ol><p>示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">defer_lock_demo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx, std::defer_lock)</span></span>; <span class="comment">//延迟加锁</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;加锁前&quot;</span>&lt;&lt; std::endl;</span><br><span class="line">    lk.<span class="built_in">lock</span>();               <span class="comment">// 手动加锁</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;加锁后&quot;</span>&lt;&lt;std::endl;</span><br><span class="line">    lk.<span class="built_in">unlock</span>();             <span class="comment">// 手动解锁</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">try_lock_demo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx, std::try_to_lock)</span></span>; <span class="comment">//尝试加锁</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;尝试加锁结果：&quot;</span> &lt;&lt; (lk.<span class="built_in">owns_lock</span>() ? <span class="string">&quot;成功&quot;</span>:<span class="string">&quot;失败&quot;</span>)&lt;&lt;std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">timeout_demo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::unique_lock&lt;std::mutex&gt; lk;</span><br><span class="line">    <span class="keyword">auto</span> now = std::chrono::steady_clock::<span class="built_in">now</span>();</span><br><span class="line">    <span class="keyword">if</span> (lk.<span class="built_in">try_lock_until</span>(now + std::chrono::<span class="built_in">milliseconds</span>(<span class="number">100</span>))) <span class="comment">// 3. 超时加锁</span></span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;100ms 后加锁\n&quot;</span>&lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;100ms后加锁失败&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">adopt_lock_demo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    mtx.<span class="built_in">lock</span>();                                              <span class="comment">// 已加锁</span></span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx, std::adopt_lock)</span></span>;   <span class="comment">// 4. 接管所有权</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;使用 adopt_lock\n&quot;</span>;</span><br><span class="line">&#125;                                                            <span class="comment">// 自动解锁</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(defer_lock_demo)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(try_lock_demo)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(timeout_demo)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t4</span><span class="params">(adopt_lock_demo)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : &#123;std::<span class="built_in">ref</span>(t1), std::<span class="built_in">ref</span>(t2), std::<span class="built_in">ref</span>(t3), std::<span class="built_in">ref</span>(t4)&#125;)</span><br><span class="line">        t.<span class="built_in">join</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h1 id="std-call-once介绍以及使用场景"><a href="#std-call-once介绍以及使用场景" class="headerlink" title="std::call_once介绍以及使用场景"></a>std::call_once介绍以及使用场景</h1><p><code>std::call_once</code>其主要作用是保证一个函数在多个线程中只被调用一次，其原型如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">_EXPORT_STD <span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">_Fn</span>, <span class="keyword">class</span>... _Args&gt;</span><br><span class="line"><span class="built_in">void</span>(call_once)(once_flag&amp; _Once, _Fn&amp;&amp; _Fx, _Args&amp;&amp;... _Ax)</span><br></pre></td></tr></table></figure><p>其中 _Once是标志，Fx是我们需要保证调用一次的函数，Ax则是参数。大多数的适用场景在于构造类的单例模式，如果执行的内容中有单例模式类的构造，有可能出现多个线程同时构造造成的多个实例。如：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">A</span>() &#123;&#125;</span><br><span class="line">    <span class="built_in">A</span>(<span class="type">const</span> A&amp;)            = <span class="keyword">delete</span>;</span><br><span class="line">    A&amp; <span class="keyword">operator</span>=(<span class="type">const</span> A&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对外唯一入口</span></span><br><span class="line">    <span class="function"><span class="type">static</span> A&amp; <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        instance_ = <span class="keyword">new</span> A; </span><br><span class="line">        <span class="keyword">return</span> *instance_;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span> </span>&#123; std::cout &lt;&lt; <span class="string">&quot;A::print() &quot;</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>; &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">static</span> A*             instance_; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>在以上单例创建中，当我们调用线程后，多个线程都执行了创建A的过程的话，就可能回造成同时进行多个getInstance这样类的单例就会被破坏。那么此时就需要call_once来保证其单例模式：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">A</span>(<span class="type">const</span> A&amp;)            = <span class="keyword">delete</span>;</span><br><span class="line">    A&amp; <span class="keyword">operator</span>=(<span class="type">const</span> A&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对外唯一入口</span></span><br><span class="line">    <span class="function"><span class="type">static</span> A&amp; <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        std::<span class="built_in">call_once</span>(initFlag_, []() &#123;</span><br><span class="line">            instance_ = <span class="keyword">new</span> A;      <span class="comment">// 只执行一次</span></span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> *instance_;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span> </span>&#123; std::cout &lt;&lt; <span class="string">&quot;线程为： &quot;</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">A</span>() &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">static</span> std::once_flag initFlag_;</span><br><span class="line">    <span class="type">static</span> A* instance_;   <span class="comment">// 原始指针，永不释放</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">std::once_flag A::initFlag_;</span><br><span class="line">A* A::instance_ = <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::thread pool[<span class="number">10</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : pool)</span><br><span class="line">        t = std::<span class="built_in">thread</span>([]() &#123; A::<span class="built_in">getInstance</span>().<span class="built_in">print</span>(); &#125;);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : pool) t.<span class="built_in">join</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>注意：call_once只能在子线程中使用，不能在主线程中使用（main函数），否则会报错。</li></ul><hr><h1 id="条件变量的使用（condition-variable）"><a href="#条件变量的使用（condition-variable）" class="headerlink" title="条件变量的使用（condition_variable）"></a>条件变量的使用（condition_variable）</h1><p>具体使用流程如下：</p><ol><li>创建一个std::condition_variable对象</li><li>创建一个互斥锁std::mutex对象来保护共享资源的访问。</li><li>在需要等待条件变量的地方：使用<code>std::unique_lock&lt;std::mutex&gt;</code>对象锁定互斥锁并且调用<code>std::condition_variable::await_until()</code>或者<code>std::condition_variable::await_for()</code>函数等待条件变量</li><li>在其他线程中需要通知等待的线程时，调用<code>std::condition_variable::notify_one()</code>或者<code>std::condition_variable::notify_all()</code>来通知等待的线程</li></ol><p>在以上流程中，最经典的场景为：生产者—消费者模型</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::condition_variable cv;</span><br><span class="line">std::queue&lt;<span class="type">int</span>&gt; tasks;</span><br><span class="line">std::mutex mtx;</span><br><span class="line"><span class="type">bool</span> finish = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//生产者</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">producer</span><span class="params">(<span class="type">size_t</span> id,<span class="type">size_t</span> num)</span> </span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; num; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx)</span></span>;</span><br><span class="line">tasks.<span class="built_in">push</span>(i * i);</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;[&quot;</span>&lt;&lt; id &lt;&lt;<span class="string">&quot;] task push&quot;</span> &lt;&lt; i * i &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line">cv.<span class="built_in">notify_all</span>();<span class="comment">//通知所有线程</span></span><br><span class="line">std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">100</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">consumer</span><span class="params">(<span class="type">size_t</span> id)</span> </span>&#123;</span><br><span class="line"><span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx)</span></span>;</span><br><span class="line">cv.<span class="built_in">wait</span>(lk, []() &#123;</span><br><span class="line"><span class="keyword">return</span> !tasks.<span class="built_in">empty</span>() || finish;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">if</span> (!tasks.<span class="built_in">empty</span>()) &#123;</span><br><span class="line"><span class="type">int</span> data = tasks.<span class="built_in">front</span>();</span><br><span class="line">tasks.<span class="built_in">pop</span>();</span><br><span class="line">lk.<span class="built_in">unlock</span>();<span class="comment">//用完即解锁</span></span><br><span class="line">std::cout &lt;&lt;<span class="string">&quot;[&quot;</span>&lt;&lt; id &lt;&lt; <span class="string">&quot;]consumer get data: &quot;</span> &lt;&lt; data &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (finish)</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="type">size_t</span> PRODUCER = <span class="number">2</span>;</span><br><span class="line"><span class="type">size_t</span> CONSUMER = <span class="number">3</span>;</span><br><span class="line"><span class="type">size_t</span> TOTAL = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line">std::vector&lt;std::thread&gt; consumers, producers;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; CONSUMER; i++)</span><br><span class="line">consumers.<span class="built_in">emplace_back</span>(consumer, i);<span class="comment">//启动所有消费者</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; PRODUCER; i++)</span><br><span class="line">producers.<span class="built_in">emplace_back</span>(producer, i, TOTAL);<span class="comment">//启动所有生产者</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : producers)t.<span class="built_in">join</span>(); <span class="comment">//等待所有生产者执行完毕</span></span><br><span class="line">&#123;<span class="comment">//生产结束</span></span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx)</span></span>;</span><br><span class="line">finish = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line">cv.<span class="built_in">notify_all</span>();<span class="comment">//再次提醒所有消费者检查finish，如果队列空了并且finish为true则退出</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : consumers) t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;执行完成&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="线程池具体实现"><a href="#线程池具体实现" class="headerlink" title="线程池具体实现"></a>线程池具体实现</h1><p>代码如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ThreadPool</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">ThreadPool</span><span class="params">(<span class="type">const</span> <span class="type">size_t</span> max_threads)</span> </span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; max_threads; i++) &#123;</span><br><span class="line">threads.<span class="built_in">emplace_back</span>([<span class="keyword">this</span>]() &#123;</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">std::unique_lock&lt;std::mutex&gt; <span class="built_in">lk</span>(mtx);</span><br><span class="line">cv.<span class="built_in">wait</span>(lk, [<span class="keyword">this</span>]() &#123;</span><br><span class="line"><span class="keyword">return</span> !tasks.<span class="built_in">empty</span>() || stop;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (stop &amp;&amp; tasks.<span class="built_in">empty</span>()) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">std::function&lt;<span class="built_in">void</span>()&gt; <span class="built_in">task</span>(std::<span class="built_in">move</span>(tasks.<span class="built_in">front</span>()));</span><br><span class="line">tasks.<span class="built_in">pop</span>();</span><br><span class="line">lk.<span class="built_in">unlock</span>();</span><br><span class="line"><span class="built_in">task</span>();</span><br><span class="line">&#125;</span><br><span class="line">&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">~<span class="built_in">ThreadPool</span>() &#123;</span><br><span class="line">&#123;</span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx)</span></span>;</span><br><span class="line">stop = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line">cv.<span class="built_in">notify_all</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : threads) t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> F,<span class="keyword">typename</span> ...Args&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">enqueue</span><span class="params">(F&amp;&amp; f ,Args &amp;&amp; ...args)</span> </span>&#123;</span><br><span class="line">std::function&lt;<span class="type">void</span>()&gt; task = </span><br><span class="line">std::<span class="built_in">bind</span>(std::forward&lt;F&gt;(f), std::forward&lt;Args&gt;(args)...);</span><br><span class="line">&#123;</span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lk</span><span class="params">(mtx)</span></span>;</span><br><span class="line">tasks.<span class="built_in">emplace</span>(std::<span class="built_in">move</span>(task));</span><br><span class="line">&#125;</span><br><span class="line">cv.<span class="built_in">notify_one</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">std::vector &lt;std::thread&gt; threads;</span><br><span class="line">std::queue &lt;std::function&lt;<span class="type">void</span>()&gt;&gt; tasks;</span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line">std::condition_variable cv;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> stop = <span class="literal">false</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="function">ThreadPool <span class="title">threadpool</span><span class="params">(<span class="number">10</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">threadpool.<span class="built_in">enqueue</span>([i] &#123;</span><br><span class="line"><span class="type">static</span> std::mutex io_mtx; <span class="comment">//为了让输出流正常输出</span></span><br><span class="line">&#123;</span><br><span class="line">std::unique_lock&lt;std::mutex&gt; <span class="built_in">lock</span>(io_mtx);</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;第&quot;</span> &lt;&lt; i &lt;&lt; <span class="string">&quot;个任务开始进行&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line">std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">&#123;</span><br><span class="line">std::unique_lock&lt;std::mutex&gt; <span class="built_in">lock</span>(io_mtx);</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;第&quot;</span> &lt;&lt; i &lt;&lt; <span class="string">&quot;个任务已经完成&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line">&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;线程池执行完毕&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="异步并发"><a href="#异步并发" class="headerlink" title="异步并发"></a>异步并发</h1><ul><li><code>async</code> <code>future</code>:</li></ul><p><code>async``future</code>是一个函数模板并且用于异步执行的一个函数，并且返回值是一个类型为<code>std::future</code>对象，表示异步操作的结果。使用<code>std::async</code>可以很方便的进行异步编程，避免了手动创建线程和管理线程的麻烦。其中调用使用<code>std::async(执行策略,函数, 参数...);</code>。接收使用<code>std::future&lt;返回值类型&gt;</code>，获取结果使用<code>.get()</code>方法。其中执行策略有两种：</p><ol><li><code>std::launch::async</code>:直接开新的线程或者复用线程池异步跑。</li><li><code>std::launch::deferred</code>:惰性求值，只有进行get方法后才进行运行。线程id与main的线程相同</li></ol><p>使用示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">slow_add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> </span>&#123;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">2</span>));</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::async,slow_add, <span class="number">3</span>, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;主线程继续运行&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;结果 = &quot;</span> &lt;&lt; result.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>packaged_task</code>:</li></ul><p>在c++中，<code>packaged_task</code>是一个类模板，用于将一个可调用对象（如函数，函数对象或lambda表达式）封装成一个异步操作，并返回一个<code>std::future</code>对象，表示异步操作的结果。<code>package_task</code>可以方便的将一个函数或可调用对象转换成一个异步操作，供其他线程使用。调用模板为<code>std::packaged_task&lt;返回值类型，(参数1类型，参数2类型，...)&gt; 名称(函数)</code>获取future则使用其<code>.get_future()</code>，其使用需要自己为其分配一个线程运行</p><p>使用示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">add</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b)</span></span>&#123; <span class="keyword">return</span> a+b; &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="type">int</span>,<span class="type">int</span>)</span>&gt; <span class="title">task</span><span class="params">(add)</span></span>;   <span class="comment">// 包函数</span></span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; result = task.<span class="built_in">get_future</span>();  <span class="comment">// 拿 future</span></span><br><span class="line"></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(std::move(task), <span class="number">3</span>, <span class="number">4</span>)</span></span>;         <span class="comment">// 这里需要使用move转换成右值传入</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;packaged_task 结果 = &quot;</span> &lt;&lt; result.<span class="built_in">get</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>promise</code>:</li></ul><p><code>promise</code>用于在一个线程中产生一个值，并且在另一个线程中获取这个值。<code>promise</code>通常和<code>async</code>一起使用实现异步编程。</p><p>基本用法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">work</span><span class="params">(std::promise&lt;<span class="type">int</span>&gt; p)</span></span>&#123;   <span class="comment">// promise 可移动</span></span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    p.<span class="built_in">set_value</span>(<span class="number">42</span>);              <span class="comment">// 手动给结果</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    std::promise&lt;<span class="type">int</span>&gt; prom;</span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt;  result = prom.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(work, std::move(prom))</span></span>;  <span class="comment">// 把 promise 交给干活线程</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;等结果…&quot;</span> &lt;&lt;std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;promise 结果 = &quot;</span> &lt;&lt; result.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="原子操作"><a href="#原子操作" class="headerlink" title="原子操作"></a>原子操作</h1><ul><li><code>std::atomic</code>:</li></ul><p>它提供了一种线程安全的方式来访问和修改共享变量，可以避免多线程环境中的数据竞争问题。他的用法就类似普通变量，但是操作是原子性的，也就意味着在多线程中他不会出现资源竞争问题。</p><p>常用操作：</p><ul><li><code>load()</code>:将<code>std::atomic</code>变量的值加载到当前线程的本地缓存中并且返回这个值。</li><li><code>store(value)</code>：将<code>value</code>的值存储到<code>std::atomic</code>的变量中，并且保证操作是原子性的。</li><li><code>exchange(value)</code>:将<code>value</code>的值存到<code>std::atomic</code>的变量之中，并且返回原来存储的数据。</li><li><code>compare_exchange_weak(expected, desired)</code>:只有当前值等于expected 才改成 desired，返回是否成功；可能假失败(有时确实相等但是依旧返回false，是由于底层执行时进行竞争所造成的，再次检查就可能恢复正常)，适合循环</li><li><code>compare_exchange_strong(expected, desired)</code>：同上，但是不允许假失败，成本高，适合单次判断</li><li><code>fetch_add(delta)</code>  / <code>fetch_sub(delta)</code>:对原子变量进行加减delta</li><li><code>is_lock_free()</code>:查询该对象是否无锁</li><li>同时也支持语法糖：++/- - /+= /-=</li></ul><p>完整示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* op, T old, T now, <span class="type">bool</span> ok = <span class="literal">true</span>)</span> </span>&#123;</span><br><span class="line">    std::cout &lt;&lt; op &lt;&lt; <span class="string">&quot; 旧值=&quot;</span> &lt;&lt; old</span><br><span class="line">              &lt;&lt; <span class="string">&quot; 新值=&quot;</span> &lt;&lt; now</span><br><span class="line">              &lt;&lt; (ok ? <span class="string">&quot; 成功\n&quot;</span> : <span class="string">&quot; 失败&quot;</span>)</span><br><span class="line">          &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::atomic&lt;<span class="type">int</span>&gt; a&#123;<span class="number">10</span>&#125;;          </span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;初始 a = &quot;</span> &lt;&lt; a.<span class="built_in">load</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 1. load / store */</span></span><br><span class="line">    <span class="type">int</span> x = a.<span class="built_in">load</span>();                <span class="comment">// 原子读</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;load  得到 &quot;</span> &lt;&lt; x &lt;&lt; std::endl;</span><br><span class="line">    a.<span class="built_in">store</span>(<span class="number">20</span>);                     <span class="comment">// 原子写</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;store 20 后 a = &quot;</span> &lt;&lt; a.<span class="built_in">load</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="type">int</span> old = a.<span class="built_in">exchange</span>(<span class="number">99</span>);        </span><br><span class="line">    <span class="built_in">show</span>(<span class="string">&quot;exchange(99)&quot;</span>, old, <span class="number">99</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    old = a.<span class="built_in">fetch_add</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="built_in">show</span>(<span class="string">&quot;fetch_add(5)&quot;</span>, old, old + <span class="number">5</span>);</span><br><span class="line">    old = a.<span class="built_in">fetch_sub</span>(<span class="number">3</span>);</span><br><span class="line">    <span class="built_in">show</span>(<span class="string">&quot;fetch_sub(3)&quot;</span>, old, old - <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    ++a;  --a;                       </span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;++a 再 --a 后 a = &quot;</span> &lt;&lt; a.<span class="built_in">load</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="type">int</span> expected = <span class="number">100</span>;</span><br><span class="line">    <span class="type">bool</span> okWeak = a.<span class="built_in">compare_exchange_weak</span>(expected, <span class="number">200</span>);</span><br><span class="line">    <span class="built_in">show</span>(<span class="string">&quot;compare_exchange_weak(100→200)&quot;</span>, <span class="number">100</span>, <span class="number">200</span>, okWeak);</span><br><span class="line"></span><br><span class="line">    expected = <span class="number">101</span>;                  </span><br><span class="line">    okWeak = a.<span class="built_in">compare_exchange_weak</span>(expected, <span class="number">200</span>);</span><br><span class="line">    <span class="built_in">show</span>(<span class="string">&quot;compare_exchange_weak(101→200)&quot;</span>, expected, <span class="number">200</span>, okWeak);</span><br><span class="line"></span><br><span class="line">    expected = a.<span class="built_in">load</span>();             </span><br><span class="line">    <span class="type">bool</span> okStrong = a.<span class="built_in">compare_exchange_strong</span>(expected, <span class="number">300</span>);</span><br><span class="line">    <span class="built_in">show</span>(<span class="string">&quot;compare_exchange_strong(当前→300)&quot;</span>, expected, <span class="number">300</span>, okStrong);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 5. 无锁查询 */</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;\nis_lock_free = &quot;</span></span><br><span class="line">              &lt;&lt; (a.<span class="built_in">is_lock_free</span>() ? <span class="string">&quot;true&quot;</span> : <span class="string">&quot;false&quot;</span>) &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;hr&gt;
&lt;h1 id=&quot;线程的创建和使用&quot;&gt;&lt;a href=&quot;#线程的创建和使用&quot; class=&quot;headerlink&quot; title=&quot;线程的创建和使用&quot;&gt;&lt;/a&gt;线程的创建和使用&lt;/h1&gt;&lt;p&gt;​        线程库中创建多线程的库是thread库,基本的线程创建是使用st</summary>
      
    
    
    
    <category term="C++进阶" scheme="https://www.lysnowq.cn/categories/C-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="C++学习" scheme="https://www.lysnowq.cn/tags/C-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>伪共享</title>
    <link href="https://www.lysnowq.cn/posts/88d55383.html"/>
    <id>https://www.lysnowq.cn/posts/88d55383.html</id>
    <published>2025-11-06T08:02:10.898Z</published>
    <updated>2025-11-07T07:05:48.684Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="CPU基本架构了解"><a href="#CPU基本架构了解" class="headerlink" title="CPU基本架构了解"></a>CPU基本架构了解</h1><p>伪共享本质是cpu的缓存问题，那么首先先介绍cpu的读写问题。        </p><p>首先是cpu的架构。cpu一般不止一个核心，有多个核心，其中如上图，每个核心自己会有两个缓存区，即L1 Cache(由指令缓存区（iCache）和数据缓存区（dCache）组成)，L2 Cache这两个缓存区间，其中所有核心还有个共享缓存区间，即L3 Cache。以上就是cpu的典型架构。</p><p>​        共享缓存中的内容遵循MESI协议。MESI 协议是指一种缓存一致性协议，用于确保多核处理器系统中各个核心的缓存数据与内存数据保持一致。其通过跟踪每个缓存行的状态来管理数据的一致性，这四个状态的首字母分别代表：Modified、Exclusive、Shared 和 Invalid。这个协议允许处理器核心之间进行有效的数据同步，从而避免数据不一致的问题。</p><p>​        其中L1和L2的随机访问延时分别是1ns和4ns。这里缓存器主要作用是将数据提前写入缓存层，减少对内存的频繁访问量和节省时间。当cpu从内存读取数据时并非是将数据挨个按照字节读取，而是一次性写入一整块内存内容，这一整块内容我们称之为Cache Line(缓存行)，即<strong>Cache Line是cpu从内存读取数据到Cache的单位</strong>。其中对数组的加载，CPU会加载连续多个数据到Cache,那么在我们访问元素时使用物理内存分布顺序去访问，就会大大提高Cache的命中率，能减少从内存读取的频率提高程序性能。</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/cpu%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84.png" alt=""></p><hr><h1 id="什么是伪共享，伪共享是怎么形成的"><a href="#什么是伪共享，伪共享是怎么形成的" class="headerlink" title="什么是伪共享，伪共享是怎么形成的"></a>什么是伪共享，伪共享是怎么形成的</h1><p>​        上面我们说过，在同一个 Cache Line 中遵循缓存一致性原则，那么伪共享就是在这个过程中Cache Line中同时包含了核心1和核心2分别要使用的数据的物理地址，那么此时在代码中虽然是多线程进行，但是其实没有进行真正的分开同步操作。具体场景流程如下：</p><ul><li>有两个变量a、b在同一个缓存行中</li><li>两个线程：<ul><li>线程1：绑定在cpu核心1，只对a进行写操作</li><li>线程2：绑定在cpu核心2，只对b进行读操作</li></ul></li><li>初始状态：还未被任何核心读取</li></ul><p>此时在以上背景下可能出现已下事件：</p><ol><li>线程2读取了b，那么缓存行载入核心2的缓存，此时核心2的缓存标记为shared，此时数据未作任何修改是与L3 Cache中的内容是一样的。</li><li>线程1读取了a,加载了该缓存行，此时核心1的状态为shared,同时这时也未做任何的数据修改，数据与共享缓存中的数据是一致的。</li><li>线程1准备开始修改数据a,此时状态再次更改为Exclusive，<strong>拿到这块内存的唯一主导权</strong>。</li><li><strong>线程1修改数据a,此时状态更改为Modified，核心2中的数据状态更改为Invalid</strong>，此时因为核心2要读取数据b内容，由于<strong>缓存行数据不一致</strong>根据缓存一致性原则就<strong>导致核心2要重新载入缓存行，那么此时重新更新状态为shared</strong>。</li></ol><p>根据以上情景可以得知在多线程中如果多个资源之间正好放到了同一个缓存行中，那么在cpu处理时就会跳入主导权的反复更迭以及数据的重复加载，这就是伪共享的形成方式</p><hr><h1 id="解决伪共享的方法"><a href="#解决伪共享的方法" class="headerlink" title="解决伪共享的方法"></a>解决伪共享的方法</h1><ul><li>使用alignas</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">alignas</span>(<span class="number">64</span>) ThreadData&#123;</span><br><span class="line">    <span class="type">int</span> counter;</span><br><span class="line">    <span class="comment">//......</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">优点：语法简洁易懂</span></span><br><span class="line"><span class="comment">缺点：整个结构体大小必须为64的倍数，不然仍有可能与其他变量共享缓存行</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><ul><li>手动填充</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">PaddedCounter</span>&#123;</span><br><span class="line">  <span class="type">int</span> value;</span><br><span class="line">  <span class="type">char</span> padding[<span class="number">64</span> - <span class="built_in">sizeof</span>(<span class="type">int</span>)];<span class="comment">//补充到64</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">优点：兼容性好，控制精细</span></span><br><span class="line"><span class="comment">缺点：可读性差，且浪费内存，线程较多时内存膨胀明显</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><ul><li>使用 std::hardware_destructive_interference_size 来获取Cache Line的大小(c++ 17)</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;new&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __cpp_lib_hardware_interference_size</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">constexpr</span> std::<span class="type">size_t</span> kCacheLine = </span><br><span class="line">    std::hardware_destructive_interference_size;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="type">static</span> <span class="keyword">constexpr</span> std::<span class="type">size_t</span> kCacheLine = <span class="number">64</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">alignas</span>(kCacheLine) ThreadData&#123;</span><br><span class="line">    std::atomic&lt;<span class="type">int</span>&gt; counter&#123;<span class="number">0</span>&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//优点：可移植性好，比手动定义更加可靠</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>c++17以及以上使用new自动对齐</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">alignas</span>(<span class="number">64</span>) MyData&#123;<span class="type">int</span> x;&#125;</span><br><span class="line"><span class="keyword">auto</span>* p= <span class="keyword">new</span> MyData</span><br></pre></td></tr></table></figure><ul><li>使用thread_local避免共享，让每个线程拥有独立的副本从根本上杜绝共享</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">thread_local</span> <span class="type">int</span> local_counter =<span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">worker</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">1000000</span>;i++) ++ local_counter;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">优点：零竞争零同步</span></span><br><span class="line"><span class="comment">缺点：无法直接跨线程会总结结果，需要额外的合并逻辑</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><ul><li>调整数据访问模式</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//对于数组,缓存行大小跳步，避免线程之间踩到同一行</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> KIntPerLine  =<span class="number">64</span>/<span class="built_in">sizeof</span>(<span class="type">int</span>);</span><br><span class="line"><span class="function">std::vector&lt;<span class="type">int</span>&gt; <span class="title">vec</span><span class="params">(KIntPerLine * <span class="number">8</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">process</span> <span class="params">(<span class="type">int</span> tid)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = tid*KIntPerLine;i&lt;vec.<span class="built_in">size</span>();i+=KIntPerLine * <span class="number">8</span>)</span><br><span class="line">    vec[i] += <span class="number">1</span>; <span class="comment">//每个线程间隔16个int，保证不共享缓存行</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        </p>]]></content>
    
    
      
      
    <summary type="html">&lt;hr&gt;
&lt;h1 id=&quot;CPU基本架构了解&quot;&gt;&lt;a href=&quot;#CPU基本架构了解&quot; class=&quot;headerlink&quot; title=&quot;CPU基本架构了解&quot;&gt;&lt;/a&gt;CPU基本架构了解&lt;/h1&gt;&lt;p&gt;伪共享本质是cpu的缓存问题，那么首先先介绍cpu的读写问题。      </summary>
      
    
    
    
    <category term="c++进阶" scheme="https://www.lysnowq.cn/categories/c-%E8%BF%9B%E9%98%B6/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++中的计时器</title>
    <link href="https://www.lysnowq.cn/posts/eb139d8.html"/>
    <id>https://www.lysnowq.cn/posts/eb139d8.html</id>
    <published>2024-10-04T03:33:15.254Z</published>
    <updated>2024-10-10T11:02:54.231Z</updated>
    
    <content type="html"><![CDATA[<h1 id="计时器"><a href="#计时器" class="headerlink" title="计时器"></a>计时器</h1><p>c++中我们经常需要去计算代码的运行时长，或者需要知道代码的运行时长而去加入一些特定条件的使用，此时就需要计时器。一般使用chrono库，可以不需要使用操作系统的库并且几乎支持所有平台，但是如果需要高精度的计时器则需要使用操作系统库。</p><p>使用示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std::literals::chrono_literals;<span class="comment">//后续使用1s时需要</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> start = std::chrono::high_resolution_clock::<span class="built_in">now</span>();<span class="comment">//记录当前时间</span></span><br><span class="line"></span><br><span class="line">std::this_thread::<span class="built_in">sleep_for</span>(<span class="number">1</span>s);</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> end = std::chrono::high_resolution_clock::<span class="built_in">now</span>();<span class="comment">//记录当前时间</span></span><br><span class="line"></span><br><span class="line">std::chrono::duration&lt;<span class="type">float</span>&gt; time = end - start;<span class="comment">//以float方式记录经过时间</span></span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; time.<span class="built_in">count</span>() &lt;&lt; <span class="string">&quot;s&quot;</span> &lt;&lt; std::endl;<span class="comment">//时间输出</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一个更具体的使用方法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Timer</span><span class="comment">//自助打印运行时间</span></span><br><span class="line">&#123;</span><br><span class="line">std::chrono::time_point&lt;std::chrono::steady_clock&gt; start, end;</span><br><span class="line">std::chrono::duration&lt;<span class="type">float</span>&gt; time;</span><br><span class="line"><span class="built_in">Timer</span>()</span><br><span class="line">&#123;</span><br><span class="line">start = std::chrono::high_resolution_clock::<span class="built_in">now</span>();</span><br><span class="line">&#125;</span><br><span class="line">~<span class="built_in">Timer</span>()</span><br><span class="line">&#123;</span><br><span class="line">end = std::chrono::high_resolution_clock::<span class="built_in">now</span>();</span><br><span class="line">time = end - start;</span><br><span class="line"></span><br><span class="line"><span class="type">float</span> ms = time.<span class="built_in">count</span>() * <span class="number">1000.0f</span>;</span><br><span class="line"><span class="type">float</span> s = time.<span class="built_in">count</span>();</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; s &lt;&lt; <span class="string">&quot;s or&quot;</span> &lt;&lt; ms &lt;&lt; <span class="string">&quot;ms&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">&#123;<span class="comment">//限定作用域</span></span><br><span class="line">Timer time;</span><br><span class="line"><span class="type">int</span> k = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++)</span><br><span class="line">k++;</span><br><span class="line">&#125;</span><br><span class="line">std::cin.<span class="built_in">get</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意：在上述代码在调试模式（debug）使用时会正常显示每一步的过程，但是如果在发布模式（relase）使用时由于编译器会自动帮我们省略简单计算以达到优化目的，此处代码实际会被简化为哦k被直接赋值100，所以测试显示的时间将会非常短与原方式不和。</p><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列</p>]]></content>
    
    
    <summary type="html">c++中的计时</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++中类型双关和关联体</title>
    <link href="https://www.lysnowq.cn/posts/5fc6f6d7.html"/>
    <id>https://www.lysnowq.cn/posts/5fc6f6d7.html</id>
    <published>2024-10-02T11:43:38.222Z</published>
    <updated>2024-10-02T13:58:02.770Z</updated>
    
    <content type="html"><![CDATA[<h1 id="类型双关"><a href="#类型双关" class="headerlink" title="类型双关"></a>类型双关</h1><p>c++作为一个强类型语言，它拥有一个类型系统 ，当我们在创建变量时必须声明整数，双精度，单精度，布尔类型或者结构体等，并且这种类型系统并不强制进行，在c++中即使类型时编译器强行执行的，但是<strong>我们可以直接访问内存</strong>。即，当我们想将一段整形类型内存当作double使用时我们可以直接访问它的内存，可以轻易饶过类型系统，如果有一个类或者结构，没有指向别处的指针，那么我们可以重新对解释整个结构或者类。在应用时，比如将一个类型变成一个字节数组写出来等。但是<strong>除非需要，否则不要轻易使用</strong>，这也是c++的效率很高的原因。</p><p>代码解释如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">int</span> a = <span class="number">50</span>;</span><br><span class="line"><span class="type">double</span> value = a;</span><br><span class="line">std::cout &lt;&lt; value &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上例子中我们在第7行加上断点，取a地址内存时如下</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002201103261.png" alt="image-20241002201103261"></p><p>而我们的value的地址如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002201134523.png" alt="image-20241002201134523"></p><p>由此我们发现此处49和40对应不上我们的数值，这是因为在程序中进行了隐式转换，实际上他不知道要转换成什么。但是还是照旧转换。</p><p>那么我们如果想将int对应的地址直接当作double来看呢？我们使用一个较为原始的方法来，如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">int</span> a = <span class="number">50</span>;</span><br><span class="line"><span class="type">double</span> value = *(<span class="type">double</span>*) &amp;a;<span class="comment">//此处即为类型双关</span></span><br><span class="line">std::cout &lt;&lt; value &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处运行结果为:</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002201621771.png" alt="image-20241002201621771"></p><p>此处我们再来看value的地址如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002201734325.png" alt="image-20241002201734325"></p><p>此处我们发现double是8位，这里则有后四位均是未初始化内存。这个过程<strong>是在我们int内存之后继续增加了4个字节的位置，然后复制过来在一个新空间使用</strong>，但是虽然可以写入，但是在读取时我们依旧读取了int后那不属于自己的四位内存，这很不好。甚至当我们不想复制直接将int当double时我们可以使用引用 即</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">double</span>&amp; value = *(<span class="type">double</span>*) &amp;a;</span><br></pre></td></tr></table></figure><p><strong>此处是非常危险的，因为这样在修改时，我们会修改int后新增的4位，会导致修改不属于我们的内存。</strong></p><p>下面使用一个更具象的例子：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Entity</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> x, y;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">Entity e = &#123; <span class="number">5</span>,<span class="number">8</span> &#125;;</span><br><span class="line"></span><br><span class="line">std::cin.<span class="built_in">get</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上代码中我们寻址e的地址发现：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002202837041.png" alt="image-20241002202837041"></p><p>里面存储了5，8在结构体中，如果没有任何成员等，它至少会有一个字节的大小，因为需要寻址，如果有成员变量，那么他们是相互挨在一起的。并且只有这成员变量的大小，类似数组。我们可以直接访问内存的方式得到这些值。</p><p>示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Entity</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> x, y;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">int</span>* <span class="title">GetPositions</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> &amp;x;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">Entity e = &#123; <span class="number">5</span>,<span class="number">8</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span>* position = (<span class="type">int</span>*)&amp;e;</span><br><span class="line"><span class="comment">//或者 int y =*(int*)((char*)&amp;e + 4);</span></span><br><span class="line">std::cout &lt;&lt; position[<span class="number">0</span>] &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; position[<span class="number">1</span>] &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//第二种方式</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    int* position = e.GetPositions()</span></span><br><span class="line"><span class="comment">    std::cout &lt;&lt; position[0] &lt;&lt; &quot;,&quot; &lt;&lt; position[1] &lt;&lt; std::endl;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">    std::cin.<span class="built_in">get</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上代码我们直接通过访问内存来访问数值输出结果为：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002203300721.png" alt="image-20241002203300721"></p><hr><h1 id="联合体"><a href="#联合体" class="headerlink" title="联合体"></a>联合体</h1><p>联合体（union）有点类似于类，但是它一次只能占用一 个成员内存，比如当我声明4个浮点数时在类中会占用4*4个字节，在union中则为4个字节。但是由于四个公用相同的内存，则改变其中一个值则其他的会跟着改变。他没有虚函数。一般和类型双关使用居多。</p><p>使用示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Union</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">union</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">float</span> a;</span><br><span class="line"><span class="type">int</span> b;</span><br><span class="line">&#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">Union u;</span><br><span class="line">u.a = <span class="number">2.0f</span>;</span><br><span class="line">std::cout &lt;&lt; u.a &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; u.b &lt;&lt; std::endl;</span><br><span class="line">std::cin.<span class="built_in">get</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出结果如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002214214693.png" alt="image-20241002214214693"></p><p>此处a,b公用内存，即b的值为用a的内存存储的数值用int类型解释所得。即同一内存地址被不同类型解释</p><p>下面进行一个更加直观的例子：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Vector2</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">float</span> x,y;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Vector4</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">union</span></span><br><span class="line">  &#123;</span><br><span class="line">      <span class="keyword">struct</span></span><br><span class="line">      &#123;</span><br><span class="line">          <span class="type">float</span> x,y,z,w;</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="keyword">struct</span></span><br><span class="line">      &#123;</span><br><span class="line">          Vector2 a,b;</span><br><span class="line">      &#125;;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintVector</span><span class="params">(<span class="type">const</span> Vector2&amp; vector)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::cout &lt;&lt; vector.x &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; vector.y &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Vector4 vector = &#123;<span class="number">1.0f</span>, <span class="number">2.0f</span>, <span class="number">3.0f</span>,<span class="number">4.0f</span>&#125;;</span><br><span class="line">    <span class="built_in">PrintVector</span>(Vector.a);</span><br><span class="line">    <span class="built_in">PrintVector</span>(Vector.b);</span><br><span class="line">    vector.z =<span class="number">500.0f</span></span><br><span class="line">    std::cout &lt;&lt; std::endl;</span><br><span class="line">    <span class="built_in">PrintVector</span>(Vector.a);</span><br><span class="line">    <span class="built_in">PrintVector</span>(Vector.b);</span><br><span class="line">    </span><br><span class="line">    std::cin.<span class="built_in">get</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20241002215350344.png" alt="image-20241002215350344"></p><p>此处可以发现，a对应x则b是对应z的位置，所以a占据x地址，b占据z地址。</p><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列</p>]]></content>
    
    
    <summary type="html">c++中类型双关和关联体基础概念及使用场景</summary>
    
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/tags/c-%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>c++的线程和名称空间</title>
    <link href="https://www.lysnowq.cn/posts/3d22a4b0.html"/>
    <id>https://www.lysnowq.cn/posts/3d22a4b0.html</id>
    <published>2024-09-30T07:14:26.262Z</published>
    <updated>2024-10-14T10:00:16.928Z</updated>
    
    <content type="html"><![CDATA[<h1 id="命名空间"><a href="#命名空间" class="headerlink" title="命名空间"></a>命名空间</h1><p>c++中为了命名空间的出现更好的解决了命名冲突，并且在使用时可以更加的清晰了解到函数的来源和出处，相比于c语言中的处理函数名字冲突的状况，c++的命名空间使得代码的可读性更高，使用和规则简洁，<strong>但是在使用时应当尽量避免全局使用using namespace和在头文件使用using namespace 因为这会使得你在调用头文件时会增加代码的不确定性</strong>在使用using namespace时要尽量缩小起使用域，不然就会破坏其提供的便捷能力。本质上来说类也是一种命名空间，所以也可以拿class来定义函数使用。</p><p>语法：”namespace 名称{代码块}“ 或”namespace 名称{namespace 名称{代码块}}“（可以一直嵌套）</p><p>调用时语法：空间名字::函数名</p><p>使用示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> example1</span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;hello&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> example2</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">namespace</span> inner</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            std::cout &lt;&lt; <span class="string">&quot;This is another function!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//使用example1中的Print函数</span></span><br><span class="line">    example1::<span class="built_in">Print</span>();</span><br><span class="line">    <span class="comment">//使用example2中的Print函数</span></span><br><span class="line">    example2::inner::<span class="built_in">Print</span>();</span><br><span class="line">    <span class="comment">//或者</span></span><br><span class="line">    <span class="keyword">using</span> <span class="keyword">namespace</span> example2::inner;</span><br><span class="line">    <span class="built_in">Print</span>();</span><br><span class="line">    <span class="comment">//也可以给空间取一个别名</span></span><br><span class="line">    <span class="keyword">namespace</span> e = example2::inner;</span><br><span class="line">    e::<span class="built_in">Print</span>();</span><br><span class="line">    <span class="comment">//嵌套命名空间也可以分开写</span></span><br><span class="line">    <span class="keyword">using</span> <span class="keyword">namespace</span> example2;</span><br><span class="line">    <span class="keyword">using</span> <span class="keyword">namespace</span> inner;</span><br><span class="line">    <span class="built_in">Print</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h1 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h1><p>在平常编程过程中，我们所编写的指令都只用了cpu的一个线程，只使用单独线程时当程序中有等待时整个程序都会停下来，使得程序运行效率大大降低，而cpu本身支持多线程运行，也就使得当其中一条指令暂停时程序可以同步进行其他的事情，并且在繁冗的代码中需要大量的计算，多线程可以增加程序处理速度。</p><p>基本语法使用：</p><p>std::thread 线程名(函数指针);多线程调用</p><p>线程名.join();让当前线程等待此线程完工（阻塞当前线程直到另一个线程完工）</p><p>std::this_thread::sleep_for(1s);在当前线程中等待1s后再执行后续内容</p><p>使用示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">bool</span> s_Finished = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">using</span> <span class="keyword">namespace</span> std::literals::chrono_literals;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;当前线程编号为：&quot;</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (!s_Finished)</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Hello!&quot;</span>&lt;&lt; x &lt;&lt; std::endl;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(<span class="number">1</span>s);<span class="comment">//等待1s</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">worker</span><span class="params">(Print,x)</span></span>;<span class="comment">//程序开始在另一个线程上运行Print函数,如果需要传参数接在函数指针后即可如</span></span><br><span class="line">    std::cin.<span class="built_in">get</span>();<span class="comment">//主线程中按任意键触发后续代码</span></span><br><span class="line">    s_Finished = <span class="literal">true</span>;<span class="comment">//停止Print函数的循环（即结束Print函数）</span></span><br><span class="line"></span><br><span class="line">    worker.<span class="built_in">join</span>();<span class="comment">//只有Print函数结束后才会执行这行代码以后的代码。</span></span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Finished!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;当前线程编号为：&quot;</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列</p>]]></content>
    
    
    <summary type="html">c++的名称空间和线程的使用</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>函数指针和lambda函数</title>
    <link href="https://www.lysnowq.cn/posts/8802863d.html"/>
    <id>https://www.lysnowq.cn/posts/8802863d.html</id>
    <published>2024-09-28T05:44:13.113Z</published>
    <updated>2025-11-07T06:55:59.040Z</updated>
    
    <content type="html"><![CDATA[<h1 id="函数指针"><a href="#函数指针" class="headerlink" title="函数指针"></a>函数指针</h1><p>函数的本质是一个指针，在c++中我们可以将函数赋值给一个变量，但是在实际过程中，这个变量得到的是函数的地址，也就是函数指针，在cpu中我们每次编译函数都是由cpu直接编译，存在于二进制文件中。</p><p>则函数变量的类型语法为：返回值类型（*变量名）（函数参数类型）；</p><p>示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">HelloWorld</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;HelloWorld&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintNumber</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;HelloWorld&quot;</span> &lt;&lt; x &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">HelloWorld</span>();<span class="comment">//直接调用函数</span></span><br><span class="line"><span class="keyword">auto</span> fun1 = HelloWorld;<span class="comment">//将函数赋值给一个变量。此时实际上是一个指针</span></span><br><span class="line"><span class="comment">//auto的具体含义如下</span></span><br><span class="line"><span class="built_in">void</span> (*fun2)();</span><br><span class="line">fun2 = HelloWorld;</span><br><span class="line">    <span class="comment">//带有参数的类型</span></span><br><span class="line">    <span class="built_in">void</span> (*fun3)(<span class="type">int</span>);</span><br><span class="line">fun2 = PrintNumber;</span><br><span class="line">    <span class="comment">//或者直接赋值</span></span><br><span class="line">    <span class="built_in">void</span> (*fun4)(<span class="type">int</span>)=PrintNumber;</span><br><span class="line">    <span class="comment">//使用时如同原函数一样使用</span></span><br><span class="line">    <span class="built_in">fun1</span>();</span><br><span class="line">    <span class="built_in">fun2</span>();</span><br><span class="line">    <span class="built_in">fun3</span>(<span class="number">3</span>);</span><br><span class="line">    <span class="built_in">fun4</span>(<span class="number">4</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//一些其他定义</span></span><br><span class="line">    <span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*HelloWorldFun)</span><span class="params">()</span></span>;</span><br><span class="line">    HelloWorldFun fun5 = HelloWorld ;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由上述可以看出，在函数变量的类型定义中auto才是经常使用的类型，因为函数的参数修改后类型往往跟着一起修改，使用auto往往能更方便省力。函数指针有较为广泛的实际用途。</p><p>示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">(<span class="type">int</span> value)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Hello&quot;</span> &lt;&lt; value &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ForEach</span><span class="params">(<span class="type">const</span> std::vector&lt;<span class="type">int</span>&gt;&amp; values, <span class="type">void</span> (*func)(<span class="type">int</span>))</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> value : values)</span><br><span class="line"><span class="built_in">func</span>(value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; values = &#123; <span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">9</span> &#125;;</span><br><span class="line"><span class="built_in">ForEach</span>(values, Print);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="lambda函数"><a href="#lambda函数" class="headerlink" title="lambda函数"></a>lambda函数</h1><p>lambda的本质是一种匿名函数，它就像是一种快速的一次性的函数，用完即废弃，它比起普通的函数作为符号，更倾向于将他视为一个变量。lambda就是一种不需要函数定义方式就可以定义一个函数的方法。</p><p>语法：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[传入方式](参数)&#123;函数体&#125;</span><br></pre></td></tr></table></figure><p>使用示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ForEach</span><span class="params">(<span class="type">const</span> std::vector&lt;<span class="type">int</span>&gt;&amp; values, <span class="type">void</span> (*func)(<span class="type">int</span>))</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> value : values)</span><br><span class="line"><span class="built_in">func</span>(value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; values = &#123; <span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">9</span> &#125;;</span><br><span class="line"><span class="built_in">ForEach</span>(values, [](<span class="type">int</span> value) &#123;std::cout &lt;&lt; <span class="string">&quot;Value:&quot;</span> &lt;&lt; value &lt;&lt; std::endl; &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果lambda函数想要调用外部的变量就需要用到[]即他的捕获方式再[]中我们可以不填，也可以填一个或多个中间用逗号分开</p><p>使用如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ForEach</span><span class="params">(<span class="type">const</span> std::vector&lt;<span class="type">int</span>&gt;&amp; values,<span class="type">const</span> std::function&lt;<span class="type">void</span>(<span class="type">int</span>)&gt;&amp; func)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> value : values)</span><br><span class="line"><span class="built_in">func</span>(value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;<span class="type">int</span>&gt; values = &#123; <span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">9</span> &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> a = <span class="number">5</span>;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    //值传递，传入所有需要的值</span></span><br><span class="line"><span class="comment">    auto lambda = [=](int value) &#123;std::cout &lt;&lt; &quot;Value1:&quot; &lt;&lt; value &lt;&lt; &quot;value2:&quot; &lt;&lt; a &lt;&lt; std::endl; &#125;;</span></span><br><span class="line"><span class="comment">    //引用传递</span></span><br><span class="line"><span class="comment">    auto lambda = [&amp;](int value) &#123;std::cout &lt;&lt; &quot;Value1:&quot; &lt;&lt; value &lt;&lt; &quot;value2:&quot; &lt;&lt; a &lt;&lt; std::endl; &#125;;</span></span><br><span class="line"><span class="comment">    //直接指定需要的变量</span></span><br><span class="line"><span class="comment">    auto lambda = [a](int value) &#123;std::cout &lt;&lt; &quot;Value1:&quot; &lt;&lt; value &lt;&lt; &quot;value2:&quot; &lt;&lt; a &lt;&lt; std::endl; &#125;;</span></span><br><span class="line"><span class="comment">    //单独指定引用</span></span><br><span class="line"><span class="comment">    auto lambda = [&amp;a](int value) &#123;std::cout &lt;&lt; &quot;Value1:&quot; &lt;&lt; value &lt;&lt; &quot;value2:&quot; &lt;&lt; a &lt;&lt; std::endl; &#125;;</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="comment">//如果需要修改值传入的参数那么我们需要使用mutable</span></span><br><span class="line">    <span class="keyword">auto</span> lambda = [=](<span class="type">int</span> value) <span class="keyword">mutable</span> &#123;a = <span class="number">6</span>;std::cout&lt;&lt;<span class="string">&quot;Value1:&quot;</span>&lt;&lt;value&lt;&lt;<span class="string">&quot;value2:&quot;</span>&lt;&lt;a&lt;&lt;std::endl;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">ForEach</span>(values, lambda);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>在上述使用时，我们的lambda函数由于使用的是原始函数指针，所以在传入参数时必须使用std::function,并且在传入时必须使用const修饰</strong>。 在进行值传递后修改值需要使用mutable，不需要修改时则可以省略不写 ，并且不会修改外部的值，如果是引用传递则会修改外部值。</p><p>lambda的应用与find_if有所结合</p><p>示例：</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;<span class="type">int</span>&gt; values = &#123;<span class="number">1</span>,<span class="number">5</span>,<span class="number">3</span>,<span class="number">7</span>,<span class="number">2</span>&#125;;</span><br><span class="line">    <span class="keyword">auto</span> it = std::<span class="built_in">find_if</span>(values.<span class="built_in">begin</span>(),values.<span class="built_in">end</span>(),[](<span class="type">int</span> value)&#123;<span class="keyword">return</span> value &gt; <span class="number">3</span>;&#125;);</span><br><span class="line">    cout &lt;&lt; *it &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述代码中就会返回第一个大于3的数即5。</p><hr><h1 id="资料来源"><a href="#资料来源" class="headerlink" title="资料来源"></a>资料来源</h1><p>youtube上the cherno的c++系列</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;函数指针&quot;&gt;&lt;a href=&quot;#函数指针&quot; class=&quot;headerlink&quot; title=&quot;函数指针&quot;&gt;&lt;/a&gt;函数指针&lt;/h1&gt;&lt;p&gt;函数的本质是一个指针，在c++中我们可以将函数赋值给一个变量，但是在实际过程中，这个变量得到的是函数的地址，也就是函数指针，</summary>
      
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++处理多返回值</title>
    <link href="https://www.lysnowq.cn/posts/7490c551.html"/>
    <id>https://www.lysnowq.cn/posts/7490c551.html</id>
    <published>2024-09-21T15:10:19.342Z</published>
    <updated>2024-10-03T12:00:52.849Z</updated>
    
    <content type="html"><![CDATA[<h1 id="返回值处理"><a href="#返回值处理" class="headerlink" title="返回值处理"></a>返回值处理</h1><p>在代码的编译运行时，常常需要一个函数最后返回多个结果，返回的结果中可能是类型相同的也可能时不同的，多个返回类型已经不能或者用现有的方式能很好的解决这个问题，如果是同一类型的返回值，我们可以返回数组，如果是多个不同的返回值，那么已经显得比较乏力，如何处理返回值已经成为了重要的问题。 </p><ul><li>返回多个值方式1：将其封装为一个类再返回</li></ul><p>我们可以将所有需要返回的数据类型扔进一个结构体，在返回时只需要返回一个结构体即可。</p><p>示例代码如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Returntype</span><span class="comment">//我们将需要返回的值放入结构体中，只需要添加自己想要的返回值内容就可以</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> x;</span><br><span class="line">    <span class="type">int</span> y;</span><br><span class="line">    std::string s1;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">Returntype <span class="title">Example</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b,std::string s)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    a=a+<span class="number">10</span>;</span><br><span class="line">    b=a+<span class="number">15</span>;</span><br><span class="line">    name=s;</span><br><span class="line">    </span><br><span class="line">    Returntype r1;</span><br><span class="line">    r1.x=a;</span><br><span class="line">    r1.y=b;</span><br><span class="line">    r1.s1=name;</span><br><span class="line">    <span class="keyword">return</span> r1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">int</span> a=<span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b=<span class="number">1</span>;</span><br><span class="line">    std::string name=<span class="string">&quot;LYsnowQ&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    Returntype Re;</span><br><span class="line">    Re=<span class="built_in">Example</span>(a,b,name);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>返回方式2：使用地址指针传参</li></ul><p>在传参时直接将地址传入，&amp;取地址符号相当于在函数内部直接修改参数值，所以不需要返回值即可直接对main参数进行修改,或者可以使用指针传入变量地址，再在内部解引用后进行赋值操作。</p><p><strong>两者区别在于：取地址必须传入有效的参数，而指针可以传入空指针（nullptr）</strong></p><p>示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="comment">//以下是使用引用</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Example</span><span class="params">(<span class="type">int</span>&amp; a,<span class="type">int</span>&amp; b,std::string&amp; s)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    a=a+<span class="number">10</span>;</span><br><span class="line">    b=a+<span class="number">15</span>;</span><br><span class="line">    s=<span class="string">&quot;LYsnowQ&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">int</span> a=<span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b=<span class="number">1</span>;</span><br><span class="line">    std::string name;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Example</span>(a,b,name);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//以下是使用指针</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Example</span><span class="params">(<span class="type">int</span>* a,<span class="type">int</span>* b,std::string* s)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(a!=<span class="number">0</span>)</span><br><span class="line">    *a=*a+<span class="number">10</span>;</span><br><span class="line">    <span class="keyword">if</span>(b!=<span class="number">0</span>)</span><br><span class="line">    *b=*a+<span class="number">15</span>;</span><br><span class="line">    <span class="keyword">if</span>(s!=<span class="number">0</span>)</span><br><span class="line">    *s=<span class="string">&quot;LYsnowQ&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">int</span> a=<span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b=<span class="number">1</span>;</span><br><span class="line">    std::string name;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Example</span>(&amp;a,&amp;b,&amp;name);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>返回方式3：使用数组，如array和vector</li></ul><p><strong>array和vector的区别在array是直接在栈上进行操作，返回时要快于vector，因为vector底层是在堆上进行操作。array在使用时还需要规定长度，所以在使用便携性能上array不如vector方便</strong></p><p><strong>而array和vector的共同缺陷是只能返回相同类型的变量，而多个类型混合时显得乏力</strong></p><p>示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;array&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">std::array&lt;<span class="type">int</span>,2&gt; <span class="title">Example</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    a = a + <span class="number">10</span>;</span><br><span class="line">    b = a + <span class="number">15</span>;</span><br><span class="line"></span><br><span class="line">    std::array&lt;<span class="type">int</span>,2&gt; results;</span><br><span class="line">    results[<span class="number">0</span>] = a;</span><br><span class="line">    results[<span class="number">1</span>] = b;</span><br><span class="line">    <span class="keyword">return</span> results;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    std::array&lt;<span class="type">int</span> ,2&gt; Re = <span class="built_in">Example</span>(a, b);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用vector</span></span><br><span class="line"><span class="function">std::vector&lt;<span class="type">int</span>&gt; <span class="title">Example</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    a = a + <span class="number">10</span>;</span><br><span class="line">    b = a + <span class="number">15</span>;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;<span class="type">int</span>&gt; results;</span><br><span class="line">    results[<span class="number">0</span>] = a;</span><br><span class="line">    results[<span class="number">1</span>] = b;</span><br><span class="line">    <span class="keyword">return</span> results;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;<span class="type">int</span>&gt; Re = <span class="built_in">Example</span>(a, b);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>返回方式4：使用pair或者tuple</li></ul><p>tuple的本质是一个类。它可以包含x个变量，而pair一次只能支持两个量，但是pair可以嵌套多个量</p><p>示例如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;tuple&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//以下为tuple</span></span><br><span class="line"><span class="function">std::tuple&lt;<span class="type">int</span>,<span class="type">int</span>,std::string&gt; <span class="title">Example</span><span class="params">(<span class="type">int</span>&amp; a, <span class="type">int</span>&amp; b, std::string&amp; s)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    a = a + <span class="number">10</span>;</span><br><span class="line">    b = a + <span class="number">15</span>;</span><br><span class="line">    s = <span class="string">&quot;LYsnowQ&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> std::<span class="built_in">make_tuple</span>(a, b, s);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b = <span class="number">1</span>;</span><br><span class="line">    std::string name;</span><br><span class="line"></span><br><span class="line">    std::tuple&lt;<span class="type">int</span>,<span class="type">int</span>,std::string&gt; T=<span class="built_in">Example</span>(a, b, name);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//如果想取出</span></span><br><span class="line">    std::string name2 = std::<span class="built_in">get</span>&lt;<span class="number">2</span>&gt;(T);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//以下为pair</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;tuple&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function">std::pair&lt;<span class="type">int</span>, std::string&gt; <span class="title">Example</span><span class="params">(<span class="type">int</span>&amp; a,std::string&amp; s)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    a = a + <span class="number">10</span>;</span><br><span class="line">    s = <span class="string">&quot;LYsnowQ&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> std::<span class="built_in">make_pair</span>(a,s);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> b = <span class="number">1</span>;</span><br><span class="line">    std::string name;</span><br><span class="line"></span><br><span class="line">    std::pair&lt;<span class="type">int</span>,std::string&gt; P = <span class="built_in">Example</span>(a,name);</span><br><span class="line">    <span class="comment">//访问内容时使用first和second或者tie</span></span><br><span class="line">    a=P.first;</span><br><span class="line">name=P.second;</span><br><span class="line">    std::<span class="built_in">tie</span>(a,name)=<span class="built_in">Example</span>(a,name);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>返回方式5：结构化绑定（c++17特性）</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;tuple&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">std::tuple&lt;std::string,<span class="type">int</span>&gt; <span class="title">CreatePersion</span><span class="params">()</span><span class="comment">//pair同样适用</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;LYsnowQ&quot;</span>,<span class="number">24</span>&#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> person = CreatPerson;</span><br><span class="line">    std::string&amp; name = std::<span class="built_in">get</span>&lt;<span class="number">0</span>&gt;(person);</span><br><span class="line">    <span class="type">int</span> age = std::<span class="built_in">get</span>&lt;<span class="number">0</span>&gt;(person);</span><br><span class="line">    <span class="comment">//或者</span></span><br><span class="line">    <span class="keyword">auto</span>[name, age] = <span class="built_in">CreatPerson</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>综上来说，在返回多个值时，最直观的使用结构体去返回，可以提高代码的可读性，然而在性能上使用取地址符更好的节约性能开支，在pair和tuple中代码的可读性不高且较为复杂，在vector和array中存在着无法返回多个不同变量的限制。在使用结构化绑定时也会显得简洁好懂。</p><hr><h1 id="资料来源"><a href="#资料来源" class="headerlink" title="资料来源"></a>资料来源</h1><p>youtube上the cherno的c++系列</p>]]></content>
    
    
    <summary type="html">c++中tuple和pair等多返回问题</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++中库的使用</title>
    <link href="https://www.lysnowq.cn/posts/ef44456b.html"/>
    <id>https://www.lysnowq.cn/posts/ef44456b.html</id>
    <published>2024-09-21T12:23:07.264Z</published>
    <updated>2024-10-02T08:37:14.672Z</updated>
    
    <content type="html"><![CDATA[<h1 id="库的概述和使用"><a href="#库的概述和使用" class="headerlink" title="库的概述和使用"></a>库的概述和使用</h1><p>c++的库通常包含零个部分，一个是include文件，一个是library文件，即一个包含目录和一个库目录，在包含目录中存放的是一堆头文件，这样在使用的过程中我们就可以实际使用预构建的二进制文件中的函数，而liberary文件中所包含的就是预先构建的二进制文件，在二进制文件中一般包含两种，一种是静态库，一种是动态库。但是不是所有的库都包含两种库，</p><p>静态库和动态库区别在于：</p><p>静态库使用静态链接，库会被梵高可执行文件中或者其他操作系统下可执行的文件</p><p>动态库使用动态链接，库在运行时被链接的，一些链接可以选择在程序运行时装载动态链接库，有个叫loadLibrary的函数可以在WindowsAPI中使用它作为例子。它可以载入动态库，从中拉出函数然后开始调用函数。也可以选择在程序启动时加载dll文件。</p><p>两者主要区别是，是否是连接到exe文件或者编译到exe文件中，还是在运行过程中只用放在exe文件旁边或者其他指定位置，让exe文件加载。</p><p>静态链接相对于动态链接在技术上可以更快，因为编译器或者连接器实际上可以执行连接时优化之类的</p><p>动态链接在被运行的程序载入时程序的部分将被补充完整</p><p>在vs中我们必须先设置，让它指向头文件（include文件）只有这样才知道那些函数可用，我们就可以获得函数声明，其次我们还要将linker（连接器）指向库文件（libaray）文件。无论静态库或者动态库都需要这样的操作。</p><hr><h1 id="静态库的使用例子（GLFW）："><a href="#静态库的使用例子（GLFW）：" class="headerlink" title="静态库的使用例子（GLFW）："></a>静态库的使用例子（GLFW）：</h1><p>首先我使用GLFW作为例子（下载地址：<a href="https://www.glfw.org）">https://www.glfw.org）</a></p><p>我将该文件的二进制编码放入我的项目根目录下我自己创建的dependencies作为库的存放文件，在其中再创建了GLFW文件，我复制了vc-2022和include文件放入其中，其中的vc-2022的文件最底层包含这几个文件，其中glfw3.dll时动态库，而glfw3dll.dll是与glfw3.dll配套的静态库，这样的话我们就不需要实际询问dll。如果我们没有dll对应的静态库我们也可以使用函数直接访问dll文件内的函数。</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921205548782.png" alt="image-20240921205548782"></p><p>使用方法：首先右击项目属性，<strong>确保最上方的配置和平台无误后</strong>，在c/c++的选项下有个常规中，在附加包含目录中，我们首先指定包含文件库的位置在相对位置的目录下的包含文件。</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921213050006.png" alt="image-20240921213050006"></p><p>这样在代码中我们就可以直接调用，如图所示：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921213326344.png" alt="image-20240921213326344"></p><p>其次，在引入头文件后我们可以直接调用函数，如glfwInit，此时不会报错，并且可以正常编译，但是生成时会报错，linker会显示未定义符号，这是因为我们没有链接他的库，我们无法使用它定义的函数。</p><p>添加link如下所示：</p><p>首先我们将库的目录添加进属性，链接器中的常规的附加库目录指定好库的根目录</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921213735921.png" alt="image-20240921213735921"></p><p>然后我们继续在链接器的输入中指定我们的库，注意<strong>使用分号与后方的默认配置的目录分开</strong></p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921213842767.png" alt="image-20240921213842767"></p><p>此时我们就可以正常使用库的功能了，如果上方附加库的目录未定义完整，则附加依赖项就需要额外的输入剩余完整的路径和文件名字，这将很不方便，所以在附加库目录一定要到根目录（可以根据项目的需求灵活改变）。还有种方式如下入股哦不需要头文件，如下所写即可，其中extern “C“ 意思是保持这个函数的原貌，因为可能链接到在c语言建立的库</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921214839460.png" alt="image-20240921214839460"></p><hr><h1 id="动态库的使用（接静态库的使用GLFW）："><a href="#动态库的使用（接静态库的使用GLFW）：" class="headerlink" title="动态库的使用（接静态库的使用GLFW）："></a>动态库的使用（接静态库的使用GLFW）：</h1><p>在glfw中它同时支持静态库和动态库的使用，所以头文件和大部分设置都可以不动，但是在连接其中我们需要导入动态库如下（<strong>不要直接导入dll文件否则会报错：fatal error LNK1107: 文件无效或损坏: 无法在 0x2D0 处读取</strong>）：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921221850561.png" alt="image-20240921221850561"></p><p>但是在我们编译过程中没有问题，但是如果生成或者运行时会发生如下报错如下所示：</p><p>编译时：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921220955556.png" alt="image-20240921220955556"></p><p>运行时：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921222108120.png" alt="image-20240921222108120"></p><p>此时的解决办法是将dll文件丢入我们的exe文件根目录下（一般在项目文件的Debug文件下），因为在exe文件运行时会在自己的根目录下寻找dll动态库文件，入股哦根目录下没有那么程序无法运行。</p><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列</p>]]></content>
    
    
    <summary type="html">c++的动态库和静态库的使用</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++动态数组使用以及优化和静态数组的基本用法</title>
    <link href="https://www.lysnowq.cn/posts/7270b145.html"/>
    <id>https://www.lysnowq.cn/posts/7270b145.html</id>
    <published>2024-09-21T07:30:43.367Z</published>
    <updated>2024-10-02T08:35:54.113Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Vector的基本使用"><a href="#Vector的基本使用" class="headerlink" title="Vector的基本使用"></a>Vector的基本使用</h1><p>c++标准库为我们提供了一个动态数组的使用方法，vector在cpp中我们使用动态数组的话一般是通过vector来使用，在cpp中，我们可以一直拓展我们数组的长度，他一般情况下没有固定的大小，基本原理类似，在当前存储的数组中如果超过了数组长度，他会在内存中创建一个更长的数组并把当前数组的值复制过去，然后删除当前数组。</p><p>部分基本语法：</p><p>.push_back();    填入元素</p><p>.size();                返回元素个数</p><p>.clear();              清空所有元素</p><p>.begin();             获取第一个元素的位置</p><p>.end();                获取最后一个元素位置</p><p>.erase();             单独移除某个元素（）中需要迭代器，如.begin()+1则会删除第二个元素</p><p>Vecotr的基本使用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Vertex</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">float</span> x,y,z;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">std::ostream&amp; <span class="keyword">operator</span>&lt;&lt;(std::ostream&amp; stream, <span class="type">const</span> Vertex&amp; vertex.z)</span><br><span class="line">&#123;</span><br><span class="line">    stream &lt;&lt; vertex.x &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; vertex.y &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; vertex.z &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> stream;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;Vertex&gt; vertices;</span><br><span class="line">    vertices.<span class="built_in">push_back</span>(&#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>&#125;);<span class="comment">//push_back(),向数组中加入元素，这里加入第一个对象并给x,y,z赋值为1，2，3</span></span><br><span class="line">    vertices.<span class="built_in">push_back</span>(&#123;<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>&#125;);<span class="comment">//加入第二个元素给x,y,z赋值为4，5，6</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>,i&lt;vertices.<span class="built_in">size</span>();i++)<span class="comment">//size()可以直接获取数组中元素个数</span></span><br><span class="line">       std::cout &lt;&lt; vertices[i] &lt;&lt; std::endl;<span class="comment">//vector可以类似数组一样直接用下标取出其中的元素</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">const</span> Vertex&amp; v:vertices) <span class="comment">//这里使用迭代器，将vertices中的元素依次取出使用.加上&amp;是不需要复制元素更省空间</span></span><br><span class="line">    std::cout &lt;&lt; v &lt;&lt; std:: endl;</span><br><span class="line">    </span><br><span class="line">    vertices.<span class="built_in">erase</span>(vertices.<span class="built_in">begin</span>()+<span class="number">1</span>);<span class="comment">//删除第二个元素</span></span><br><span class="line">    </span><br><span class="line">    vertices.<span class="built_in">clear</span>();<span class="comment">//直接清除vertices中的元素</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h1 id="vector的基础优化"><a href="#vector的基础优化" class="headerlink" title="vector的基础优化"></a>vector的基础优化</h1><p>vector的基本工作原理是先创建一个数组，可以让你不断地push_back加入新元素，当现有空间已经不能够容下后续继续加入的元素时，它会重新开辟一片更大的空间，将现有的数组复制到开辟的新空间，删除旧空间的内容。而在复制迁移的过程中就比较消耗时间，所以优化的切入点即为如何避免去频繁的拓展空间即复制的过程。</p><p>一些语法使用解释：</p><p>.reserve();             设置容器的元素个数</p><p>.emplace_back(); 传入参数列表构造元素</p><p>示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Vertex</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">float</span> x,y,z;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Vertex</span>(<span class="type">float</span> x,<span class="type">float</span> y,<span class="type">float</span> z)</span><br><span class="line">        :<span class="built_in">x</span>(x), <span class="built_in">y</span>(y), <span class="built_in">z</span>(z)</span><br><span class="line">    &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Vertex</span>(<span class="type">const</span> Vertex&amp; vertex)</span><br><span class="line">        :<span class="built_in">x</span>(vertex.x),<span class="built_in">y</span>(vertex.y),<span class="built_in">z</span>(vertex.z)</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Copied!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;Vertex&gt; vertices;</span><br><span class="line">    vertices.<span class="built_in">push_back</span>(<span class="built_in">Vertex</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>));</span><br><span class="line">    vertices.<span class="built_in">push_back</span>(<span class="built_in">Vertex</span>(<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>));</span><br><span class="line">    vertices.<span class="built_in">push_back</span>(<span class="built_in">Vertex</span>(<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>));    </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>运行结果如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921194312761.png" alt="image-20240921194312761"></p><p>在这里我们可以看到，进行了6次复制过程，这个过程非常的消耗性能。</p><ul><li>第一次优化:</li></ul><p>首先这里相当于每次加入新元素时就会独自开辟一个新空间，每次加入一个新的元素就会开辟一次空间，那么为了减少空间的开辟次数，我们可以手动为其开辟空间。</p><p>例子：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//修改主函数代码</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;Vertex&gt; vertices;</span><br><span class="line">    vertices.<span class="built_in">reserve</span>(<span class="number">3</span>);<span class="comment">//手动为其开辟三个元素空间</span></span><br><span class="line">    vertices.<span class="built_in">push_back</span>(<span class="built_in">Vertex</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>));</span><br><span class="line">    vertices.<span class="built_in">push_back</span>(<span class="built_in">Vertex</span>(<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>));</span><br><span class="line">    vertices.<span class="built_in">push_back</span>(<span class="built_in">Vertex</span>(<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>));    </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>运行结果如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921194806761.png" alt="image-20240921194806761"></p><p>此处，我们这里从6个减少到了3个，此处我们将vector的空间开辟工作优化掉了，那么这里的复制的原因是什么呢？首先，在我们创建加入数组时，我们创建的位置在主函数上，也就是在main的栈上创建的Vertex对象，我们创建了对象后，再将main栈的对象复制vertices里面，此时就造成了资源浪费。</p><ul><li>第二次优化：</li></ul><p>了解的复制机制后我们的目的是让对象在数组李可以在内部自己生成创建对应的元素，以省去main栈的创建复制过程</p><p>示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//依旧更改主函数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;Vertex&gt; vertices;</span><br><span class="line">    vertices.<span class="built_in">reserve</span>(<span class="number">3</span>);<span class="comment">//手动为其开辟三个元素空间</span></span><br><span class="line">    vertices.<span class="built_in">emplace_back</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>);</span><br><span class="line">    vertices.<span class="built_in">emplace_back</span>(<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>);</span><br><span class="line">    vertices.<span class="built_in">emplace_back</span>(<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>);    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出结果为:</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240921195505425.png" alt="image-20240921195505425"></p><p>可以看到在这个过程中我们没有任何复制过程，极大的加快了代码的运行速度。</p><hr><h1 id="array的基本使用"><a href="#array的基本使用" class="headerlink" title="array的基本使用"></a>array的基本使用</h1><p>c++中vector是动态数组，可以随时扩充数组的大小，整个数组的长度是变化的。而在array中数组在被定义的时候开始就是固定的，不能随意更改大小长度。其长度在定义时就是一个常数。</p><p>语法：std::array&lt;类型，大小&gt; 名称；</p><p>使用示例</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;array&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">   std::array&lt;<span class="type">int</span>,5&gt; data;</span><br><span class="line">    data[<span class="number">0</span>]=<span class="number">1</span>;</span><br><span class="line">    data[<span class="number">1</span>]=<span class="number">2</span>;</span><br><span class="line">    data[<span class="number">2</span>]=<span class="number">3</span>;</span><br><span class="line">    data[<span class="number">3</span>]=<span class="number">4</span>;</span><br><span class="line">    data[<span class="number">4</span>]=<span class="number">5</span>;</span><br><span class="line">    data[<span class="number">5</span>]=<span class="number">6</span>;</span><br><span class="line">    </span><br><span class="line">    data.<span class="built_in">size</span>();<span class="comment">//获取数组的大小</span></span><br><span class="line">    data.<span class="built_in">end</span>();<span class="comment">//数组的最后一个</span></span><br><span class="line">    data.<span class="built_in">begin</span>();<span class="comment">//数组的第一个</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>诸如以上，其实和c的普通的数组的定义没有多大的区别，但是在使用上如果使用c语言的数组你必须得维护数组的长度，即特别是在别的函数使用时不断地传递数组的大小，而array则不需要，他可以直接访问数组的长度，并且array可以使用迭代器，在使用时用begin（）和end（）循环时则可以直接迭代，使用起来方便，维护也比c语言数组便捷省力。<strong>在调试时array会检查数组是否越界而c语言的数组不会，这会导致c语言的数组使用时拥有很大的不稳定因素和安全隐患，会导致你的代码无意间修改了不属于你的内存的量。</strong></p><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列</p>]]></content>
    
    
    <summary type="html">c++的vector的基础操作和基础优化以及array的基础使用</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>箭头操作符</title>
    <link href="https://www.lysnowq.cn/posts/850cdb83.html"/>
    <id>https://www.lysnowq.cn/posts/850cdb83.html</id>
    <published>2024-09-20T01:24:52.762Z</published>
    <updated>2024-10-02T08:45:09.512Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述及常规用法"><a href="#概述及常规用法" class="headerlink" title="概述及常规用法"></a>概述及常规用法</h1><p>箭头操作符在c++中是一个提供方便的操作符，特别是在类的实例化过后与指针配合使用时作用尤为突出，在使用指针指向一个实例后，我们无法直接调用其中的成员函数，究其原因是我们的指针不是具体的实例，无法调用其中的成员函数。</p><p>例子如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Entity</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> x;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">()</span> <span class="type">const</span><span class="comment">//如果使用的const创建的，此处也要有const</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Hello!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Scopedstr</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    Entity* m_Obj;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Scopedptr</span> (Entity* other)</span><br><span class="line">    :<span class="built_in">m_Obj</span>(other)</span><br><span class="line">    &#123;&#125;</span><br><span class="line">    ~<span class="built_in">Scopedptr</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">delete</span> m_Obj;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//重载-&gt;</span></span><br><span class="line">    Entity* <span class="keyword">operator</span>-&gt;()</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> m_Obj;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="type">const</span> Entity* <span class="keyword">operator</span>-&gt;() <span class="type">const</span><span class="comment">//如果创建时使用的为const则提供const版本</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> m_Obj;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//传出m_Obj</span></span><br><span class="line">    <span class="function">Entity* <span class="title">GetObject</span><span class="params">()</span></span>&#123; <span class="keyword">return</span> m_Obj;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Scopedptr entity=<span class="keyword">new</span> <span class="built_in">Entity</span>();<span class="comment">//此处构建的Scopedptr会自动释放空间</span></span><br><span class="line">    <span class="comment">//第一种方式传递出类中的m_Obj再调用</span></span><br><span class="line">    entity.<span class="built_in">GetObject</span>()-&gt;<span class="built_in">Print</span>();</span><br><span class="line">    <span class="comment">//第二种方式用重载</span></span><br><span class="line">    entity-&gt;<span class="built_in">Print</span>();    </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h1 id="箭头操作符的特殊用法"><a href="#箭头操作符的特殊用法" class="headerlink" title="箭头操作符的特殊用法"></a>箭头操作符的特殊用法</h1><p>在结构体中我们在结构体中定义的变量方式顺序不同也会导致他们的存储地址在内存中的偏移量不同，如 float x,y,z 和float x,z,y在内存中偏移量是相反的（互换），即在第一个中x为0，y为4，z为8，而在第二个中z为4，y为8.此时我们需要知道每个成员偏移量时可以利用-&gt;符号来得到每个变量的相对偏移量</p><p> 例子如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Vector3</span>  </span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">float</span> x,y,z;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> offest1 = (<span class="type">int</span>)&amp;((Vector3*)<span class="number">0</span>)-&gt;x;<span class="comment">//计算x的偏移量</span></span><br><span class="line">    <span class="type">int</span> offest2 = (<span class="type">int</span>)&amp;((Vector3*)<span class="number">0</span>)-&gt;y;<span class="comment">//计算y的偏移量</span></span><br><span class="line">    <span class="type">int</span> offest3 = (<span class="type">int</span>)&amp;((Vector3*)<span class="number">0</span>)-&gt;z;<span class="comment">//计算z的偏移量</span></span><br><span class="line">    std::cout &lt;&lt; offest1 &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; offest2 &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; offest3 &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>得到的结果为：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240920131542199.png" alt="image-20240920131542199"></p><p>即我们得到了x,y,z三个的偏移量</p><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列教学</p>]]></content>
    
    
    <summary type="html">c++中箭头操作符的常用和特殊用法</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++的智能指针</title>
    <link href="https://www.lysnowq.cn/posts/3f7a8e21.html"/>
    <id>https://www.lysnowq.cn/posts/3f7a8e21.html</id>
    <published>2024-09-18T13:29:29.356Z</published>
    <updated>2024-10-02T08:35:47.063Z</updated>
    
    <content type="html"><![CDATA[<h1 id="智能指针"><a href="#智能指针" class="headerlink" title="智能指针"></a>智能指针</h1><ul><li><p>什么是智能指针：</p><p>智能指针就是可以自己进行指针地址的创建和销毁的指针，在智能指针中，当需要开辟空间时它可以自己new一个空间来调用地址，并且在不需要的时候或者空着的时候自动delete内存空间。在使用智能指针时我们基本不需要使用new，所以智能指针的代码风格也很工整清爽。在编写代码时可以自动化管理内存。</p></li><li><p>智能指针的分类（智能指针包含在memory头文件中）：</p><ul><li>unique_ptr(作用域指针)：<strong>unique_ptr只能有一个</strong>，不能复制一个一样的指针，否则有其中一个释放时，另一个就会指向那个被释放的区域，会报错。<strong>在使用unique_ptr时我们对类的实例化过程时只能显示构造函数，不能进行隐式构造</strong>。</li><li>语法：std::unique_ptr&lt;参数模板&gt; 名字 =std::make_unique&lt;参数模板&gt;()；</li></ul><p>例子如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Entity</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Entity</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Creat a Entity!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    ~<span class="built_in">Entity</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Destory a Entity!&quot;</span> &lt;&lt; std::endl; </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Print</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;This is an Entity!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="function">std::unique_ptr&lt;Entity&gt; <span class="title">entity</span><span class="params">(<span class="keyword">new</span> Entity())</span></span>;<span class="comment">//第一种创建方法</span></span><br><span class="line">std::unique_ptr&lt;Entity&gt; entity = std::<span class="built_in">make_unique</span>&lt;Entity&gt;();<span class="comment">//第二种创建方法（有时可能出于异常时使用此方法，即由于构造函数出现异常时指针会为空）</span></span><br><span class="line">        </span><br><span class="line">        entity-&gt;<span class="built_in">Print</span>();<span class="comment">//像正常函数一样调用即可</span></span><br><span class="line">    &#125;<span class="comment">//当离开这个作用域时这个指针会自动销毁</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>shared_ptr(共享指针):sharing_ptr的使用取决于使用的编译器和其中的标准库，大多数情况下，他是作为引用计数（一种用来统计你的指针有多少引用的方法）在引用计数中一旦计数为0则会自动销毁。例如用了两次sharing_ptr则引用计数为2，如果其中一个被释放了那么为1，直到最后一个也被释放才会自动销毁。</li><li>语法：std::shared_ptr&lt;参数模板&gt; 名字 =std::make_shared&lt;参数模板&gt;()；</li></ul><p>例子如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Entity</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Entity</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Creat a Entity!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    ~<span class="built_in">Entity</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Destory a Entity!&quot;</span> &lt;&lt; std::endl; </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Print</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;This is an Entity!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    &#123;</span><br><span class="line">        std::shared_ptr&lt;Entity&gt; e0;</span><br><span class="line">        &#123;</span><br><span class="line"><span class="comment">//std::shared_ptr&lt;Entity&gt; entity(new Entity());//第一种创建方法</span></span><br><span class="line">std::shared_ptr&lt;Entity&gt; entity = std::<span class="built_in">make_shared</span>&lt;Entity&gt;();<span class="comment">//第二种创建方法,在这种方法中不写new是因为异常安全</span></span><br><span class="line">        e0 = entity;<span class="comment">//支持复制</span></span><br><span class="line">        &#125;<span class="comment">//在出这个作用域时entity就会销毁，但是e0依旧存活</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>weak_ptr:一般与shared_ptr连用，因为当shared_ptr赋值给weak_ptr时不会增加引用次数，这样可以通过weak_ptr去判断底层对象是否还存在。所有有可能weak_ptr会指向一个无效对象。</li></ul><hr><h1 id="资料来源"><a href="#资料来源" class="headerlink" title="资料来源"></a>资料来源</h1><p>youtube上the cherno的c++系列</p></li></ul>]]></content>
    
    
    <summary type="html">c++的智能指针的基础介绍和语法运用</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++操作符重载</title>
    <link href="https://www.lysnowq.cn/posts/11f33fa.html"/>
    <id>https://www.lysnowq.cn/posts/11f33fa.html</id>
    <published>2024-09-18T11:50:53.240Z</published>
    <updated>2024-10-02T09:17:23.354Z</updated>
    
    <content type="html"><![CDATA[<h1 id="c-的操作符号重载"><a href="#c-的操作符号重载" class="headerlink" title="c++的操作符号重载"></a>c++的操作符号重载</h1><ul><li>什么是操作符：</li></ul><p>操作符的本质就是代替函数执行任务的符号。有运算符号加减乘除等基础数学操作符，还有解引用，取地址符号，<strong>其中new和delete也是操作符的一种</strong>，c++中有很多操作符，它们的本质就是调用函数或者方法所有的符号代替实现的功能的都叫做操作符，操作符自身也就是一个函数，也包括我们使用的 大括号 小括号 逗号 尖括号（左移操作和右移操作等）。</p><ul><li>重载操作符：</li></ul><p>重载操作符就是重新去定义操作符，扩充它的功能，或者重新给他一个新的含义等操作方法，是一个非常有用的操作方法，在c++中我们拥有对操作符的完全修改的权力。</p><p>重载操作符例子如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span> </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Vector2</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">float</span> x,y;</span><br><span class="line">    <span class="built_in">Vector2</span>(<span class="type">float</span> x, <span class="type">float</span> y)</span><br><span class="line">        :<span class="built_in">x</span>(x),<span class="built_in">y</span>(y)&#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function">Vector2 <span class="title">Multiply</span><span class="params">(<span class="type">const</span> Vector2&amp; other)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Vector2</span>(x*other.x,y*other.y);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function">Vector2 <span class="title">Add</span><span class="params">(<span class="type">const</span> Vector2&amp; other)</span> <span class="type">const</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Vector2</span>(x+other.x,y+other.y);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    Vector2 <span class="keyword">operator</span>+(<span class="type">const</span> Vector2&amp; other) <span class="type">const</span> <span class="comment">//重载了加号</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Add</span>(other)</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    Vector2 <span class="keyword">operator</span>*(<span class="type">const</span> Vector2&amp; other) <span class="type">const</span> <span class="comment">//重载了乘号</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Multiply</span>(other)</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//对==的重载</span></span><br><span class="line"><span class="type">bool</span> <span class="keyword">operator</span>==(<span class="type">const</span> Vector2&amp; other) <span class="type">const</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> other.x==x &amp;&amp; other.y==y;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//在==重载基础上重载!=</span></span><br><span class="line">    <span class="type">bool</span> <span class="keyword">operator</span>==(<span class="type">const</span> Vector2&amp; other) <span class="type">const</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> !(*<span class="keyword">this</span>==other);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//重构操作符(因为在类外面所以要定义一个引入类的操作符)</span></span><br><span class="line">std::ostream&amp; <span class="keyword">operator</span>&lt;&lt;(std::ostream&amp; stream,<span class="type">const</span> Vector2&amp; other)</span><br><span class="line">&#123;<span class="comment">//ostream指的是输出流</span></span><br><span class="line">    stream &lt;&lt; other.x &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; other.y &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> stream;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">Vector2 <span class="title">a_speed</span><span class="params">(<span class="number">10.0f</span>,<span class="number">10.0f</span>)</span></span>;</span><br><span class="line">    <span class="function">Vector2 <span class="title">speed</span><span class="params">(<span class="number">0.5f</span>,<span class="number">0.5f</span>)</span></span>;</span><br><span class="line">    <span class="function">Vector2 <span class="title">powerup</span><span class="params">(<span class="number">1.1f</span>,<span class="number">1.1f</span>)</span></span></span><br><span class="line"><span class="function">    Vector2 final_speed1</span>=a_speed + speed * powerup;</span><br><span class="line">    Vector2 final_speed2=a_speed + speed * powerup + speed;</span><br><span class="line">    <span class="comment">//所以最终速度应该为10*1.1+0.5；</span></span><br><span class="line">    <span class="comment">//通过重构的操作符&lt;&lt;直接输出实例的值</span></span><br><span class="line">    std::cout &lt;&lt; final_speed &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">if</span>(final_speed1==final_speed2)</span><br><span class="line">    <span class="keyword">if</span>(final_speed1!=final_speed2)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>重载操作符就是赋予符合额外的功能，在py中更接近于魔术方法，但是仅限于在类中使用，但是在cpp中可以任何地方使用并且有完全的权限去更改它。</p><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列</p>]]></content>
    
    
    <summary type="html">c++符号的重载操作</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>c++隐式构造函数，隐式转换和explicit</title>
    <link href="https://www.lysnowq.cn/posts/6bd152cd.html"/>
    <id>https://www.lysnowq.cn/posts/6bd152cd.html</id>
    <published>2024-09-18T07:28:03.613Z</published>
    <updated>2024-10-02T08:36:06.004Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="c-中的隐式构造函数"><a href="#c-中的隐式构造函数" class="headerlink" title="c++中的隐式构造函数"></a>c++中的隐式构造函数</h1><p>在c++中允许编译器<strong>进行一次隐式转换</strong>，即将一个数据类型转化为另一个数据类型来用。像基础的char命名的用整数赋值会转化为asc码来显示，相当于一次隐形转化。而在类的构造函数中我们也可以使用这种隐式转化来构造实例。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Example</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> m_age;</span><br><span class="line">    std::string m_name;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Example</span>(<span class="type">const</span> std::string name)</span><br><span class="line">        :<span class="built_in">m_age</span>(<span class="number">-1</span>),<span class="built_in">m_name</span>(name)&#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Example</span>(<span class="type">int</span> age)</span><br><span class="line">        :<span class="built_in">m_age</span>(age),<span class="built_in">m_name</span>(<span class="string">&quot;Unknow&quot;</span>)&#123;&#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">(<span class="type">const</span> Example&amp; e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    stdL::cout &lt;&lt; e.</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//以下方式都可以成功创造出一个对象，即进行了一次隐式转化创建了实例并且调用了对用的构造函数</span></span><br><span class="line">Example a=<span class="number">21</span>;</span><br><span class="line">    <span class="built_in">Print</span>(<span class="number">22</span>);</span><br><span class="line">    <span class="comment">//以下方式不可以</span></span><br><span class="line">    Example b=<span class="string">&quot;LYsnowQ&quot;</span>;</span><br><span class="line">    <span class="built_in">Print</span>(<span class="string">&quot;LYsnowQ&quot;</span>);</span><br><span class="line">    <span class="comment">//此处的面板为char数组类型，如果需要正常传入则需要先将char数组类型转化为std::string而编译器只能进行一次转换所以会报错</span></span><br><span class="line">    <span class="comment">//正确方法是如下</span></span><br><span class="line">    <span class="built_in">Print</span>(std::<span class="built_in">string</span>(<span class="string">&quot;LYsnowQ&quot;</span>));</span><br><span class="line">    Example b=std::<span class="built_in">string</span>(<span class="string">&quot;LYsnowQ&quot;</span>);</span><br><span class="line">    <span class="built_in">Print</span>(<span class="built_in">Example</span>(<span class="string">&quot;LYsnowQ&quot;</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="explicit关键字"><a href="#explicit关键字" class="headerlink" title="explicit关键字"></a>explicit关键字</h1><p>在上述的隐式转换中，编译器可以帮我们进项隐式转化，但是有时候我们像禁用编译器的转化，以防止代码出现差错，呢么explicit关键字就是禁用编译器的隐式编译器的代码转化， </p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Example</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> m_age;</span><br><span class="line">    std::string m_name;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">explicit</span> <span class="title">Example</span><span class="params">(<span class="type">const</span> std::string name)</span></span></span><br><span class="line"><span class="function">        :m_age(<span class="number">-1</span>),m_name(name)&#123;</span>&#125;</span><br><span class="line">    </span><br><span class="line">   <span class="function"><span class="keyword">explicit</span> <span class="title">Example</span><span class="params">(<span class="type">int</span> age)</span></span></span><br><span class="line"><span class="function">        :m_age(age),m_name(<span class="string">&quot;Unknow&quot;</span>)&#123;</span>&#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Print</span><span class="params">(<span class="type">const</span> Example&amp; e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    stdL::cout &lt;&lt; e.</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//以下方式都在使用了explicit后无法使用</span></span><br><span class="line">Example a=<span class="number">21</span>;</span><br><span class="line">    <span class="built_in">Print</span>(<span class="number">22</span>);</span><br><span class="line">    <span class="comment">//需要改成以下方式</span></span><br><span class="line">    <span class="function">Example <span class="title">a</span><span class="params">(Example(<span class="number">21</span>))</span></span>;</span><br><span class="line"><span class="built_in">Print</span>(<span class="built_in">Example</span>(<span class="string">&quot;LYsnowQ&quot;</span>));</span><br><span class="line">    <span class="comment">//或者使用以下方式：</span></span><br><span class="line">    Example a = <span class="built_in">Example</span>(<span class="number">21</span>);</span><br><span class="line">    a = <span class="built_in">Example</span>(<span class="string">&quot;LYsnowQ&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h1 id="资料参考："><a href="#资料参考：" class="headerlink" title="资料参考："></a>资料参考：</h1><p>youtube上the cherno的cpp系列教学</p>]]></content>
    
    
    <summary type="html">c++中类的隐式构造和转换规则</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>构造函数的初始化成员列表</title>
    <link href="https://www.lysnowq.cn/posts/0.html"/>
    <id>https://www.lysnowq.cn/posts/0.html</id>
    <published>2024-09-17T09:24:35.796Z</published>
    <updated>2024-10-02T08:38:20.523Z</updated>
    
    <content type="html"><![CDATA[<h1 id="初始化成员列表"><a href="#初始化成员列表" class="headerlink" title="初始化成员列表"></a>初始化成员列表</h1><p>在cpp中我们经常创造一些类的使用，在初始化时过多的变量会导致代码冗乱和可读性下降，在一些特定语句使用时还会带来一系列的程序安全隐患和意想不到的bug。初始化成员列表在class的成员初始化中具有相当好用，可读性强，便捷的优点。</p><p>初始化成员列表的语法如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">析构函数名()</span><br><span class="line">    :成员<span class="number">1</span>(成员<span class="number">1</span>初始量),成员<span class="number">2</span>(成员<span class="number">2</span>初识量),.....</span><br><span class="line">&#123;</span><br><span class="line">    代码块</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>需要注意的是在初始化成员列表时，初始化的成员顺序，必须按照类中成员的声明顺序进行，否则会报错。</p><p>以下为实例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> a;</span><br><span class="line">    <span class="type">int</span> b;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">A</span>()</span><br><span class="line">        :<span class="built_in">a</span>(<span class="number">10</span>),<span class="built_in">b</span>(<span class="number">20</span>)</span><br><span class="line">    &#123;</span><br><span class="line">         代码块   </span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>而在一些特定情况下初始化成员列表可以避免一些额外的性能消耗的一些bug发生</p><p>例子如下</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Num</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Num</span>()</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;this is Num class&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">Num</span>(<span class="type">int</span> x)</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;cout &quot;</span>&lt;&lt; x &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">int</span> a;</span><br><span class="line">    std::string s_name;</span><br><span class="line">    Num m_num;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">A</span>()</span><br><span class="line">    :<span class="built_in">a</span>(<span class="number">10</span>),<span class="built_in">s_name</span>(<span class="string">&quot;A&quot;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">         <span class="comment">//s_name=&quot;A&quot;;//这里赋值时会有=额外的开销，在此处真正的赋值过程为s_name=std::string(&quot;A&quot;),相当于再次创建了一个string性能消耗会增加。   </span></span><br><span class="line">            m_num=<span class="built_in">Num</span>(<span class="number">1</span>);<span class="comment">//此处我们想构建的m_num实行第二种构造函数的内容</span></span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    A a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时输出结果如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240917173920333.png" alt="image-20240917173920333"></p><p>即上述过程中两个构造函数均被实现了一次即有两次的实例对象创建。而当我们换用初始化成员列表后如下：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">class <span class="selector-tag">A</span></span><br><span class="line">&#123;</span><br><span class="line">private:</span><br><span class="line">int a;</span><br><span class="line">    std::string s_name;</span><br><span class="line">    Num m_num;</span><br><span class="line">public:</span><br><span class="line">    <span class="built_in">A</span>()</span><br><span class="line">    :<span class="built_in">a</span>(<span class="number">10</span>),<span class="built_in">s_name</span>(<span class="string">&quot;A&quot;</span>),<span class="built_in">m_num</span>(<span class="built_in">Num</span>(<span class="number">1</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>此时的运行结果如下：</p><p><img src="https://lysnowq.oss-cn-shenzhen.aliyuncs.com/image-20240917174321789.png" alt="image-20240917174321789"></p><p>即我们只得到了使用带有传入参数的构造函数的实例的创建。</p><hr><h1 id="资料参考"><a href="#资料参考" class="headerlink" title="资料参考"></a>资料参考</h1><p>YouTube上Thecherno的cpp系列</p>]]></content>
    
    
    <summary type="html">初始化成员列表的运用环境和使用方式</summary>
    
    
    
    <category term="c++基础" scheme="https://www.lysnowq.cn/categories/c-%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="c++学习" scheme="https://www.lysnowq.cn/tags/c-%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
</feed>
