【问题标题】:What is a converting constructor in C++ ? What is it for?什么是 C++ 中的转换构造函数?它是干什么用的?
【发布时间】:2018-01-17 04:28:50
【问题描述】:

我听说 C++ 有一个叫做“转换构造函数”或“转换构造函数”的东西。这些是什么,它们是做什么用的?我看到它提到了这段代码:

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
}

 int main()
{
    MyClass M = 1 ;
}

【问题讨论】:

  • 我真的不明白这个问题有什么问题 - 这是一个问题,即什么是转换构造函数以及它们的用途。显然是他问的,因为他并不完全了解它们的用法。我正在学习 C++,每次看到一个新概念时,我都会用谷歌搜索它,这就是我提出这个问题的方式。很多时候,我在 StackOverFlow 上遇到这样的问题,这有助于我理解这个概念。我认为 StackOverFlowers 应该鼓励这样做。
  • 疯了。我有同样的非真实问题。猜猜这使它成为一个完整的问题。
  • 危险!主持人:C++03 和 C++11 标准的第 12.3.1 节描述了这种构造函数。参赛者:什么是转换构造函数? Stackoverflow 主持人:错了,回复必须是真实问题的形式。
  • @bvj 虚构问题的平方是真实问题,但是否定的:)

标签: c++ constructor copy-constructor


【解决方案1】:

转换构造函数的定义在 C++03 和 C++11 之间是不同的。在这两种情况下,它都必须是非explicit 构造函数(否则它不会参与隐式转换),但对于 C++03,它也必须可以使用单个参数调用。那就是:

struct foo
{
  foo(int x);              // 1
  foo(char* s, int x = 0); // 2
  foo(float f, int x);     // 3
  explicit foo(char x);    // 4
};

构造函数 1 和 2 都是 C++03 和 C++11 中的转换构造函数。构造函数 3 必须接受两个参数,它只是 C++11 中的转换构造函数。最后一个构造函数 4 不是转换构造函数,因为它是 explicit

  • C++03:§12.3.1

    声明的构造函数没有函数说明符 explicit 可以用单个参数调用,它指定了从其第一个参数的类型到其类的类型的转换。这样的构造函数称为转换构造函数。

  • C++11:§12.3.1

    声明的构造函数没有函数说明符 explicit 指定从其参数类型到其类类型的转换。这样的构造函数称为转换构造函数。

为什么在 C++11 中将具有多个参数的构造函数视为转换构造函数?这是因为新标准为我们提供了一些方便的语法,用于使用 braced-init-lists 传递参数和返回值。考虑以下示例:

foo bar(foo f)
{
  return {1.0f, 5};
}

将返回值指定为 braced-init-list 的能力被视为一种转换。这使用了foo 的转换构造函数,它采用floatint。另外,我们可以通过bar({2.5f, 10})调用这个函数。这也是一种转换。由于它们是转换,因此它们使用的构造函数是转换构造函数

因此,重要的是要注意,使采用floatintfoo 的构造函数具有explicit 函数说明符将阻止上述代码编译。上面的新语法只有在有一个转换构造函数可用时才能使用。

  • C++11:§6.6.3:

    带有 braced-init-listreturn 语句通过指定初始化列表中的复制列表初始化 (8.5.4) 初始化要从函数返回的对象或引用。

    §8.5:

    在参数传递 [...] 中发生的初始化 [...] 称为复制初始化。

    §12.3.1:

    显式构造函数与非显式构造函数一样构造对象,但仅在显式使用直接初始化语法 (8.5) 或强制类型转换 (5.2.9, 5.4) 时这样做。

【讨论】:

    【解决方案2】:

    使用转换构造函数进行隐式转换

    让问题中的例子更复杂

    class MyClass
    {
      public:
         int a, b;
         MyClass( int i ) {}
         MyClass( const char* n, int k = 0 ) {}
         MyClass( MyClass& obj ) {}
    }
    

    前两个构造函数是转换构造函数。第三个是复制构造函数,因此它是另一个转换构造函数。

    转换构造函数可以实现从参数类型到构造函数类型的隐式转换。在这里,第一个构造函数可以将int 转换为MyClass 类的对象。第二个构造函数可以将字符串转换为MyClass 类的对象。第三...从MyClass 类的对象到MyClass 类的对象!

    要成为转换构造函数,构造函数必须有一个参数(在第二个参数中,第二个参数有一个默认值)并且声明时不带关键字explicit

    然后,main 中的初始化可以如下所示:

    int main()
    {
        MyClass M = 1 ;
        // which is an alternative to
        MyClass M = MyClass(1) ;
    
        MyClass M = "super" ;
        // which is an alternative to
        MyClass M = MyClass("super", 0) ;
        // or
        MyClass M = MyClass("super") ;
    }
    

    显式关键字和构造函数

    现在,如果我们使用 explicit 关键字会怎样?

    class MyClass
    {
      public:
         int a, b;
         explicit MyClass( int i ) {}
    }
    

    那么,编译器不会接受

       int main()
        {
            MyClass M = 1 ;
        }
    

    因为这是隐式转换。相反,必须写

       int main()
        {
            MyClass M(1) ;
            MyClass M = MyClass(1) ;
            MyClass* M = new MyClass(1) ;
            MyClass M = (MyClass)1;
            MyClass M = static_cast<MyClass>(1);
        }
    

    explicit 关键字始终用于防止构造函数的隐式转换,它适用于类声明中的构造函数。

    【讨论】:

    • 第一个示例中的第三个构造函数不是复制构造函数。复制构造函数参数必须是以下之一:X&amp;const X&amp;volatile X&amp;const volatile X&amp;
    • 你可以在最后一个例子中写MyClass M(1);等。也要小心那些多字符文字。
    • 您在发布代码之前是否甚至费心编译代码?您的 strings 实际上是多字符常量,不会隐式转换为 char const *!
    • 我也不认为构造函数需要一个参数才能成为转换构造函数。它只需要是非显式的:“声明的构造函数没有 function-specifier explicit 指定从其参数类型到其类的类型的转换。这样的构造函数称为转换构造函数。”
    • @MooingDuck 它说“可以用单个参数调用” - 基本上是一样的。
    【解决方案3】:

    转换构造函数是一个单参数构造函数,声明时不使用函数说明符 explicit 。编译器使用转换构造函数将对象从第一个参数的类型转换为转换构造函数的类的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多