为什么 p3 在这个例子中需要一个默认构造函数?
Why does p3 need a default constructor in this example?
假设我有这样的 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
};
在关于error C2512的微软文档中,有一个对象创建时没有参数的例子,因为它没有默认构造函数,所以会出现这个错误。然而,在这个例子中,我通过传递所有必要的参数来创建我的对象。为什么我还需要默认构造函数?
tri3(p3 p1, p3 p2, p3 p3)
构造函数无法初始化其 class 的 _p1
、_p2
和 _p3
成员,因此它们必须具有默认构造函数。
_p1 = p1;
_p2 = p2;
_p3 = p3;
这不是施工。这是分配给 existing 对象。它们已经建成。
要正确构造 class 成员,您必须在构造函数声明本身中使用 成员初始化 。
tri3(p3 p1, p3 p2, p3 p3) : _p1{p1}, _p2{p2}, _p3{p3}
{
}
在正确使用成员初始化方面,您必须遵循许多重要规则,有关详细信息,请参阅 C++ 教科书。
问题是由您的 tri3
构造函数引起的。
按照您的方式实现它,意味着 p3 _p1
等应该是默认构造的,然后分配给(在 tri3
构造函数的主体中)。
您应该像下面这样更改实现以避免 _p1
、_p2
、_p3
:
的默认构造步骤
tri3(p3 p1, p3 p2, p3 p3)
:_p1(p1), _p2(p2), _p3(p3)
{}
这称为成员初始值设定项列表:Constructors and member initializer lists
您声明的任何对象都是将调用其构造函数的候选者。
如果对象有默认构造函数,只需要 data-type 和对象名就足够了,不需要任何额外的东西,比如括号。否则,你应该仔细设计你的 class 是否需要成员初始化。
大多数情况下编译器会警告您 variables/object 您没有正确初始化。
正如我上面提到的,您声明的任何对象都需要初始化。编译器将寻找可用的构造函数。如果有一个完美的契合,它将被选中。否则,您将不可避免地遇到错误,例如:
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
}
因此,有时它似乎可以工作,因为某些编译器,但您的程序会在某一时刻崩溃。
假设我有这样的 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
};
在关于error C2512的微软文档中,有一个对象创建时没有参数的例子,因为它没有默认构造函数,所以会出现这个错误。然而,在这个例子中,我通过传递所有必要的参数来创建我的对象。为什么我还需要默认构造函数?
tri3(p3 p1, p3 p2, p3 p3)
构造函数无法初始化其 class 的 _p1
、_p2
和 _p3
成员,因此它们必须具有默认构造函数。
_p1 = p1;
_p2 = p2;
_p3 = p3;
这不是施工。这是分配给 existing 对象。它们已经建成。
要正确构造 class 成员,您必须在构造函数声明本身中使用 成员初始化 。
tri3(p3 p1, p3 p2, p3 p3) : _p1{p1}, _p2{p2}, _p3{p3}
{
}
在正确使用成员初始化方面,您必须遵循许多重要规则,有关详细信息,请参阅 C++ 教科书。
问题是由您的 tri3
构造函数引起的。
按照您的方式实现它,意味着 p3 _p1
等应该是默认构造的,然后分配给(在 tri3
构造函数的主体中)。
您应该像下面这样更改实现以避免 _p1
、_p2
、_p3
:
tri3(p3 p1, p3 p2, p3 p3)
:_p1(p1), _p2(p2), _p3(p3)
{}
这称为成员初始值设定项列表:Constructors and member initializer lists
您声明的任何对象都是将调用其构造函数的候选者。 如果对象有默认构造函数,只需要 data-type 和对象名就足够了,不需要任何额外的东西,比如括号。否则,你应该仔细设计你的 class 是否需要成员初始化。
大多数情况下编译器会警告您 variables/object 您没有正确初始化。
正如我上面提到的,您声明的任何对象都需要初始化。编译器将寻找可用的构造函数。如果有一个完美的契合,它将被选中。否则,您将不可避免地遇到错误,例如:
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
}
因此,有时它似乎可以工作,因为某些编译器,但您的程序会在某一时刻崩溃。