c++语言基础21-图形的面积(面向对象)

21 图形的面积

题目描述

考虑一个简单的图形类层次结构,包括基类 Shape 和两个派生类 Rectangle 和 Circle。每个类都有一个用于计算面积的方法。你的任务是编写一个程序,根据输入数据创建一个图形对象,然后计算并输出其面积。
输入描述
输入包括多行,每行包含一个图形的描述。 描述的第一个单词是图形类型("rectangle"或"circle"),然后是与该图形相关的参数。 对于矩形,参数是宽度和高度,对于圆形,参数是半径。输入以单词"end"结束。
输出描述
对于每个图形描述,输出其类型和面积。使用两位小数点精度输出面积。
输入示例
rectangle 5 3
circle 2
end
输出示例
Rectangle area: 15.00
Circle area: 12.56
提示信息
长方形面积的计算 = 长 * 宽圆形面积的计算 = 3.14 * 半径 * 半径

“类”是现实世界中的实体在计算机世界中的抽象概念,类可以看作是对象的模板,它定义了对象的结构和行为方式,可以用来创建具有相同属性和行为的多个对象,而对象是“类”的实现。

类的基本用法

class 类名{
访问修饰符:// 成员变量,表示类的属性, 定义方式和变量的定义一样// 成员方法,表示类的行为, 定义方式和方法的定义一样
}; // 分号结束一个类

C++使用class定义一个类,并在类中定义成员变量和成员方法。

访问修饰符指定了成员变量和成员方法的可见性和访问权限。常用的修饰符包括 private(私有)、public(公有)和protected(受保护),

public: 被修饰的成员在类的内部、派生类(子类)的内部和类的对象外部都可以访问。

private: 被修饰的成员只能在定义该成员的类的内部访问。

protected: 被修饰的成员只能在定义该成员的类的内部以及派生类汇总访问。

比如下面的示例,public定义成员变量和成员方法的权限,后面定义了成员变量myAttribute和成员方法myMethod。

class MyClass {
public:// 成员变量int myAttribute;// 成员方法void myMethod() {// 方法实现}
};int main() {// 创建对象MyClass obj;// 访问属性obj.myAttribute = 42;// 调用方法obj.myMethod();return 0;
}

封装

封装是面向对象的三大特性之一,封装的主要目的是为了保证数据的安全性,我们假设有一个Circle类,它具有半径这个属性。

class Circle {
public:// 成员变量int radius;
};

然后我们创建一个圆对象

Circle circe; // 创建一个对象

创建对象之后,外部代码可以直接访问和修改半径,甚至将其设置为负数,这样的设计显然是不合理的。

// 外部代码没有经过验证可以直接访问和修改半径
circle.radius = 10

为了防止这些问题的发生,我们可以通过封装隐藏对象中一些不希望被外部所访问到的属性或方法,具体怎么做呢?可以分为两步:

将对象的属性名,设置为private,只能被类所访问

提供公共的get和set方法来获取和设置对象的属性

class Circle {
// 私有属性和方法
private:int radius;  // 将圆的半径设置为私有的
// 公有属性和方法
public:// setXX方法设置属性void setRadius(int r) {radius = r;}// getXXX方法获取属性int getRadius() {return radius;}
};

使用封装,我们隐藏了类的一些属性,具体的做法是使用get方法获取属性,使用set方法设置属性,如果希望属性是只读的,则可以直接去掉set方法,如果希望属性不能被外部访问,则可以直接去掉get方法。此外我们还可以在读取属性和修改属性的同时做一些其他的处理,比如如下的操作:

void setRadius(int r) {// 对输入的半径进行验证,只有半径大于0,才进行处理if (r >= 0) {radius = r;} else {cout << "半径不能为负数" << endl;}
}

构造函数

类的构造函数和之前结构体的构造函数类似,用于初始化对象的成员变量,构造函数与类同名,没有返回类型,并且在对象创建时自动调用。其基本语法包括:

函数名:与类名相同

参数列表:可以有零个或多个参数,用于在创建对象时传递初始化信息。

函数体: 用于执行构造函数的初始化逻辑。

