c++小项目
题目
C++面向对象综合实践:图形类的多态实现与文件操作
本项目通过实现一个图形(Shape)类及其派生类(圆形Circle、矩形Rectangle),综合运用C++面向对象编程的核心特性,包括:
- 抽象类与纯虚函数:定义抽象基类Shape,包含计算面积和显示信息的纯虚函数。
- 继承与多态:通过虚继承实现圆形和矩形类,并重写基类虚函数。
- 友元函数:实现计算图形总面积的功能,演示友元函数对保护成员的访问。
- 静态成员:使用静态变量统计当前存在的图形对象数量。
- 文件操作:将图形信息写入文件,并从文件中读取并显示。
- 动态内存管理:在堆上创建对象数组,正确使用new/delete和new\[\]/delete\[\]。
- 类型转换:使用dynamic_cast进行安全的向下类型转换。
通过这个项目,可以深入理解C++面向对象设计、多态机制、内存管理以及文件I/O操作的综合应用。
代码实现
javascript
#include<iostream>
#include<string>
#include<fstream>
#define pi 3.1415
using namespace std;
class shape {
public:
virtual double getarea() = 0;
virtual void show() = 0;
shape(string n) {
name = n;
count++;
}
virtual ~shape() {
count--;
};
friend void printtotalarea(shape* arr[], int n);
//这里传入函数的形参,类型 数组[]=类型* 数组
//所以shape* arr[]=shape**arr
//C++传参规则,将数组名传给参数后,会自动退化为首元素地址
//数组里的每个元素都是shape*,首元素地址就是shape**
//传进函数等价于shape**
static int count;
protected:
string name;
double area=0.0;
};
int shape::count = 0;
class circle :virtual public shape {
private:
double radius;
public:
circle(string n,double r):shape(n) {
radius = r;
}
double getarea() {
this->area = pi * radius * radius;
return this->area;
}
void show() {
cout << "图形"<< name << endl;
cout << "面积" << getarea() << endl;
cout << "半径" << radius << endl;
}
double getR() {
return radius;
}
};
class rectangle :virtual public shape {
private:
double width;
double height;
public:
rectangle(string name,double w, double h):shape(name) {
width=w;
height=h;
}
double getarea() {
this-> area = width * height;
return this->area;
}
void show() {
cout << "图形" <<name<< endl;
cout << "面积" << getarea() << endl;
}
double getW() {
return width;
}
double getH() {
return height;
}
};
void printtotalarea(shape* arr[], int n) {
double total = 0.0;
for (int i = 0; i < n; i++) {
arr[i]->getarea();
total += arr[i]->area;//友元函数调用shape里面的保护数据area
//也可以写成 total+=arr[i]->getarea();
}
cout << "总面积是" << total << endl;
}
void writeshapefile(shape* arr[], int n) {
ofstream ofs;
ofs.open("test.txt", ios::out);
if (!ofs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
for (int i = 0; i < n; i++) {
circle* c = dynamic_cast<circle*>(arr[i]);
if (c != NULL) {
ofs << "circle"
<< "半径" << c->getR()
<< "面积" << c->getarea() << endl;
}
else {
rectangle* r = dynamic_cast<rectangle*>(arr[i]);
ofs << "rectangle"
<< "长" << r->getH()
<< "宽" << r->getW()
<< "面积" << r->getarea();
}
}
ofs.close();
cout << "数据以写进文件test.txt" << endl;
}
void readshapefile() {
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
string type;
while (ifs>>type) {
if (type == "circle") {
double r;//需要再定义一个变量
ifs >> r;//用这个变量来存储
cout << "圆形 半径" << r << " " << "面积" << pi * r * r << endl;
}
else if (type == "rectangle") {
double w, h;
ifs >> h >> w;
cout << "长方形 长" << h << " " << "宽" << w << " " << "面积" << h * w << endl;
}
}
ifs.close();
}
int main() {
shape* c1 = new circle("圆",3);
shape* r1 = new rectangle("长方形", 3, 4);
shape* c2 = new circle("圆", 4);
shape* r2 = new rectangle("长方形", 5, 6);
//shape* arr[4] = { c1,c2,r1,r2 };
//for(int i=0;i<4;i++){
//delete arr[i];
//}这是在栈内存上面开辟
shape** arr = new shape* [4]{ c1,c2,r1,r2 };
printtotalarea(arr, 4);
for (int i = 0; i < 4; i++) {
arr[i]->show();
}
writeshapefile(arr, 4);
readshapefile();
cout << "当前图形数量" << shape::count << endl;
for (int i = 0; i < 4; i++) {
delete arr[i];//释放数组里面的每一个元素
}
delete[]arr;//释放整个数组
return 0;
}
遇到的问题
1.抽象类无法实例化
在写基类shape里面写有无省纯虚函数virtual double getarea()=0;但是派生类写的是double getarea(double r),带参数,函数签名不匹配。此时的circle没有实现纯虚函数,仍是抽象类。抽象类不能new对象
2.需要写无省构造函数的两个情景
1.定义了无省对象:circle c;
2.子类构造没有写初始化列表
3.友元函数的访问权限
友元函数不受public,protected,private的限制,可以访问protected成员,普通外部函数做不到。
4.new新数组
shape*c1=new circle("圆",3);
在堆上开辟了数组元素
我现在想让开辟的元素构成一个数组有两种方法
1.shape*arr[4]={c1,c2....}
这是在栈上创建shape*类型数组,里面存的元素都是shape*类型的
2.shape**arr=new shape*[4]{c1,c2...}
这是在堆上创建,最后记得要释放
delete []arr;
delete arr[i];//for循环
在给函数传实参时候,由于形参类型是shape*arr\[\],最后退化为数组首元素地址,也就是shape**类型。
构造,析构函数规则
1.初始化列表可以给父类构造传参
2.多态场景的基类析构必须用virtual,防止内存泄漏
2.析构不能重载,一个类只能有一个析构函数,构造可以重载
文件打开模式
ios::in读取 ios::out写入
javascript
#include<iostream>
#include<string>
#include<fstream>
#define pi 3.1415
using namespace std;
class shape {
public:
shape() {};
virtual double getarea() = 0;
virtual ~shape() {};
};
class circle :public shape {
public :
circle(double r) {
radius = r;
}
double getarea() {
return pi * radius * radius;
}
double getr() {
return radius;
}
private:
double radius;
};
class rect :public shape {
public:
rect(double w, double h) {
width = w;
height = h;
}
double getarea() {
return width * height;
}
double getw() {
return width;
}
double geth() {
return height;
}
private:
double width;
double height;
};
void writefile(circle c,rect r) {
ofstream ofs;
ofs.open("shape.txt", ios::out);
ofs << "circle" << " "<<c.getr() << endl;
ofs << "矩形" << " " << r.getw() <<" " << r.geth() << endl;//这些数据是写在记事本里面的
ofs.close();
cout << "数据已经写入文件" << endl;
}
void readfile() {
ifstream ifs;
ifs.open("shape.txt", ios::in);
if (!ifs.is_open()) {
return;
}
string type;
while (ifs >> type) {
if (type == "circle") {
double r;
ifs >> r;
cout << "圆形" << " " << "面积" << pi*r*r << endl;//这是显示到屏幕上面的
}
else {
double w;
double h;
ifs >> w >> h;
cout << "矩形" << " " << "面积" << w*h << endl;
}
}
ifs.close();
}
int main() {
circle c(3);
rect r(4, 5);
//writefile(c,r);
readfile();
return 0;
}
文件读写操作
可以直接在记事本中修改,不用用C++代码来修改