Виртуальные функции "Форма" Назначение

Я не могу заставить свой класс цилиндров правильно выполнять функции печати и объема. Вот инструкции для назначения: Разработайте класс с именем Shape, который является абстрактным базовым классом. Shape имеет две чисто виртуальные функции, printShapeName и print.

Shape содержит две другие виртуальные функции, area и volume, каждая из которых имеет реализацию по умолчанию, которая возвращает нулевое значение.

Класс Point наследует эти реализации (площадь и объем точки равны нулю) от Shape. Точка имеет координаты x и y для закрытых членов.

Класс Circle является производным от Point с публичным наследованием. Объем Circle равен 0.0, поэтому объем функции-члена базового класса не переопределяется. Круг имеет ненулевую площадь, поэтому функция площади в этом классе переопределяется. Напишите get и установите функции для возврата и назначения нового радиуса для круга.

Класс Cylinder является производным от Circle с публичным наследованием. Цилиндр имеет площадь и объем, отличные от таковых в Круге, поэтому функции площади и объема в этом классе переопределяются. Напишите get и установите функции для возврата высоты и назначения новой высоты.

Создайте одну точку, один круг и один цилиндр, затем напечатайте результаты.

//
//  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.

1 ответ

На случай, если вы еще не нашли ответ, cptr->print(5,pi); звонки print с 2 параметрами. Circle имеет 2 параметра printCylinder имеет 3 параметра print, поскольку Cylinder наследует 2 параметра print от Circle, Cylinder печатается так, как будто это Circle,

Комментарий JakeFreeman может оказаться бесполезным, но он абсолютно прав. Давайте попробуем и сделаем это полезным, не повторяя пару глав вашего учебника.

Вы столкнулись с двумя основными концепциями ОО: инкапсуляция и полиморфизм.

Инкапсуляция является основной проблемой, и решение этой проблемы решает проблему полиморфизма.

Cylinder объект должен представлять один и только один цилиндр. Он должен иметь свой радиус и высоту, хранящиеся в нем. Вызов функции и передача радиуса и высоты идеологически неверны. Когда вы выполняете метод на Cylinder, он должен уже знать его радиус и высоту. volume Метод не должен принимать никаких параметров, потому что все, что нужно для вычисления объема, уже должно быть известно. Это позволяет любой форме иметь одинаковый volume интерфейс, даже если метод поддержки интерфейса, вероятно, будет отличаться для каждой фигуры.

Точно такая же логика применима к области.

Далее, не все фигуры имеют радиус, поэтому Shape ничего не должен знать о радиусе. Или высота. Или глубина. Может быть, форма знает точку привязки, но это все, что должна знать форма.

также print не требует никаких параметров, за исключением, возможно, ссылки на поток ввода-вывода, который вы хотите распечатать, поскольку потоки ввода-вывода - это не то, что обычно ожидалось бы, чтобы цилиндр знал или заботился о том, чтобы его было достаточно.

Один ворчание: зачем вообще обходить пи? Если у вас есть система, в которой число 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. Квадрат - это специализированный прямоугольник. Прямоугольник - это специализированная форма. Но это не главное.

Я рекомендую группировку setX а также setY в 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, вычисление объема с использованием Circle"s area метод и height, свой area метод для вычисления его площади поверхности, и другой метод печати, который печатает его статистику

Приложение: если ваш компилятор поддерживает его, и к этому времени должно это сделать, воспользуйтесь override ключевое слово. Если компилятор находит метод, помеченный как override, и ничего не переопределяет, вы получите хорошее чистое сообщение об ошибке, а не кучу отладок.

Другие вопросы по тегам