下面我们还以Person类作为示例,包含一个默认构造函数和带参数的构造函数。

const string& personName表示对string类型对常量引用,你可以传递字符串参数,但是不能在函数中修改这个参数的值。

class Person {
private:int age;string name;
public:// 默认构造函数Person() {age = 20;name = "Tom"}// 带参数的构造函数Person(int personAge, const string& personName) {age = personAge;name = personName}
}
int main() {// 使用默认构造函数创建对象Person person1;// 使用带参数的构造函数创建对象Person person2(20, "Jerry");return 0;
}

此外,还有构造函数的成员初始化列表写法,这种写法允许在进入构造函数主体之前对类成员进行初始化,比如下面的示例。

Person(int personAge, const string& personName) : age(personAge), name(personName) {}

在上面的代码中, Person 类的构造函数接受一个 string 类型的参数 persnName和一个int类型的参数personAge,并通过成员初始化列表初始化了 成员变量。在这里,: age(personAge), name(personName) 表示将 personAge 的值赋给 age 成员变量, 将 personName 的值赋给 name , 从而完成了成员变量初始化。这种方法更常用

继承

在对象中,总有一些操作是重复的,比如说Person类具有姓名、身高、年龄等特征,并具有一些行走、吃饭、睡觉的方法,而我们要实现一个Teacher类,Teacher首先也是一个人,他也具备人的特征和方法,那我们是不是也应该用代码去实现这些特征和方法呢,这就势必会产生一些重复的代码。

因此,我们可以采用“继承”的方式使得一个类获取到其他类中的属性和方法。在定义类时,可以在类名后指定当前类的父类(超类), 子类可以直接继承父类中的所有属性和方法,从而避免编写重复性的代码,此外我们还可以对子类进行扩展。

假设,我们有一个图形类Shape, 它具有一个属性和一个方法,属性为类型,方法为求图形的面积

class Shape {
protected:string type;  // 形状类型public:// 构造函数Shape(const string& shapeType) : type(shapeType) {}// 求面积的函数double getArea() const {return 0.0;}// 获取形状类型string getType() const {return type;}
};

在上面的代码中,getArea函数使用const用来修饰,是用来表示该函数不会修改对象的状态,使用const能保证对对象的访问是安全的。

要想实现继承,我们还需要一个关于圆的类Circle,它继承自Shape类

// Circle 类,继承自 Shape
class Circle : public Shape {
private:int radius;  // 圆的半径public:// 构造函数, 调用Shape的构造函数,初始化了类型为"circle"Circle(int circleRadius) : Shape("Circle"), radius(circleRadius) {}// 重写基类的方法double calculateArea() const override{return 3.14 * radius * radius;  // 圆的面积公式}// 获取半径int getRadius() const {return radius;}
};

在这个 Circle 类的代码中,const 关键字用于几个不同的地方,每个地方都有其特定的含义和作用:

  1. 成员函数后的 const:

    • calculateArea()getRadius() 函数声明的末尾,const 表示这些函数不会修改类的任何成员变量。
    • 这意味着这些函数可以在 const 对象(不能被修改的对象)上调用。
    • 使用 const 增加了函数的灵活性,使其可以在更多的上下文中被调用,比如在处理不可变的 Circle 对象时。
  2. 构造函数中的 const:

    • Circle 构造函数的初始化列表中,const 关键字不是直接使用的,但这里有一个与 const 相关的行为。构造函数初始化列表中的 Shape("Circle") 调用是在构造 Circle 对象之前,初始化其基类 Shape 部分。尽管这里没有直接使用 const,但它涉及到如何安全地初始化 const 成员(如果 Shape 类中有 const 成员)。
  3. 返回值前的 const:

    • calculateArea() 函数中,const 没有直接用在返回值前面,但这通常是一个常见的做法。如果返回值是一个引用或指针,那么在它前面加上 const 可以防止返回的对象被修改。

在 C++ 中,正确和恰当地使用 const 关键字是一种良好的编程实践,它可以增加代码的可读性、安全性和效率。在这个特定的例子中,它主要用于确保某些成员函数不会修改对象的状态,这对于理解对象的行为和它们在程序中的作用非常重要。

