c++构造函数调用和对象创建

C++ constructor calling and object creation

本文关键字:对象 创建 函数调用 c++      更新时间:2023-10-16
class Test{
    public :
        int x;  
        Test()
        {
            x = 0;
            cout<<"constructor with no arguments called"<<endl;
        }
        Test(int xx)
        {
            x = xx;
            cout<<"constructor with single int argument called"<<endl;
        }
        
};

int main()
{
    Test a(10);
    Test aa = 10;
}

输出:程序编译和输出

具有单个int形参的

构造函数称为

具有单个int形参的

构造函数称为

但现在

class Test{
    public :
        int x;  
        Test()
        {
            x = 0;
            cout<<"constructor with no arguments called"<<endl;
        }
        Test(int xx)
        {
            x = xx;
            cout<<"constructor with single int argument called"<<endl;
        }
        
        Test( Test& xx)
        {
            x = xx.x;
            cout<<"copy constructor called"<<endl;
        }
        
        
};

int main()
{
    Test a(10);
    Test aa = 10;
}

编译失败。

constructorinvokings.cc:36:7: error: no viable constructor copying variable of type 'Test'
        Test aa = 10;
             ^    ~~
constructorinvokings.cc:23:3: note: candidate constructor not viable: no known conversion from 'Test' to 'Test &' for 1st
      argument
                Test( Test& xx)
                ^
1 error generated.

我是c++新手。

Aren't Test a(10) and Test aa = 10;相同的?

为什么添加复制构造函数与Test aa=10冲突?

如果我修改Test(Test&xx)到测试(const Test&它正在起作用。但是,当我们试图调用带整型参数的构造函数时,为什么编译器要检查复制构造函数签名呢?

请澄清

提前感谢。

到目前为止你得到的所有答案都过于复杂和/或有点误导。

在第一个构造/初始化中:

T a(10);

很明显的事情发生了。第二个构造/初始化更有趣:

T aa = 10;

这相当于:

T aa(T(10));

意思是创建一个T类型的临时对象,然后构造aa作为这个临时对象的副本。这意味着调用复制构造函数。

c++有一个默认的复制构造函数,当你没有显式声明时,它会为类创建一个复制构造函数。因此,即使class T的第一个版本没有声明复制构造函数,但仍然有一个。编译器声明的签名是T(const T &)

现在,在第二种情况下,您声明的东西看起来像复制构造函数,您将参数设置为T &,而不是const T &。这意味着编译器在尝试编译第二个表达式时,会尝试使用复制构造函数,但它不能。它抱怨你声明的复制构造函数需要一个非const参数,而给它的参数是const。所以它失败了。

另一个规则是,在编译器将初始化转换为T aa(T(10));之后,然后允许将其转换为T aa(10);。这就是所谓的"复制省略"。在某些情况下,允许编译器跳过对复制构造函数的调用。但是,只有当它验证表达式格式正确并且在复制构造函数调用仍然存在时不会生成任何编译器错误时,它才会这样做。因此,这是一个优化步骤,它可能会影响程序的哪些部分运行,但不会影响哪些程序是有效的,哪些是错误的(至少从它们是否编译的角度来看)。

Test a(10) and Test aa = 10;是不一样的。第一个构造一个总分为10分的Test,而第二个构造一个总分为10分的Test ,然后从中复制构造一个。复制构造函数的添加是冲突的,因为它声明复制构造对于两个操作数都是可变的操作。在某些情况下,复制构造函数将源作为非const引用,但这是一些复杂的情况,这里不是您的情况。

Edit:请注意,在第二种情况下,编译器被允许省略复制构造,并且通常会这样做。

(此处较少赘述的测试案例)

这两种形式的初始化实际上并不是完全等价的。

前者是你的标准结构:

T o(10);

然而,后者更复杂。

T o = 10;

这里,RHS上的表达式(10)被转换为类型T,然后对象o从转换后的表达式复制构造。复制构造在这里是不可能的,因为您的T(T&)禁止隐式T(T const&)的合成,并且RHS上的临时对象不能绑定到refto -non- const

现在,编译器被允许省略复制构造(这种情况经常发生),但是转换必须仍然有效,否则程序是病态的。


[n3290: 8.5/13]:初始化的形式(使用括号或=)通常不重要,但在初始化式或被初始化的实体有一个类类型;见下文。[. .]

[n3290: 8.5/14]:

形式出现的初始化
T x = a;

以及参数传递、函数返回、抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)称为copy-initialization。(注意:复制初始化可以调用move(12.8)。 -end note]

