刃雪 發表於 2025-12-3 11:07:26

C++构造函数中explicit详解

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>1. 什么是explicit</li><li>2. 隐式转换的问题</li><li>3.explicit的使用示例</li><ul class="second_class_ul"><li>基本用法</li><li>多参数构造函数</li></ul><li>4. C++11 之后的扩展</li><ul class="second_class_ul"><li>(1)explicit用于转换运算符</li><li>(2) C++20 的explicit(bool)</li></ul><li>5. 最佳实践</li><ul class="second_class_ul"></ul><li>总结</li><ul class="second_class_ul"></ul><li>补充</li><ul class="second_class_ul"></ul></ul></div><p>在 C++ 编程中,构造函数是类的核心部分之一。我们常常使用构造函数来初始化对象。但是,如果不加限制,某些构造函数可能会被 <strong>隐式调用</strong>,从而带来一些意料之外的行为。</p>
<p>为了解决这个问题,C++ 提供了 <code>explicit</code> 关键字。</p>
<p class="maodian"></p><h2>1. 什么是explicit</h2>
<p>在 C++ 中,<code>explicit</code> 关键字用于修饰 <strong>单参数构造函数</strong> 或 <strong>可以看作单参数的构造函数</strong>,阻止编译器进行 <strong>隐式类型转换</strong> 或 <strong>拷贝初始化</strong>。</p>
<ul><li>默认情况下,单参数构造函数既可以显式调用,也可以被编译器用来进行隐式类型转换。</li><li><code>explicit</code> 告诉编译器:<strong>这个构造函数只能显式调用,不能用于隐式转换。</strong></li></ul>
<p class="maodian"></p><h2>2. 隐式转换的问题</h2>
<p>来看一个例子:</p>
<div class="jb51code"><pre class="brush:cpp;">#include &lt;iostream&gt;
using namespace std;

class Fraction {
private:
    int numerator;
    int denominator;

public:
    Fraction(int num, int den = 1) : numerator(num), denominator(den) {}
    void print() const {
      cout &lt;&lt; numerator &lt;&lt; "/" &lt;&lt; denominator &lt;&lt; endl;
    }
};

int main() {
    Fraction f1 = 5;// 隐式调用 Fraction(5, 1)
    f1.print();       // 输出:5/1
}
</pre></div>
<p>在上面的例子中:</p>
<ul><li><code>Fraction f1 = 5;</code> 本质上是调用 <code>Fraction(5, 1)</code>,因为编译器允许用 <code>int</code> 隐式转换成 <code>Fraction</code>。</li><li>虽然看似方便,但有时会带来 <strong>不可控的隐式转换</strong>,导致逻辑错误或二义性。</li></ul>
<p class="maodian"></p><h2>3.explicit的使用示例</h2>
<p class="maodian"></p><h3>基本用法</h3>
<p>如果我们在构造函数前加上 <code>explicit</code>:</p>
<div class="jb51code"><pre class="brush:cpp;">class Fraction {
private:
    int numerator;
    int denominator;

public:
    explicit Fraction(int num, int den = 1) : numerator(num), denominator(den) {}
    void print() const {
      cout &lt;&lt; numerator &lt;&lt; "/" &lt;&lt; denominator &lt;&lt; endl;
    }
};

int main() {
    Fraction f1(5);   // ✅ 显式调用,可以
    // Fraction f2 = 5; // ❌ 编译错误,不能隐式转换
}
</pre></div>
<ul><li><code>Fraction f1(5);</code> 依然可以显式调用。</li><li>但 <code>Fraction f2 = 5;</code> 会报错,因为 <code>explicit</code> 禁止了隐式转换。</li></ul>
<p class="maodian"></p><h3>多参数构造函数</h3>
<p>有时构造函数有多个参数,但如果除第一个外的参数都有默认值,它依然算作 <strong>单参数构造函数</strong>,也可能引发隐式转换。</p>
<div class="jb51code"><pre class="brush:cpp;">class Fraction {
public:
    explicit Fraction(int num, int den = 1) { /* ... */ }
};
</pre></div>
<p>这里如果没有 <code>explicit</code>,表达式 <code>Fraction f = 5;</code> 依然会成立。</p>
<p class="maodian"></p><h2>4. C++11 之后的扩展</h2>
<p class="maodian"></p><h3>(1)explicit用于转换运算符</h3>
<p>在 C++11 之前,类的类型转换函数(比如 <code>operator bool</code>)会允许隐式转换:</p>
<div class="jb51code"><pre class="brush:cpp;">class Test {
public:
    operator bool() const { return true; }
};

