<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Langs on aiken&#39;s blog</title>
    <link>https://aikenh.cn/tags/langs/</link>
    <description>Recent content in Langs on aiken&#39;s blog</description>
    <generator>Hugo -- 0.137.0</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 22 Oct 2024 17:05:32 +0000</lastBuildDate>
    <follow_challenge>
      <feedId>90080597450814464</feedId>
      <userId>72439538841050112</userId>
    </follow_challenge>
    <atom:link href="https://aikenh.cn/tags/langs/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>头文件互相包含</title>
      <link>https://aikenh.cn/posts/ts1_%E5%A4%B4%E6%96%87%E4%BB%B6%E4%BA%92%E7%9B%B8%E5%8C%85%E5%90%AB/</link>
      <pubDate>Tue, 22 Oct 2024 17:05:32 +0000</pubDate>
      <guid>https://aikenh.cn/posts/ts1_%E5%A4%B4%E6%96%87%E4%BB%B6%E4%BA%92%E7%9B%B8%E5%8C%85%E5%90%AB/</guid>
      <description>使用类前置声明来解决CPP头文件相互包含带来的编译问题</description>
      <content:encoded><![CDATA[


  
  

<blockquote class="alert-blockquote alert-error">
  <p class="alert-heading">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
      <path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>
    </svg>
    <span>Error</span>
  </p>
  <p>编写 C++ 代码时偶尔会遇到两个类需要相互引用的情况，如果在 h 文件中相互包含会导致 &ldquo;has not been declared&rdquo; 等声明问题，此时需要使用前置声明的方式来解决该问题</p>

</blockquote>
<h3 id="code-example">Code Example</h3>
<p>以一个实际场景为例：假设有工具类 Search Helper 提供一系列搜索功能，还有另一个 ObjectAttr 定义一系列操作对象的属性，在 ObjectAttr 中需要引用 SearchHelper 提供的某个基础函数，而 SearchHelper 中部分针对 ObjectAttr 设计的某些功能则需要将 ObjectAttr 中的类作为入参，此时我们可能会在 h 文件中相互引用；</p>
<p>在 obj_definition.h 中有 <code>GetSelfNearObj</code> 函数，其过程中需要调用 search_helper 中的 <code>GetDistObj</code> 函数进行辅助运算</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>cpp</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;util/search_helper.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">namespace</span> <span class="n">object_info</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">class</span> <span class="nc">ObjectAttr</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">		<span class="n">LocationType</span> <span class="n">location</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="kt">int</span> <span class="n">ObjId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="kt">int</span> <span class="n">NearObjId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">		<span class="kt">void</span> <span class="n">GetSelfNearObj</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>在 search_helper.h 中有以下函数定义</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>cpp</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;base/obj_definition.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">namespace</span> <span class="n">search_helper</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">class</span> <span class="nc">SearchHelper</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">		<span class="kt">int</span> <span class="n">GetDistObj</span><span class="p">(</span><span class="k">const</span> <span class="n">object_info</span><span class="o">::</span><span class="n">ObjectAttr</span> <span class="o">&amp;</span> <span class="n">obj1</span><span class="p">,</span> <span class="k">const</span> <span class="n">object_info</span><span class="o">::</span><span class="n">ObjectAttr</span> <span class="o">&amp;</span> <span class="n">obj2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>这种情况下编译会出现未定义，未声明，not a type 之类<strong>缺少类型说明符</strong>的错误。</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span></span>
    </div>
    <div class="code-content">
        
        <pre tabindex="0"><code>error: ... has not been declared</code></pre>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><h3 id="fix--analysis">Fix &amp; Analysis</h3>
<p>上述错误是由于两个文件构成了循环依赖，类 <code>SearchHelper</code> 依赖于 <code>ObjectAttr</code>，<code>ObjectAttr</code> 又依赖于 <code>SearchHelper</code>；因此编译器会报错导致无法通过编译；</p>
<p>在这种情况下，可以通过前向声明的方式来解决这种问题，即不去进行循环引用，在search_helper 中不 <code>#include obj_definition.h</code>，但是对需要的 <code>ObjectAttr</code> 进行一个空声明，然后在 .cpp 中 <code>#include obj_definition.h</code>  具体如下，将 H 文件修改为；</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>cpp</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">namespace</span> <span class="n">objct_info</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">class</span> <span class="nc">ObjectAttr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="n">search_helper</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">class</span> <span class="nc">SearchHelper</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> 	<span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl"> 		<span class="kt">int</span> <span class="n">GetDistObj</span><span class="p">(</span><span class="k">const</span> <span class="n">object_info</span><span class="o">::</span><span class="n">ObjectAttr</span> <span class="o">&amp;</span> <span class="n">obj1</span><span class="p">,</span> <span class="k">const</span> <span class="n">object_info</span><span class="o">::</span><span class="n">ObjectAttr</span> <span class="o">&amp;</span> <span class="n">obj2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>在对应的 <code>.cpp</code> 文件中添加对 <code>obj_definition</code> 的包含，这样在编译过程中，<code>ObjectAttr</code> 会被自动链接到正确的定义；</p>
<div class="code-block">
    <div class="code-title" onclick="toggleCode(this)">
        
        <span class="code-block-open"><ion-icon name="chevron-down-outline"></ion-icon></span>
        <span class="code-block-close"><ion-icon name="chevron-forward-outline"></ion-icon></ion-icon></span>
        <span>cpp</span>
    </div>
    <div class="code-content">
        
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;base/obj_definition.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// origin cpp file below...
</span></span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span></code></pre></div>
    </div>
</div>

<script>
function toggleCode(element) {
    const codeContent = element.nextElementSibling;
    if (codeContent.style.display === "none" || codeContent.style.display === "") {
        codeContent.style.display = "block"; 
        codeContent.parentNode.classList.remove("code-has-hidden-child");
    } else {
        codeContent.style.display = "none"; 
        codeContent.parentNode.classList.add("code-has-hidden-child");
    }
}
</script><p>上述例子展示了不同命名空间下的情况，这里秉持的原则就是不同文件中对类的前置声明要保持一致，无论是命名空间、参数、类型等，在同一个命名空间中的情况要更简单，可以以此类推，这里不再赘述；</p>
<h3 id="reference">Reference</h3>
<ol>
<li><a href="https://blog.csdn.net/hfutljx/article/details/81508740" target="_blank" rel="noopener">c++ 头文件互相包含问题</a>
</li>
<li><a href="https://www.cnblogs.com/staring-hxs/p/3244251.html" target="_blank" rel="noopener">c++ 类声明 类前置声明范例</a>
</li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