在上面的示例代码中,图形类拥有shape属性和getArea、getType方法,而子类在父类这些属性和方法的基础上新增了radius属性和getRadius方法,并且在子类和父类中都有getArea这个方法,这被称为方法的重写,方法的重写需要override关键字,其意思是子类重写父类的方法,并提供自己的实现。

多态

多态常常和继承紧密相连,它允许不同的对象使用相同的接口进行操作,但在运行时表现出不同的行为。多态性使得可以使用**基类类型的指针或引用来引用派生类的对象,从而在运行时选择调用相应的派生类方法。
**
C++中实现多态性的方法是通过virtual虚函数,比如下面的示例:

class Shape {
public:virtual double calculateArea() const = 0;
};
class Circle : public Shape {
private:int radius;public:double calculateArea() const override {return 3.14 * radius * radius;}
};
class Rectangle : public Shape {
private:int width;int height;public:// 构造函数,用于初始化 width 和 heightRectangle(int w, int h) : width(w), height(h) {}// width * height 的结果是整数,但 calculateArea 方法的返回类型是 double// 为了确保结果是一个浮点数,使用 static_cast<double> 将其显式转换为 double 类型double calculateArea() const override {return static_cast<double>(width * height);}
};

这里使用virtual在父类中定义了一个虚函数,而= 0表示这是一个纯虚函数,即定义的函数在基类中没有实现,但是要求它的派生类都必须提供这个函数的实现,这种抽象的方法使得 Shape 类成为一个抽象基类,不能被实例化,只能被用作派生其他类的基类。

然后两个派生类 Circle 和 Rectangle则是重写了 calculateArea 方法,它们提供了各自的实现,有着不同的计算逻辑。

int main() {std::vector<Shape*> shapes;shapes.push_back(new Rectangle(4, 5));shapes.push_back(new Circle(3));for (const Shape* shape : shapes) {std::cout << "Area: " << shape->calculateArea() << std::endl;}return 0;
}

之后我们创建了一个容器shapes,包含不同类型的图形对象,然后循环遍历该容器并为每一个shape对象调用 calculateArea 方法,尽管方法名称相同,但实际调用的方法是根据对象的类型动态确定的,这其实就是多态的概念。

我的acm模式代码

#include <iostream>
#include <string>
#include <vector>
// 引入iomanip库文件,用于控制输出格式
#include <iomanip>class Shape {
public:virtual double CalculateArea() const = 0;virtual std::string GetType() const = 0;
};class Rectangle: public Shape {
public:Rectangle(int width, int height):width(width), height(height) {}std::string GetType() const override {return "Rectangle";}double CalculateArea() const override{return height * width;}
private:double height;double width;
};class Circle : public Shape {
public:Circle(double radiuspara):radius(radiuspara) {}std::string GetType() const override {return "Circle";}double CalculateArea() const override{return radius * radius * 3.14;}
private:double radius;std::string type;
};int main() {// 定义一个容器,容纳 shape对象std::vector<Shape*> shapes;while (true) {std::string type;// 获取输入的 type类型std::cin >> type;if (type == "end") {break;}if (type == "rectangle") {// 获取输入的宽度和高度int width, height;std::cin >> width >> height;// 新建Rectangle对象shapes.push_back(new Rectangle(width, height));} else if (type == "circle") {int radius;// 获取输入的半径std::cin >> radius;// 新建 Radius 对象shapes.push_back(new Circle(radius));}}// 输出结果,控制小数位数为两位for (const Shape* shape : shapes) {// shape对象调用同一个方法,有不同的处理逻辑std::cout << shape->GetType() << " area: " << std::fixed << std::setprecision(2) << shape->CalculateArea() << std::endl;}return 0;
}

