代码之家  ›  专栏  ›  技术社区  ›  nohaga

为什么在本例中p3需要默认构造函数?

  •  1
  • nohaga  · 技术社区  · 2 年前

    假设我有这样的C++代码:

     //main.cpp
    
    #include "p3.h"
    #include "tri3.h"
    
    int main()
    {
        p3 point1(0.0f, 0.0f, 0.0f);
        p3 point2(1.0f, 0.0f, 0.0f);
        p3 point3(2.0f, 0.0f, 0.0f);
    
        tri3 triangle(point1, point2, point3);
    }
    

    //p3.h
    
    #pragma once
    
    class p3
    {
    public:
        float _x;
        float _y;
        float _z;
        p3(float x, float y, float z)
        {
            _x = x;
            _y = y;
            _z = z;
        }
    };
    

    //tri3.h
    
    #pragma once
    
    #include "p3.h"
    
    class tri3
    {
    public:
        p3 _p1;
        p3 _p2;
        p3 _p3;
        tri3(p3 p1, p3 p2, p3 p3)
        {
            _p1 = p1;
            _p2 = p2;
            _p3 = p3;
        }
    };
    

    编译失败,Visual Studio 2022中出现以下错误: error C2512: 'p3': no appropriate default constructor available

    当我像这样编辑“p3.h”时,编译是成功的,没有错误:

    //p3.h
    
    #pragma once
    
    class p3
    {
    public:
        float _x;
        float _y;
        float _z;
        p3(float x, float y, float z)
        {
            _x = x;
            _y = y;
            _z = z;
        }
    
        p3() = default; // <-- Adding this makes the program compile just fine
    };
    

    在Microsoft文档中,关于 error C2512 例如,创建对象时没有参数,由于没有默认构造函数,因此会发生此错误。然而,在本例中,我通过传递所有必要的参数来创建对象。为什么我仍然需要一个默认构造函数?

    3 回复  |  直到 2 年前
        1
  •  3
  •   Sam Varshavchik    2 年前
        tri3(p3 p1, p3 p2, p3 p3)
    

    构造函数未能初始化其类的 _p1 , _p2 _p3 成员,因此它们必须具有默认构造函数。

            _p1 = p1;
            _p2 = p2;
            _p3 = p3;
    

    这不是建筑。这是分配给 现有的 物体。它们已经建成。

    要正确构造类成员,必须使用 成员初始化 在构造函数声明本身中。

        tri3(p3 p1, p3 p2, p3 p3) : _p1{p1}, _p2{p2}, _p3{p3}
        {
        }
    

    有许多重要的规则必须遵循,当涉及到正确使用成员初始化时,请参阅C++教科书获取更多信息。

        2
  •  1
  •   wohlstad    2 年前

    问题是由您的 tri3 建造师。

    用你的方式实现它意味着 p3 _p1 etc应该是默认构造的,然后分配给(在 tri3 构造函数)。

    您应该像下面这样更改实现,以避免默认的构造步骤 _p1 , _p2 , _p3 :

    tri3(p3 p1, p3 p2, p3 p3)
        :_p1(p1), _p2(p2), _p3(p3)
    {}
    

    这叫做 成员初始值设定项列表 : Constructors and member initializer lists

        3
  •  0
  •   Orkhan Aliyev    2 年前

    您声明的任何对象都是其构造函数将被调用的候选对象。 若对象有默认构造函数,那个么只有数据类型和对象名称就足够了,不需要任何额外的东西,比如括号。否则,无论是否需要初始化成员,都应该仔细设计类。

    大多数情况下,编译器会警告您未正确初始化的变量/对象。

    如上所述,您声明的任何对象都需要初始化。编译器将查找可用的构造函数。如果有一个完美的适合,它将被选中。否则,将不可避免地出现如下错误:

    error C2512: 'dataType': no appropriate default constructor available

    此外,试图通过 default 关键字不合适。正如我所说,您将创建的对象可能需要初始化一些对象,即使编译器没有通知您。

    例如:

    class Foo
    {
    public:
       Foo(){};// This is a default constructor
       // You could use Foo() = default; instead. No difference between them
    }
    // You can safely create it
    Foo foo;// ok
    Foo foo(); // ok
    

    但如果你有以下情况:

    class Bar
    {
    public:
        Bar(const std::string& string) : _string("test"),_string_(string) ;// both should be here, you cannot use default constructor for this class. 
    //(In addition, as _string("test") suggests, it may have an rvalue but the reference type can't)
        {};
        
    private:
       // Both should be in initializer list
       const std::string _string;
       std::string& _string_;
    }
    

    这是一个编译器会警告你的情况。 但如果你想做这样的事情:

    class FooBar
    {
    public:
    FooBar(){};
    private:
    int m_number;// some compilers won't even warn you about this
    Bar* p_PointerObject;// or this
    }
    

    因此,有时由于某些编译器的原因,它似乎可以工作,但您的程序会在某个时刻崩溃。