【问题标题】:Virtual functions "Shape" Assignment虚拟功能“形状”分配
【发布时间】:2017-11-28 01:13:26
【问题描述】:

我似乎无法让我的圆柱体类正确执行其打印和音量功能。以下是作业说明: 设计一个名为 Shape 的类,它是一个抽象基类。 Shape 有两个纯虚函数,printShapeName 和 print。

Shape 包含另外两个虚函数,面积和体积,每个虚函数都有一个返回零值的默认实现。

Point 类从 Shape 继承了这些实现(点的面积和体积都为零)。 A Point 具有 x 和 y 坐标私有成员。

类 Circle 是从具有公共继承的 Point 派生的。 Circle 的体积为 0.0,因此基类成员函数 volume 不会被覆盖。 Circle 具有非零面积,因此在此类中覆盖了面积函数。编写 get 和 set 函数以返回并为 Circle 分配新半径。

类 Cylinder 派生自具有公共继承的 Circle。 Cylinder 的面积和体积与 Circle 不同,因此在这个类中面积和体积函数都被覆盖了。编写 get 和 set 函数以返回高度并分配新高度。

创建一个点、一个圆和一个圆柱体,然后打印结果。

//
//  Shape.hpp
//  HW6_VirtualFunctions
//
//  Created by Aviv Fedida on 11/25/17.
//  Copyright © 2017 Aviv Fedida. All rights reserved.
//

#ifndef Shape_hpp
#define Shape_hpp

#include <stdio.h>
#include <iostream>
#include <cmath>
using namespace std;

class Shape {
protected:  // protected members
    double width, height, radius, pi, x, y;
public:
    void setWidth(double a);    // prototype for width setter
    void setHeight(double b);   // prototype for height setter
    void setX(double c);
    void setY(double d);
    void setRad(double r);
    void setPi(double p);
    double getWidth() { // get width to return only
        return width;
    }
    double getHeight() { // get height to return only
        return height;
    }
    double getX() {
        return x;
    }
    double getY() {
        return y;
    }
    double getRad() {
        return radius;
    }
    double getPi() {
        return pi;
    }
    // Create public virtual functions
    virtual void printShapeName() = 0; // pure virtual for printing shape's name
    virtual void print(double a, double b) = 0; // pure virtual print function
    virtual double area() {  // virtual area function returns default null
        return 0;
    }
    virtual double volume() {  // virtual default volume function returns null
        return 0;
    }
};  // END BASE CLASS -------------------------------------------------------------------------------------

class Point: public Shape {
    // x & y coordinates needed for a point
public:
    // Prototypes for print & set functions
    void printShapeName();
    void print(double a, double b);
    };  // end first derived class ------------------------------------------------------------------------

class Circle: public Point {
public:
    //  Protoypes for print functions
    void printShapeName();
    void print(double r, double p);
    //  Prototypes for area & volume functions
    double area(double r, double p);
};  // end second derived class ----------------------------------------------------------------------------

class Cylinder: public Circle {
public:
    double area(double r, double p);
    void printShapeName();
    double volume(double r, double p, double h);
    void print(double r, double p, double h);
}; // end third and final derived class --------------------------------------------------------------------------

//  Some definitions outside classes
//--------------------------------------------------    BEGIN   -----------------------------------------------------------
//  Shape base class setter functions defined
void Shape::setWidth(double a) {
    width = a;
}
void Shape::setHeight(double b) {
    height = b;
}
void Shape::setX(double c) {
    x = c;
}
void Shape::setY(double d) {
    y = d;
}
void Shape::setRad(double r) {
    radius = r;
}
void Shape::setPi(double p) {
    p = 3.1416;
    pi = p;
}

void Point::printShapeName() {   // Print name of class
    cout << "Point " << endl;
}
void Point::print(double a, double b) {   // Print values within class
    cout << "(" << a << "," << b << ")" << endl;
}

void Circle::printShapeName() {
    cout << "Circle " << endl;
}
//  Circle area function defined
double Circle::area(double r, double p) {
    double area;
    area = p*(pow(r,2));
    return area;
}
void Circle::print(double r, double p) {
    cout << "Area of circle is: " << area(r, p) << endl;
    cout << "Volume of circle is: " << volume() << endl;
}

void Cylinder::printShapeName() {
    cout << "Cylinder " << endl;
}
double Cylinder::area(double r, double p) {
    double area;
    area = 2*p*r;
    return area;
}
double Cylinder::volume(double r, double p, double h) {
    double volume;
    volume = p*(pow(r,2))*h;
    return volume;
}
void Cylinder::print(double r, double p, double h) {
    cout << "Area of cylinder is: " << area(r, p) << endl;
    cout << "Volume of cylinder is: " << volume(r, p, h) << endl;
}



#endif /* Shape_hpp */

//
//  main.cpp

#include <iostream>
#include "Shape.hpp"
#include "Shape.cpp"

using namespace std;

int main() {
    double pi = 3.1416; // Variable for pi
    //  Instantiate class objects
    Point guard;
    Circle k;
    Cylinder cid;
    //  Instantiate pointers to class objects
    Shape *pptr;
    Shape *kptr;
    Shape *cptr;
    //  Assign memory of objects to pointer variables
    pptr = &guard;
    kptr = &k;
    cptr = &cid;
    //  Call objects via pointers and print members
    pptr->printShapeName();
    pptr->print(5,6);
    cout << '\n';
    kptr->printShapeName();
    kptr->print(9,pi);
    cout << '\n';
    cptr->printShapeName();
    cptr->getHeight();
    cptr->setHeight(8);
    cptr->print(5,pi);
    return 0;
}