在这段代码中,GetTypeCircle 类的一个成员函数,它被定义为覆写(override)一个基类(可能是 Shape)中的虚函数。这个函数的作用和使用 const 关键字的含义可以如下分析:

  1. 函数作用:

    • GetType 函数的目的是返回一个表示对象类型的字符串。在这个例子中,对于 Circle 类的任何实例,它返回字符串 "Circle"
    • 这样的函数通常用于多态场景,例如,当你有一个指向基类的指针或引用,而实际指向的是派生类对象时,你可以通过这个函数来了解对象的实际类型。
  2. const 关键字:

    • 在函数声明的末尾出现的 const 关键字表明这个函数是一个常量成员函数。这意味着该函数保证不会修改类的任何成员变量(也不会调用任何非常量成员函数)。
    • 这使得该函数可以在常量对象或常量引用/指针上调用。这增加了函数的使用场景,尤其是在需要保证不会修改对象状态的上下文中。
  3. override 关键字:

    • override 关键字用于显示地标示该函数是一个覆写的虚函数,即它在基类(可能是 Shape)中已经声明,并在此被重新定义。
    • 使用 override 关键字可以帮助编译器检查函数签名的正确性。如果基类中没有相应的虚函数与之匹配,编译器会报错。这有助于避免因为函数签名的微小差异(如参数类型、const修饰符等)导致的错误。

综合来看,GetType 函数是一个不改变对象状态的多态接口,其通过 constoverride 关键字在C++中得到了恰当的表述和保障。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/620589.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据StarRocks(七):数据表创建

1. 基本概念 1.1 Row & Column 一张表包括行&#xff08;Row&#xff09;和列&#xff08;Column&#xff09;。Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。 ⚫ 在默认的数据模型中&#xff0c;Column 只分为排序列和非排序列。存储引擎会按照排序列对…

RK3568笔记八: Display子系统

modetest 是由 libdrm 提供的测试程序&#xff0c;可以查询显示设备的特性&#xff0c;进行基本的显示测试&#xff0c;以及设置显示的模式。 我们可以借助该工具来学习 Linux DRM 应用编程&#xff0c;另外为了深入分析 Rockchip DRM driver&#xff0c;有必要先了解一下这个…

Hive基础知识(十三):Hive的Group by语句与Having语句

1. Group By 语句 GROUP BY 语句通常会和聚合函数一起使用&#xff0c;按照一个或者多个列队结果进行分组&#xff0c;然后对每个组执行聚合操作。 1&#xff09;案例实操&#xff1a; &#xff08;1&#xff09;计算 emp 表每个部门的平均工资 hive (default)> select de…

PHP AES 加密示例

PHP中实现AES加密的一个基本示例涉及到使用openssl_encrypt函数。这个函数允许你使用不同的加密算法&#xff0c;包括AES。下面是一个简单的示例&#xff0c;展示了如何使用AES加密一个字符串。 首先&#xff0c;你需要确定几个关键的参数&#xff1a; 数据&#xff08;Data&…

redis实现延迟任务(二)

实现思路 我们实现文章地定时发布主要是利用zset地score属性。我们可以在score里存入要发布地时间戳地值&#xff0c;然后在定时刷新任务方法里&#xff0c;通过获取本地时间与score里的时间进行对比&#xff0c;因为本地时间是在不断变大的&#xff0c;如果大于等于的话那么就…

领域驱动设计应用之WebAPI

领域驱动设计应用之WebAPI 此篇文章主要讲述领域驱动设计在WebApi中的应用&#xff0c;以及设计方式&#xff0c;这种设计的原理以及有点。 文章目录 领域驱动设计应用之WebAPI前言一、相对于传统设计模式的有点二、WebAPI对接中的使用案例业务拆分父类设计HttpResponse(返回)…

2024PMP考试新考纲-【过程领域】近期典型真题和很详细解析(5)

今天华研荟继续为您分享【过程Process领域】的新考纲下的真题&#xff0c;进一步帮助大家体会和理解新考纲下PMP的考试特点和如何应用知识来解题&#xff0c;并且举一反三&#xff0c;在两个多月的时间内&#xff0c;一次性、高等级通过2024年PMP考试。 2024年PMP考试新考纲-【…

JavaScript基础04

1 - 数组 1.1 数组的概念 数组可以把一组相关的数据一起存放&#xff0c;并提供方便的访问(获取&#xff09;方式。 数组是指一组数据的集合&#xff0c;其中的每个数据被称作元素&#xff0c;在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅…

HBase 复制、备份、迁移

