默认、参数化、复制构造函数和赋值运算符情况下的构造函数行为

Constructor behavior in case of default, parameterized, copy ctor and assignment operator

我正在经历 Thinking in C++ 并且对 C++ 中构造函数的行为有些困惑。这是我的示例代码:

#include<iostream>
using namespace std;

class base
{
    public:
           int a;

/*Ctor 1*/           base()  { cout<<"  default"<<endl; }

/*Ctor 2*/           base(int a){ cout<<"  base::int "<<endl; }

/*Ctor 3*/           base(const base& b) { cout<<"  base::cc "<<endl; }

/*Asgn 1*/           base operator=(base b){ 
                     cout<<"  base::assignment - base"<<endl;
                     return b;
                 }

/*Asgn 2*/     /*      base operator=(int b){
                     cout<<"  base::assignment - int"<<endl;
                     return (base)b;
                 } */
};
int main()
{
    base b;
    int a = 10;
    b = a;
    system("PAUSE");
    return 0;
}

输出:

谁能给我解释一下输出结果? 我只希望打给

  1. 默认构造函数。
  2. 参数化构造函数。

我无法理解为什么我会收到对赋值运算符和复制构造函数的调用,其他对象是 "int" 类型。如果我取消注释“Asgn 2”,我会得到一个调用它而不是 Asgn 1,这是可以理解的。

如果我收到复制构造函数的调用(它始终将对象引用作为其参数),是否是因为编译器将 int 转换为基类型?

输出

default
base::int 
base::assignment - base
base::cc 

结果如下:

base b;

这里创建一个b - 这将使用默认构造函数

int a = 10;
b = a;

我们有一个赋值 - 唯一可用的取一个 base 类型的值 - 所以编译器挠头说 "ah-ha" 得到了一个可以创建对象的构造函数版本从 int 输入 base。我们可以使用它。

所以你得到了输出

cout<<"  base::int "<<endl;

现在编译器可以使用赋值运算符了。该参数是一个 base 类型的对象,但由于它是临时的,因此不需要调用它(参见 http://en.cppreference.com/w/cpp/language/copy_elision),然后赋值运算符输出

cout<<"  base::assignment - base"<<endl;

但是赋值 return 的值不作为引用 - 所以它需要将这个 return 值复制到 b - 从而调用复制构造函数。因此

cout<<"  base::cc "<<endl;

首先,base(int a) 是一个converting constructor as

  • 需要一个参数

  • 它不使用 explicit 关键字

转换构造函数可用于隐式转换:

void foo(base b);

void bar() {
    foo(3);
}

此处 int 参数将使用转换构造函数隐式转换为 base 类型。

因为通过值参数(arguments by reference passed by reference)被复制,所以正式调用了复制构造函数;但是在这里,副本的源是一个临时对象,使用 int 构造函数隐式创建的对象。所以允许编译器融合临时对象和参数,直接构建目标对象。此优化是可选的,编译器仍必须验证是否可以调用复制构造函数:它已在此处声明并可访问 (public)。

因为优化非常简单,所以几乎所有(或所有?)编译器都会做;许多编译器甚至在不太激进的优化级别(大多数优化被禁用)都这样做。

您将赋值运算符声明为按值接受参数并 return 复制(而不是引用),这种情况很少见(但 不是 非法):

/*Asgn 1*/           base operator=(base b){ 
                     cout<<"  base::assignment - base"<<endl;
                     return b;
                 }

这意味着需要复制构造函数来将参数传递给赋值运算符,以及 return 指令。

请注意,它是一个运算符这一事实是无关紧要的,您可以将其命名为 assign:

/*Asgn 1*/           base assign(base b){ 
                     cout<<"  base::assignment - base"<<endl;
                     return b;
                 }

并正常调用:

base a,b;
a.assign(b);
b.assign(base());
b.assign(base(2));
b.assign(3);

a.assign(b)会调用拷贝构造函数来创建assign.

的参数

base() 使用默认构造函数创建一个临时对象,base(2) 使用 int 构造函数创建一个临时对象(当您显式创建临时对象时,它会 not 不管构造函数是否是转换构造函数)。然后你可以 assign 在创建的临时文件上。编译器通过直接构造参数来避免复制构造。

b.assign(3)中,临时对象的创建是隐式的,构造函数是转换构造函数这一事实是相关的。

return 语句创建另一个副本; operator= 的惯用法是:

type& type::operator= (const type &source) {
    copy stuff
    return *this;
}

引用绑定到目标对象,不会发生冗余复制。