C++ 如何在另一个类中创建不同类的对象



我有一个有两个类的程序,一个用于点,一个用于矩形。我需要在我的矩形类中创建两个点类对象并使它们私有。我不知道如何使用矩形构造函数来使用点构造函数制作点,每次我这样做时都会收到错误"类"Point2d"不存在默认构造函数"。即使我制作了一个默认的构造函数,我仍然得到这个。我在这里不知所措。

#include <iostream>
#include <cmath>
using namespace std;
class point2D {
private:
int x = 0;
int y = 0;
public:
int getX() {
return x;
}
int getY() {
return y;
}
point2D(int ax, int ay) {
x = ax;
y = ay;
}
};
class rectangleType {
private:
point2D p0;
point2D p1;
public:
int getX0() {
return p0.getX();
}
int getY0() {
return p0.getY();
}
int getX1() {
return p1.getX();
}
int getY1() {
return p1.getY();
}
int getWidth() {
return abs(getX1() - getX0());
}
int getLength() {
return abs(getY1() - getY0());
}
int getPerimeter() {
return 2 * (getWidth() + getLength());
}
int getArea() {
return (getWidth() * getLength());
}
rectangleType(int ax0, int ay0, int ax1, int ay1) {
point2D p0(ax0, ay0);
point2D p1(ax1, ay1);
}
};

基+成员初始化列表中未以其他方式专门初始化的成员和基类需要提供默认构造。在最简单的形式中,给定如下:

class S
{
public:
S(int) {}
};

这是不可能的:

S s;

因为定义的唯一 CTOR(尽管默认复制 CTOR)需要一个int参数,并且没有提供任何参数。这也许是设计S的人故意的.没有这个需要论证的S可能没有意义。

将这一点推进到您的具体情况,您有一个类,point2D,其唯一定义的构造过程(尽管默认复制)是这样的:

point2D(int ax, int ay)

就像我们前面示例中S一样,这意味着如下内容:

point2D pt;

是不可能的。但这正是这里正在发生的事情(以及其他几个问题)

rectangleType(int ax0, int ay0, int ax1, int ay1) 
// here, before entering the function body below, p0 and p1
//  must be successfully constructed *somehow*
{
point2D p0(ax0, ay0);
point2D p1(ax1, ay1);
}

在输入构造函数主体之前,必须构造所有基类(您没有基类)和成员变量(您有两个:p0p1)。由于您没有直接初始化成员初始化列表中的p0p1成员,因此编译器会尝试查找默认构造函数(不带任何参数,或者声明的所有参数都有足够的默认值)。 它找不到。通过提供非默认的 ctor,您已经声明了"这是这些应该构建的方式"。因此,编译器说它找不到成功创建这些东西的方法。

在您的情况下,提供这样的构造函数虽然可能,但不一定是正确(当然不是唯一的)解决方案。是的,你可以,但事实证明,所做的只是增强以后的问题。例如,这将允许编译:

rectangleType(int ax0, int ay0, int ax1, int ay1) 
{
point2D p0(ax0, ay0);
point2D p1(ax1, ay1);
}

但是现在你还有另一个问题。您的成员p0p1不是您在上面看到的成员。上面的代码所做的只是声明另外两个与你的成员同名的 id(从而将后者遮蔽起来),构造它们,在函数退出时丢弃它们,最终让你的两个成员默认初始化,但不使用提供的参数。此后不久,你挠了挠头,想知道出了问题。

您仍然可以为point2D提供默认 ctor (您以前不想这样做,但现在感觉有点"被迫"),然后执行以下操作:

rectangleType(int ax0, int ay0, int ax1, int ay1) 
{
p0 = point2D(ax0, ay0);
p1 = point2D(ax1, ay1);
}

但现在我们只是堆积在垃圾上,一种反模式正在形成。现在,我们使用我们认为不需要(甚至可能是故意不想要的)ctor默认构造我们的p0p1成员,然后通过构造另外两个point2D对象并使用复制分配来收获它们来放弃这些努力。

有更好的方法

成员初始化

该语言提供了一种告诉编译器的方法,"在进入我的构造函数体之前,首先构造我的基类(如果有的话)和这样的成员":

rectangleType(int ax0, int ay0, int ax1, int ay1) 
: p0(ax0, ay0)
, p1(ax1, ay1)
{
}

语法可能看起来有点奇怪,但除了你以前见过的前导冒号之外,其余的都见过。事实上,您之前甚至在以前的代码中使用它,但在错误的位置和错误的目标变量中使用它。此语法表示"像这样构造这些成员,然后进入类构造函数体"。

另一个例子

这不是您可以(或应该)使用该语言此功能的唯一地方。例如,point2D构造函数如下所示:

point2D(int ax, int ay) 
{
x = ax;
y = ay;
}

但是现在您知道您也可以这样做:

point2D(int ax, int ay) 
: x(ax)
, y(ay)
{
}

当然,这并不重要,任何有理智的编译器都会在上述微不足道的这个功能的用法中生成类似/相同的代码,但它强调了一个更大的整体主题。如果可能,请使用成员初始化列表来构造成员。它通常更有效率,而且在许多情况下效率更高。

关于订购的特别说明

我提到这一点只是因为它通常是相关的,并且可能是一些你没有预料到的有趣行为的原因。使用成员初始化列表时,成员的构造顺序不是由列表的顺序决定的;它由成员声明的顺序决定。一个例子来说明我的意思。

在您的班级中,分数p0p1按该特定顺序自上而下声明。使用我们新发现的成员初始化语法,当我们这样做时会发生什么(注意成员列表中的顺序):

rectangleType(int ax0, int ay0, int ax1, int ay1) 
: p1(ax1, ay1)
, p0(ax0, ay0)
{
}

看起来p1将在p0之前构建.事实并非如此。类中的声明顺序才是最重要的。由于类是这样的:

private:
point2D p0;
point2D p1;

这意味着p0将首先构造,然后p1而不考虑成员初始化列表中的顺序。这可能会导致一些有趣的行为,尤其是当您没有预料到它时。在你的琐碎案例中,总体上并不重要,但仍然值得注意。

您的错误告诉您必须为 points 类创建一个默认构造函数,例如

point2D() {}

或者如果你想将 x 和 y 初始化移动到构造函数

point2D()
: x { 0 }, y { 0 } {}

发生这种情况是因为您创建了一个采用两个参数的专用构造函数。如果没有这样,编译器将为您生成一个默认构造函数。:)

至于矩形类中的初始化,请尝试以下操作:

rectangleType(int ax0, int ay0, int ax1, int ay1)
: p { { ax0, ay0 } }, p1 { { ax1, ay1 } } {}

此外,要删除多余的分配(谢谢@user4581301),您可以将当前2D(int ax, int ay)转换为以下内容:

point2D(int ax, int ay)
: x { ax }, y { ay } {}

延伸阅读:

  • https://en.cppreference.com/w/cpp/language/constructor(再次感谢@user4581301)

构造函数应如下所示:

rectangleType(int ax0, int ay0, int ax1, int ay1)
: p0 {ax0, ay0},
p1 {ax1, ay1}
{}

这是一个成员初始化列表,通过将参数传递给它们各自的构造器来初始化值 P0 和 P1

最新更新