行业分享 HBase金融大数据乾坤大挪移 https://www.jianshu.com/p/cb4a645dd66a HBase跨机房迁移技术分享总结 https://www.jianshu.com/p/defc787b2704 dbaplus181期&#xff1a;腾讯金融HBase跨机房迁移实战 https://m.qlchat.com/topic/details?topicId2000003847589595 ht…

神经网络的三个特征,和卷积和最大池化有什么联系

神经网络的三个特征是层次结构、权重共享和非线性激活函数。 层次结构&#xff1a;神经网络由多个层组成&#xff0c;包括输入层、隐藏层和输出层。这种层次结构使得神经网络能够逐层提取数据的特征&#xff0c;并且通过调整每一层的权重来学习数据的表征。 权重共享&#xff…

HarmonyOS的应用类型(FA vs Stage)

HarmonyOS目前提供两种应用模型 FA(Feature Ability)模型: HarmonyOS API 7开始支持的模型,已经不再主推。 Stage模型: HarmonyOS API 9开始新增的模型,是目前主推且会长期演进的模型。在该模型中,由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的…

(五)Python中第三方常用库(webbrower、pyautogui、smtplib、xlwt、xlrd、openpyxl等)

文章目录 一、库的安装方法二、pyautogui库&#xff08;模拟键盘按键、鼠标操作和GUI交互&#xff09;三、webbrower库&#xff08;操作浏览器&#xff09;四、smtplib库&#xff08;模拟发送邮件&#xff09;五、xlwt库&#xff08;操作Excel写入数据&#xff09;六、xlrd库&a…

C++学习笔记——友元、嵌套类、异常

目录 一、友元 一个使用友元的示例代码 输出结果 二、嵌套类 一个使用嵌套类的示例代码 输出结果 三、异常 一个使用异常处理的示例代码 输出结果 四、结论 五、使用它们的注意事项 上一篇文章链接&#xff1a; C中的继承和模板是非常强大和灵活的特性&#xff0c;它…

【HuggingFace Transformer库学习笔记】基础组件学习:Datasets

基础组件——Datasets datasets基本使用 导入包 from datasets import *加载数据 datasets load_dataset("madao33/new-title-chinese") datasetsDatasetDict({train: Dataset({features: [title, content],num_rows: 5850})validation: Dataset({features: [titl…

【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《图形学 | 图像解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f30c;1. 初识模式识别…

AUTOSAR从入门到精通-Autosar 中断机制(六)

目录 知识储备 安全机制的程序流监控 看门狗在autosar 架构中位置 看门狗在autosar中简单流程

VMware workstation安装debian-12.1.0虚拟机并配置网络

VMware workstation安装debian-12.1.0虚拟机并配置网络 Debian 是一个完全自由的操作系统&#xff01;Debian 有一个由普罗大众组成的社区&#xff01;该文档适用于在VMware workstation平台安装debian-12.1.0虚拟机。 1.安装准备 1.1安装平台 Windows 11 1.2软件信息 软…

Nodejs软件安装​

Nodejs软件安装​ 一、简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 官网&#xff1a;http://nodejs.cn/api/ 我们关注于 node.js 的 npm 功能&#xff0c;NPM 是随同 NodeJS 一起安装的包管理工具&#xff0c;JavaScript-NPM&#xff0c;Java-Maven&…

Redis命令 - Strings命令组常用命令

1、Set命令 SET key value [EX seconds] [PX milliseconds] [NX|XX]1.1 参数说明&#xff1a; EX seconds: 设置key的过期时间&#xff0c;单位时秒PX milliseconds: 设置key的过期时间&#xff0c;单位时毫秒NX: 只有key不存在的时候&#xff0c;才会设置key的值XX: 只有key…

花了三天的时间做了一个多功能 AI 助手

嗨&#xff01;我是团子&#xff0c;大家新年快乐呀~ 前几天看到一些好朋友在朋友圈晒自己的年度总结&#xff0c;立新年 Flag&#xff0c;看到大家一年满满的收获&#xff0c;再看看自己&#xff0c;不由得想再看看人家&#xff0c;然后再看看自己&#xff0c;然后再看看人家…