04 图形计算器
04 图形计算器
作者:abnb250/山中鹤鸣
博客:https://www.cnblogs.com/abnb250
交流群:1062464078
点击返回目录
前情提要
这一节用抽象基类 Shape 加 Circle/Rectangle/Square/Triangle 四个派生类,
配合 std::vector<std::unique_ptr<Shape>> 演示多态、RAII 与菜单式交互,另有面积/周长累加、按索引删除等功能。
注释版盯住四处:
Shape.h 的 include guard 宏名与 #ifndef 不匹配,会重复定义;
readdouble 里变量必须声明为 double 而不是 int,否则小数会被截断或拒绝;
showmain 的 cin >> choice 会留下换行,create 里的 getline 会读到空行直接返回;
Triangle 构造里的排序是"疑似反直觉",需确认是否有调用方依赖 a<=b<=c。
代码(加注释版)
注释解释了两种场景:
- 代码有反直觉痕迹,自然写法就在旁边。
- 代码看起来自然,但背后有隐形决策。
Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include "Shape.h"
class Rectangle : public Shape{
private:
double l;
double s;
public:
Rectangle(): l(2.0), s(1.0) {}
Rectangle(double l_, double s_) : l(l_), s(s_) {}
~Rectangle() {}
double area() const override;
double perimeter() const override;
string name() const override;
double get_l() const ;
double get_s() const;
};
#endif
Rectangle.cpp
#include "Rectangle.h"
double Rectangle::area() const {
return l*s;
}
double Rectangle::perimeter() const {
return 2*(l + s);
}
string Rectangle::name() const {
return "Rectangle";
}
double Rectangle::get_l() const{
return l;
}
double Rectangle::get_s() const{
return s;
}
Shape.h
#ifndef SHAPE_H
// 不能把宏名写成 SHAPEH:与 #ifndef SHAPE_H 不匹配,重复包含时 Shape 会被重复定义
#define SHAPEH
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
using std::string;
class Shape{
protected:
public:
Shape() {}
virtual ~Shape(){}
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual string name() const = 0;
void print() const;
};
#endif
Shape.cpp
#include "Shape.h"
#include <iomanip>
void Shape::print() const {
std::cout << "name: " << name() << '\n';
std::cout << std::fixed << std::setprecision(2) << "area: " << area() << '\n';
std::cout << std::fixed << std::setprecision(2) << "perimeter: " << perimeter() << '\n';
}
Square.h
#ifndef SQUARE_H
#define SQUARE_H
#include "Shape.h"
class Square : public Shape{
private:
double b;
public:
Square(): b(1.0) {}
Square(double b_) : b(b_) {}
~Square() {}
double area() const override;
double perimeter() const override;
string name() const override;
double get_b() const ;
};
#endif
Square.cpp
#include "Square.h"
double Square::area() const {
return b*b;
}
double Square::perimeter() const {
return 4*b;
}
string Square::name() const {
return "Square";
}
double Square::get_b() const{
return b;
}
Triangle.h
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "Shape.h"
#include <algorithm>
class Triangle : public Shape{
private:
double a;
double b;
double c;
public:
Triangle(): a(5.0) , b(4.0) , c(3.0) {}
Triangle(double a_, double b_, double c_);
~Triangle() {}
double area() const override;
double perimeter() const override;
string name() const override;
double get_a() const ;
double get_b() const;
double get_c() const;
};
#endif
Triangle.cpp
#include "Triangle.h"
#include <algorithm>
Triangle::Triangle(double a_, double b_, double c_){
double tmp[3] = {a_, b_, c_};
// 疑似反直觉:海伦公式不要求排序;去掉后 area/perimeter 等价,但 get_a/get_b/get_c 返回输入顺序,是否有调用方依赖 a<=b<=c 需验证
std::sort(tmp, tmp + 3);
a = tmp[0];
b = tmp[1];
c = tmp[2];
}
double Triangle::area() const {
double p = perimeter()/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
double Triangle::perimeter() const {
return a+b+c;
}
string Triangle::name() const {
return "Triangle";
}
double Triangle::get_a() const{
return a;
}
double Triangle::get_b() const{
return b;
}
double Triangle::get_c() const{
return c;
}
Circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
#include "Shape.h"
class Circle : public Shape{
private:
double r;
public:
Circle(): Shape(), r(1.0) {}
Circle(double r_) : Shape(), r(r_) {}
~Circle() {}
double area() const override;
double perimeter() const override;
string name() const override;
double get_r() const ;
double get_d() const;
};
#endif
Circle.cpp
#include "Circle.h"
double Circle::area() const {
return 3.14159 * r * r;
}
double Circle::perimeter() const {
return 2*3.14159*r;
}
string Circle::name() const {
return "Circle";
}
double Circle::get_r() const{
return r;
}
double Circle::get_d() const{
return r*2;
}
main.cpp
#include "Shape.h"
#include "Circle.h"
#include "Rectangle.h"
#include "Square.h"
#include "Triangle.h"
#include <vector>
#include <memory>
#include <iomanip>
#include <string>
#include <sstream>
using std::cout;
using std::cin;
// 创建图形、列出图形、计算总面积、计算总周长、删除图形和退出
std::vector<std::unique_ptr<Shape>> v;
bool ok(double a, double b, double c){
if(a + b > c && a + c > b && b + c > a) return true;
return false;
}
bool readint(int n, std::vector<int>& vs){
string line;
if(!getline(cin, line)) return false;
std::stringstream ss(line);
for(int i = 0; i < n; i++){
int x;
if(!(ss >> x)) return false;
vs.push_back(x);
}
char extra;
if(ss >> extra) return false;
return true;
}
bool readdouble(int n, std::vector<double>& vs){
string line;
if(!getline(cin, line)) return false;
std::stringstream ss(line);
for(int i = 0; i < n; i++){
// 不能声明为 int:输入 2.5 时只读到 2,随后 ss>>extra 读到 '.' 返回 false,小数无法创建
int x;
if(!(ss >> x)) return false;
vs.push_back(x);
}
char extra;
if(ss >> extra) return false;
return true;
}
void create() {
cout << "====================" << '\n';
cout << "[1] circle" << '\n';
cout << "[2] rectangle" << '\n';
cout << "[3] square" << '\n';
cout << "[4] triangle" << '\n';
cout << "[0] quit" << '\n';
cout << '\n';
cout << "please cin" << '\n';
cout << "====================" << '\n';
int choice;
std::vector<int> tmp;
// readint 用 getline,而 showmain 的 cin>>choice 留下换行,首次 getline 会读到空行直接 return;需先 cin.ignore()
if(!readint(1, tmp) || tmp.size() < 1) return;
choice = tmp[0];
switch(choice){
case 1: {
double r;
std::vector<double> tm;
if(!readdouble(1, tm) || tm.size() < 1) {cout << "please use number" << '\n'; break;}
r = tm[0];
if(r <= 0){
cout << "error" << '\n';
break;
}
std::unique_ptr<Shape> tmp = std::make_unique<Circle>(r);
v.push_back(std::move(tmp));
}break;
case 2:{
double l, s;
std::vector<double> tm;
if(!readdouble(2, tm) || tm.size() < 2) {cout << "please use number" << '\n'; break;}
l = tm[0];
s = tm[1];
if(l <= 0 || s <= 0){
cout << "error" << '\n';
break;
}
std::unique_ptr<Shape> tmp = std::make_unique<Rectangle>(l, s);
v.push_back(std::move(tmp));
}break;
case 3:{
double b;
std::vector<double> tm;
if(!readdouble(1, tm) || tm.size() < 1) {cout << "please use number" << '\n'; break;}
b = tm[0];
if(b <= 0){
cout << "error" << '\n';
break;
}
std::unique_ptr<Shape> tmp = std::make_unique<Square>(b);
v.push_back(std::move(tmp));
}break;
case 4:{
double a, b, c;
std::vector<double> tm;
if(!readdouble(3, tm) || tm.size() < 3) {cout << "please use number" << '\n'; break;}
a = tm[0];
b = tm[1];
c = tm[2];
if(a <= 0 || b <= 0 || c <= 0 || !ok(a,b,c)){
cout << "error" << '\n';
break;
}
std::unique_ptr<Shape> tmp = std::make_unique<Triangle>(a, b, c);
v.push_back(std::move(tmp));
}break;
default: {
cout << "please use other numbers" << '\n';
}
case 0: break;
}
return;
}
void list() {
if(v.size() == 0){
cout << "empty" << '\n';
return;
}
int index = 0;
for(auto& x : v){
cout << index++ << '\n';
x->print();
}
return;
}
void getarrea() {
if(v.size() == 0){
cout << "empty" << '\n';
return;
}
double res = 0.0;
for(auto& x : v){
res += x->area();
}
cout << std::fixed << std::setprecision(2) << res << '\n';
return;
}
void getperimeter() {
if(v.size() == 0){
cout << "empty" << '\n';
return;
}
double res = 0.0;
for(auto& x : v){
res += x->perimeter();
}
cout << std::fixed << std::setprecision(2) << res << '\n';
return;
}
void deletes() {
int index;
cin >> index;
if(v.size() == 0){
cout << "empty" << '\n';
return;
}
if(index >= v.size() || index < 0){
cout << "out range" << '\n';
return;
}
v.erase(v.begin() + index);
return;
}
void quit() {
cout << "byebye" << '\n';
return;
}
void showmain(){
while(1){
cout << "====================" << '\n';
cout << "[1] create a shape" << '\n';
cout << "[2] list the shapes" << '\n';
cout << "[3] get all area" << '\n';
cout << "[4] get all perimeter" << '\n';
cout << "[5] delete a shape" << '\n';
cout << "[0] quit" << '\n';
cout << '\n';
cout << "please cin" << '\n';
cout << "====================" << '\n';
int choice;
cin >> choice;
switch(choice){
case 1:create(); break;
case 2:list();break;
case 3:getarrea();break;
case 4:getperimeter();break;
case 5:deletes();break;
case 0:quit();break;
default:
cout << "quit" << '\n';
break;
}
if(choice == 0) break;
}
}
int main(){
using namespace std;
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
showmain();
return 0;
}
/*
我要这天再遮不住我眼,我要这地再埋不住我心。
交流群:1062464078
*/

浙公网安备 33010602011771号