【问题标题】:Issue with C++ constructorC++ 构造函数的问题
【发布时间】:2011-08-23 11:16:49
【问题描述】:

编辑:这个问题出现了,我想我做得很好!去堆栈溢出!! :D

我要考试了,去年考试的一个问题是发现以下构造函数的实现问题并编写一个更正的构造函数。

Rectangle::Rectangle(string col, int len, int br)
{
    setColour(col);
    length =len;
    breadth=br;
}

类定义如下:

class Polygon
{
public:
    Polygon(string col="red");
    void printDetails(); // prints colour only
    virtual double getArea()=0;
    void setColour(string col);
private:
    string colour;
};


class Rectangle : public Polygon
{
public:
    Rectangle(string, int, int);
    void printDetails(); // prints colour and area
    // for part 3, delete the line below
    double getArea();
private:
    int length;
    int breadth;
};

我已经将代码写入编译器并且运行良好。我猜这个问题与继承有关,因为string colour; 是私有的,但setColour 是公开的,所以我无法弄清楚。除非它的Rectangle::Rectangle(string col, int len, int br):length(len), breadth(br) 然后在contrucor里面设置颜色什么的。

它只值 3 分,所以如果没有人愿意回答,这没什么大不了的,但我认为如果我要从事程序员的职业,尽可能多地了解我的兴趣。 ;)

感谢您的帮助。

PS,请参阅 Rectangle 中的 getArea()。当我删除它时,它告诉我它“无法实例化抽象类”。这是什么意思?

编辑:这是主要内容:

void main (void)
{
    Rectangle rect1 ("blue",5,6);
    Rectangle *prect2 = new Rectangle("red",5,6);
    rect1.setColour("red");
    rect1.printDetails();
    prect2->printDetails();
}

【问题讨论】:

标签: c++ constructor


【解决方案1】:

我没有发现任何问题,尽管您可以通过使用初始化列表来提高效率(否则您的两个类的私有成员都会被初始化两次):

Rectangle::Rectangle(string col, int len, int br) 
: Polygon(col), length(len), breadth(br)
{

}

注意初始化列表也可以调用Polygon的构造函数。

请参阅Why should I prefer to use member initialization list?,了解使用初始化列表的优势的完整描述。

【讨论】:

  • 我想这就是问题所指的。它没有说有什么问题,只是说实现有问题,并编写一个更正的代码,所以我认为我们在这里讨论的是最佳实践。我相信这已经回答了我的问题。谢谢!
  • ...并在两个构造函数中通过引用传递字符串将摆脱另外两个字符串构造。
【解决方案2】:

如果是关于最佳 C++ 实践,那么:

  1. 通过 const 引用传递字符串参数;
  2. 使用初始化列表并通过将颜色传递给父构造函数来初始化颜色,而不是 setColour。

【讨论】:

  • Pass string parameters by const reference 除非您要复制字符串...然后您应该按值传递并复制交换或移动...
【解决方案3】:

我唯一看到的是有两个 printDetails() 但基类不是虚拟的,所以你不会得到预期的多态行为。

【讨论】:

  • 我同意这一点。对我来说,这是唯一“真正”的问题。
  • 代码确实打印出颜色和面积,所以当我打印出矩形细节时,它使用了自己类中的函数,例如cout << getColour() << " : " << getArea() << endl;。此外,问题是通过构造函数的实现来发现错误。感谢您的回复。
  • 确实,如果你将堆分配对象 (prect2) 的指针转换为基类指针 (Polygon*) 然后调用重写方法,我认为它只会表现不佳。这个问题当然与您第一次询问的构造函数问题是分开的。
  • ...假设您想要多态行为。有一些(次要)开销与此相关。
【解决方案4】:

我看到的主要“问题”(而且有点小)是派生构造函数让父类使用其默认的 colo(u)r 值(“red”),然后提供它自己的值。这有点浪费,因为你本可以从一开始就给它正确的。

Rectangle::Rectangle(string col, int len, int br) : Polygon(col) {
    length =len;
    breadth=br;
};

现在,完成上述操作后,您不妨以这种方式初始化所有成员:

Rectangle::Rectangle(string col, int len, int br) 
    : Polygon(col), length(len), breadth(br) {};

嗯。现在我看到这个,还有另一个问题。您的构造函数通过复制传入 std::string 对象,而不是修改它们。那也太浪费了。所有构造函数string参数都应该改为string const &参数。这可能会避免在运行时对字符串进行额外的复制构造,并通知编译器和用户您实际上并未修改输入字符串(这是一种很好的做法,但实际上您并未修改)。

所以最终版本看起来更像:

Rectangle::Rectangle(string const & col, int len, int br) 
    : Polygon(col), length(len), breadth(br) {};

对于每个调用的 Rectangle 构造函数,此公式将您从 4 个 std::string 构造(和 3 个破坏)减少到 2 个。通过对 Polygon 构造函数进行相同的更改,可以进一步将其减少为 1 个。

【讨论】:

  • 是的,我也是这么想的,但是如果它指定一个字符串,即“红色”而不是创建/初始化一个字符串变量并将其分配到内存中,这真的很浪费吗?我认为这类似于有一个后备值,比如一个 int 的“0”,以防万一出现问题并且没有提供一个。
  • 好吧,如果你要在其中保留它的默认值,它不会非常浪费,但是给定的构造函数不会这样做。它让Polygon 构造函数采用默认值(它创建了std::string "red"),然后Rectangle 构造函数主体立即将其销毁并复制构造新的col。有点浪费。
【解决方案5】:

您应该使用 col 参数调用基本构造函数:

Rectangle::Rectangle(string col, int len, int br) : Polygon(col)
{
    //setColour(col);
    length =len;
    breadth=br;
}

关于getArea()
删除它时它不编译的原因是因为该函数在 Polygon 类 virtual double getArea()=0; 中使用 =0 标记为 pure virtual;

【讨论】:

    【解决方案6】:

    关于Rectangle::getArea() 的PS:在Polygon 中virtual double getArea()=0; 的声明意味着该函数是pure virtual function。您可以从概念上想到:“所有多边形都有面积,所以我应该能够问它是什么,但除非多边形具有特定类型(正方形,圆形),否则它不会知道它的面积是多少”。

    这意味着您的 Polygon 类是一个抽象类。通过不在 Rectangle 类中定义 getArea(),您的 rectangle 类也是一个抽象类。您无法实例化 Rectangle,因为编译器不知道任何 Rectangle::getArea() 函数定义。

    【讨论】:

      【解决方案7】:

      您还可以在初始化列表中添加对基类构造函数的调用:

      Rectangle::Rectangle(string col, int len, int br)
          : Polygon(col), length(len), breadth(br)
      

      它使用基类的构造函数,所以有点整洁。

      【讨论】:

        【解决方案8】:

        我可以在这里想到一些可能的问题:

        1. 使用初始化列表来分配值。

        2. 调用基类构造函数设置颜色。

        3. 字符串可能不是表示颜色的最佳类型。也许在这里使用 enum 或单独的 color class 会更好。如果处理得当,这也可以防止传递无效颜色。

        4. 谈到无效值:应该在构造函数中验证长度和宽度(您不希望以负面区域结束,是吗?)。至少使用一个断言。它对发布版本没有影响,但可以防止开发错误。对于更公开的界面,exceptions 也可能是一种选择(这在某种程度上是个人喜好)。

        5. 如果您真的想使用字符串作为颜色,请通过 const 引用 传递它(并可能测试空字符串等边缘情况)。

        6. printDetails 应该是virtual,所以你可以用基类指针调用它。当前的实现可能无法按预期运行。

        7. 该类似乎是为多态而设计的。如果需要从基类指针中删除,则必须定义 虚拟析构函数。既然已经有虚方法了,估计也没什么坏处。

        8. getAreaprintDetails 应声明为 const,以便可以在 const 对象上调用它们。他们不应该修改对象。

        这只是一个可能性列表。其中许多取决于类的预期用途,可能不需要,但仔细考虑它们并没有什么坏处。

        【讨论】:

          【解决方案9】:

          如前所述,printDetails 不会按预期运行。
          我还认为仅仅在 Rectangle 类中声明 getArea() 有点作弊,因为你没有为它提供实现,如果你碰巧在你的代码中调用它,你会得到一个链接器错误。

          【讨论】:

          • 还有一个我没有包含在实现中的文件。假设一切都正确实施。感谢您的回复。
          【解决方案10】:

          可能存在初始化顺序问题。 Polygon::setColour 可以调用纯虚Polygon::getArea。 (没有迹象表明它需要,但存在这种可能性。) Rectangle 中的实现可能需要 lengthbreadth 来计算面积,但它们不是尚未初始化。

          最小的解决方法是在调用 setColour 之前初始化 lengthbreadth

          Rectangle::Rectangle(string col, int len, int br)
          {
              length =len;
              breadth=br;
              setColour(col);
          }
          

          当然,最好从 Polygon 中删除 pure virtual getArea() 声明,因为任何 Polygon 方法似乎都不需要它。但这超出了问题的范围。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-02-27
            • 1970-01-01
            • 2011-03-13
            相关资源
            最近更新 更多