int main() {
    Test t;
    if (t) {// 隐式调用 operator bool()
      cout &lt;&lt; "True" &lt;&lt; endl;
    }
}
</pre></div>
<p>但有时我们并不希望这种隐式转换。C++11 允许写成:</p>
<div class="jb51code"><pre class="brush:cpp;">class Test {
public:
    explicit operator bool() const { return true; }
};

int main() {
    Test t;
    // if (t) { } // ❌ 错误,不能隐式转换
    if (static_cast&lt;bool&gt;(t)) {// ✅ 必须显式转换
      cout &lt;&lt; "True" &lt;&lt; endl;
    }
}
</pre></div>
<p class="maodian"></p><h3>(2) C++20 的explicit(bool)</h3>
<p>C++20 引入了更灵活的语法:<code>explicit(bool)</code>。<br />这让我们可以根据编译期常量决定是否允许隐式调用。</p>
<div class="jb51code"><pre class="brush:cpp;">struct A {
    explicit(true) A(int) {}   // 永远显式
    explicit(false) A(double) {} // 永远允许隐式
};
</pre></div>
<p>这种写法在模板编程中很有用。</p>
<p class="maodian"></p><h2>5. 最佳实践</h2>
<ol><li><p><strong>几乎总是给单参数构造函数加 <code>explicit</code></strong><br />这样可以避免隐式转换带来的混乱,除非你确实需要这种转换。</p></li><li><p><strong>转换运算符应当尽量显式</strong><br />尤其是 <code>operator bool</code>,因为隐式转换到 <code>bool</code> 可能导致奇怪的条件判断。</p></li><li><p><strong>允许隐式转换的场景</strong><br />如果你的类本质上就是包装某个类型(比如 <code>string_view</code> 可以从 <code>const char*</code> 隐式转换),那么允许隐式转换可以让使用更加自然。</p></li></ol>
<p class="maodian"></p><h2>总结</h2>
<ul><li>explicit 的主要作用:防止构造函数或转换运算符被隐式调用。</li><li>在单参数构造函数和转换运算符中使用最为常见。</li><li>自 C++11 起,还能用于 operator bool;C++20 引入 explicit(bool),进一步增强灵活性。</li><li>最佳实践:默认加上 explicit,除非你有充分理由允许隐式转换。</li><li>关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以无需将这些构造函数指定为explicit的。</li><li>只能在类内声明构造函数时使用explicit关键字。</li></ul>
<p class="maodian"></p><h2>补充</h2>
<ul><li>接受一个单参数的const char*的string构造函数不是explicit的</li><li>接受一个容量参数的vector构造函数是explicit的</li></ul>
<p>到此这篇关于C++构造函数中explicit详解的文章就介绍到这了,更多相关C++构造函数explicit内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>C++&nbsp;Explicit关键字详细解析</li><li>C++ explicit构造函数实例解析</li><li>C++ explicit关键字的应用方法详细讲解</li><li>C++中的explicit关键字实例浅析</li><li>C++中的explicit关键字详解</li><li>C++ explicit关键字的使用详解</li><li>C++&nbsp;之explicit关键字</li><li>C++&nbsp;explicit关键字讲解</li><li>C++&nbsp;explicit显式关键字的实现</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: C++构造函数中explicit详解