如果我尝试在 Cylinder 类的打印函数中添加第三个高度参数,则会收到错误消息。它最终使用了我的 Circle 类定义。

【问题讨论】:

  • “请帮助”这就是重点。 “我似乎无法让我的圆柱体类正确执行其打印和体积功能。”出了什么问题?当您和回答者知道问题所在时,答案总是会更好。
  • 我强烈建议不要这样做:#include "Shape.cpp"。包括头文件。编译和链接实现文件。可能 IDE 会做他们应该做的事情,并为您编译和链接这个文件,从而导致大量的链接器错误。由于我们不知道您的问题是什么,因此可能就是这样。也可能不是。
  • 您没有正确实施 OO 编程,这不是错误,它很可能与错误有关。
  • yegor256.com/2014/09/16/getters-and-setters-are-evil.html ...我会说getter和setter没有任何价值。
  • @user4581301感谢您提供提供帮助意图的唯一评论。是的,我应该更清楚。那肯定是个错误。另一个是圆柱类中 print() 函数的输出。内容如下:

标签: c++ inheritance polymorphism virtual-functions abstraction


【解决方案1】:

如果您还没有找到答案,cptr-&gt;print(5,pi); 会使用 2 个参数调用 printCircle 有一个 2 参数 print Cylinder 有一个 3 参数 print。由于Cylinder 继承了Circle 的第二个参数print,所以Cylinder 被打印成Circle

JakeFreeman 的评论可能没有帮助,但它完全正确。让我们尝试在不重复教科书的几章的情况下使其有用。

您与两个主要的 OO 概念发生冲突:封装和多态性。

封装是这里的主要问题,处理它可以解决多态问题。

Cylinder 对象应该代表一个且仅代表一个圆柱体。它应该有它的半径和高度存储在其中。调用函数并传入半径和高度在意识形态上是完全错误的。当您在Cylinder 上执行方法时,它应该已经知道它的半径和高度。 volume 方法不应该带参数,因为计算体积所需的一切都应该是已知的。这允许任何形状具有相同的volume 接口,即使支持该接口的方法可能对于每个形状都不同。

同样的逻辑也适用于区域。

接下来,并非所有形状都有半径,所以Shape 应该对半径一无所知。或者身高。或深度。也许形状知道一些锚点,但这就是形状应该知道的全部。

同样print 应该不需要任何参数,除了可能是对您要打印到的 IO 流的引用,因为 IO 流不是人们通常期望 Cylinder 知道或关心到足以包含的东西。

一个唠叨:为什么要绕过圆周率?如果您有一个 pi 不是常数的系统,那么您将拥有一个非常奇怪的系统。

修改后的形状:

class Shape {
protected:  // protected members
    static constexpr double pi = 3.1459 // add more precision as needed
    double x, y;
public:
    Shape (double posx, double posy): x(posx), y(posy)
    { // set up as much as you can in a constructor because otherwise you always 
      // have to look over your shoulder and test, "Did the object get properly 
      // initialized?"
    }
    void setX(double c)
    {
         if (c is within logical bounds)
         { // because a setter that does not protect the object is no better for
           encapsulation than a public member variable
             x = c;
         }
    }
    void setY(double d);
    {
         if (d is within logical bounds)
         {
             x = d;
         }
    }
    double getX() {
        return x;
    }
    double getY() {
        return y;
    }
    // Create public virtual functions
    virtual void printShapeName() = 0; // pure virtual for printing shape's name
    virtual void print() = 0; // pure virtual print function
    virtual double area() {  // virtual area function returns default null
        return 0;
    }
    virtual double volume() {  // virtual default volume function returns null
        return 0;
    }
}; 

注意除了形状的基本结构外,没有任何设置。在正方形上强制使用setRadius 函数简直是愚蠢的。

还请注意,这消除了对Point 的需要,但这是一件好事。圆是点的一种吗?不。一个圆是在一个点上。除了极少数例外,除非存在所谓的 is-a 关系,否则不要扩展。正方形是特殊的矩形。矩形是一种特殊的形状。但两者都不是重点。

我建议将setXsetY 组合成一个setPosition,这两者同时进行,因为稍后您可以更轻松地将其转换为原子事务。

修改Circle:

class Circle: public Shape {
protected:
    double radius;
public:
    Circle(double posx, double posy, double rad): Shape(posx, posy), radius(rad)
    {
    }
    void setRadius(double rad)
    {
        if (rad makes sense)
        {
            radius = rad;
        }
    }
    double getRadius()
    {
        return radius;
    }
    //  Protoypes for print functions
    void printShapeName();
    virtual void print();
    double area();
};

double Circle::area() {
    return  pi*radius*radius; // note on pow. It can be very slow. For simple 
                              // exponents just multiply
}
void Circle::print() {
    cout << "Area of circle is: " << area() << '\n';
    // also shy away from `endl` in favour of a raw end of line.
    // endl flushes the stream to the underlying media, also very expensive,
    // so something best done when you have to or you have nothing better to do. 
    cout << "Volume of circle is: 0\n";
}

Circle 将需要添加的内容添加到Shape。而已。一点也不差。 Cylinder 也会这样做。它是一个圆加上一个height,一个height 的访问器,一个使用Circlearea 方法和height 的体积计算,它自己的area 方法来计算其表面积,以及另一个打印打印其统计信息的方法

附录:如果您的编译器支持它,并且此时应该支持它,请利用 override 关键字。如果编译器发现一个标记为覆盖的方法并且它没有覆盖任何东西,你会得到一个很好的干净错误消息,而不是一堆调试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-12
    • 1970-01-01
    • 2010-11-01
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 2011-03-29
    相关资源
    最近更新 更多