[n3290: 8.5/16]:初始化式的语义如下:[. .]如果目标类型是(可能是cv限定的)类类型:[..]否则(即,对于剩余的复制初始化情况,),用户定义的转换序列,可以从源转换类型为目标类型或(当使用转换函数时)的派生类,如13.3.1.4所述,并通过过载解析(13.3)选择最佳的一个。如果转换不能完成或有歧义,初始化是不规范的。 [. .]

[n3290: 12.8/31]:当满足某些条件时,实现允许省略类对象的复制/移动构造,甚至如果对象的复制/移动构造函数和/或析构函数有副作用。(. .这种拷贝/移动操作的省略,称为拷贝在下列情况下(可能是)允许省略合并以消除多个副本):[…]

  • 当一个临时类对象没有绑定到引用时(12.2)将被复制/移动到具有相同属性的类对象类型时,复制/移动操作可省略方法的目标直接构造临时对象省略了复制/移动。[. .]

(我实际上找不到一个引用明确地说明,即使副本被省略,复制构造函数仍然必须是可访问的,但情况就是这样。)

我不同意K-ballo的观点。当你定义一个只有一个参数的c'tor时,它可以被编译器用于隐式转换。这就是在这种情况下发生的。

所以当你运行程序时,它会调用"c'tor with single arg"来构造一个AWA aa。

Test aa = 10;

本声明不涉及"复制"或"赋值"。如果您为编译器提供了单个参数c'tor,那么在这种情况下,它会[错误地]使用它。

// So basically compiler converts this:
Test aa = 10;
// to
Test aa(10);

可以通过将c'tor标记为显式来防止滥用。只需将Test( int x )更改为explicit Test( int x )这里有一些关于显式关键字的解释:在c++中显式关键字是什么意思?

如果我修改Test(Test&xx)到测试(const Test&它正在起作用。但是,当我们试图调用带整型参数的构造函数时,为什么编译器要检查复制构造函数签名呢?

我也不明白这个,想知道。:)

我注意到copy c'tor从来没有被调用过。

#include<iostream>
using namespace std;
class Test
{
public :
    int x;
    Test() { x=0; cout << "    Test() - " << x << endl;}
    Test(int xx) { x=xx; cout << "    Test(int xx) - " << x << endl;}
    Test(Test& xx){ x=xx.x; cout << "    Test(Test& xx) - " << x << endl;}
    Test(const Test& xx){ x=xx.x; cout << "    Test(const Test& xx) - " << x << endl;}
    Test& operator= (const Test& xx) { x=xx.x; cout << "    Test& operator= (const Test& xx) - " << x << endl; return *this;}
};

int main()
{
    cout << "--Test a(10);--" << endl;
    Test a(10);
    cout << "--Test aa = 20;--" << endl;
    Test aa = 20;
    cout << "--Test aaa = aa;--" << endl;
    Test aaa = aa;
    cout << "--aaa = aa;--" << endl;
    aaa = aa;
    cout << "--aaa = 30;--" << endl;
    aaa = 30;
}
/*
OUTPUT:
    --Test a(10);--
        Test(int xx) - 10
    --Test aa = 20;--
        Test(int xx) - 20
    --Test aaa = aa;--
        Test(Test& xx) - 20
    --aaa = aa;--
        Test& operator= (const Test& xx) - 20
    --aaa = 30;--
        Test(int xx) - 30
        Test& operator= (const Test& xx) - 30
*/

和当copy c'tor with const arg Test(const Test& xx)被注释掉时:

我们得到编译错误。

D:WorkspacesCodeBlocksTestmain.cpp: In function 'int main()':
D:WorkspacesCodeBlocksTestmain.cpp:21:19: error: no matching function for call to 'Test::Test(Test)'
D:WorkspacesCodeBlocksTestmain.cpp:10:9: note: candidates are: Test::Test(Test&)
D:WorkspacesCodeBlocksTestmain.cpp:9:9: note:                 Test::Test(int)
D:WorkspacesCodeBlocksTestmain.cpp:8:9: note:                 Test::Test()
Process terminated with status 1 (0 minutes, 0 seconds)
4 errors, 0 warnings

第21行是Test aa = 20;