#include <iostream>
#include <cstdio>
#include <cstring>
#include <type_traits>
#include <typeinfo>
using namespace std;
class Shape{
public:
Shape() {};
Shape(const Shape& rhs) : s(rhs.s) {};
virtual Shape* getObj() {
Shape *p = new Shape(*this);
return p;
}
virtual double area() {};
virtual void printType() {cout << "Shape! " << s;};
int s = 0;
};
class Circle:public Shape{
public:
Circle() {};
Circle(const Circle& rhs) : Shape(rhs), c(rhs.c) {};
virtual Shape* getObj() {
Shape *p = new Circle(*this);
return p;
}
virtual double area() {};
virtual void printType() {cout << "Circle! " << c << " " << s;}
int c = 0;
};
class Triangle:public Shape{
public:
Triangle() {};
Triangle(const Triangle& rhs) : Shape(rhs), t(rhs.t) {};
virtual Shape* getObj() {
Shape *p = new Triangle(*this);
return p;
}
virtual double area() {};
virtual void printType() {cout << "Triangle! " << t << " " << s;}
int t = 0;
};
class Manage {
public:
Manage() : cnt_(0) {};
Manage(const Manage& rhs) : cnt_(rhs.cnt_) {
for (int i = 0; i < cnt_; i ++) {
// sp[i] = new (remove_reference<decltype(*rhs.sp[i])>::type)((*rhs.sp[i]));
sp[i] = rhs.sp[i] -> getObj();
}
}
void add(Shape* p) { sp[cnt_ ++] = p; };
void display() {
for (int i = 0; i < cnt_; i ++) {
cout << i << ": ";
sp[i] -> printType();
cout << " " << sp[i] << endl;
}
};
public:
int cnt_;
Shape* sp[101];
};
int main()
{
Manage m1;
Shape shp[30];
Circle cl[30];
Triangle tg[40];
for (int i = 0; i < 3; i ++) {
shp[i].s = i;
m1.add(&shp[i]);
}
for (int i = 0; i < 3; i ++) {
cl[i].c = i;
cl[i].s = 2 * i;
m1.add(&cl[i]);
}
for (int i = 0; i < 4; i ++) {
tg[i].t = i;
tg[i].s = 3 * i;
m1.add(&tg[i]);
}
Manage m2(m1);
m1.display();
m2.display();
}
第一次实验报告
一、 题目分析
Manage类中装满了静态类型为Shape*的指针,但是其动态类型可能是指向基类或者子类的指针,因此难点就在于如何在深拷贝时处理指针指向的类型的问题。其实并不需要知道,交给各自的类就可以了。
二、 流程分析
1. 对于基类及其子类的扩展
基类和两个子类的结构是一样的,故仅以基类举例
Shape();
Shape(const Shape& rhs);
virtual Shape* getObj();
virtual void printType()
int s = 0;
添加了默认构造,拷贝构造,一个打印当前类型和数据成员的值的函数,以及一个得到当前对象副本并返回基类指针的虚函数。
2.Manage类
public:
Manage()
Manage(const Manage& rhs)
void add(Shape* p)
void display()
public:
int cnt_;
Shape* sp[101];
添加了默认构造函数,拷贝构造函数,添加基类指针的add函数,和打印全部基类指针指向对象的动态类型及其数据成员,并打印其所在地址的display,display调用printType。
三、程序难点
1.深拷贝
唯一难点。一开始我的想法是仅仅改变Manage类中的代码,因为我认为Manage这个类作为Shape类及其派生类的用户,不应该因为自身的缘故要求类的作者去添加某些函数实现自己的拷贝功能。所以首先我想到的是RTTI(run-time type identity), 使用typeid运算符,利用typeid(*p).name 确定对象的名字。不过这样显得有些蠢,需要将这个C风格的字符串和基类及其所有子类的名字进行比较,于是被我放弃了。而后我想到了decltype关键字,可以返回表达式结果的类型,但是我并不确定它返回的是静态的类型还是动态的类型,不过我决定尝试一下是否可行。
sp[i] =
new(<decltype(*rhs.sp[i])>::type)((*rhs.sp[i]));
然而编译器报错了,我想起来对指针做解引用操作时,decltype将判定为引用类型,这和auto是不一样的,所以我又百度到了一个模版
remove_reference<>
再次尝试。
sp[i] =new(remove_reference<decltype(*rhs.sp[i])>::type)((*rhs.sp[i]));
不过打印拷贝后出来的结果全部是Shape类型。我发现我忽略了一个问题,不管decltype返回的是静态类型还是动态类型,只有指针和引用才会有动态和静态类型不一致的情况,对象的静态类型和动态类型一定是一致的,也就不可能根据decltype得到指针的动态类型的。不然还要typeid干吗呢。
当然dynami_cast就更不行了。
我确实想不出不修改Shape和它的派生类的方法了,于是便在Shape及其派生类中添加一个getObj的虚函数,不接受参数,返回Shape* ,new一个对象,使用自身作为参数进行拷贝构造,之后返回指向这个对象的指针。
我一开始还在思考:只有一个基类的指针,如果它指向派生类对象,那么就只能访问派生类的虚函数和基类成员,无法访问派生类的数据成员以及非虚的函数,那么如何拷贝数据成员呢?我当时还问了老师,老师的建议是使用一个虚函数做类似于构造函数的功能。不过后来我发现我的担心是多余的。在getObj函数中,调用了当前这个类的拷贝构造函数,而当前这个类就是虚表匹配后的类,调用其拷贝构造函数,拷贝构造函数初始化列表中调用其父类的拷贝构造,在函数体中再完成当前类的拷贝构造,就可以解决这个问题了。
四、 优点与缺点
优点:
a) 实现了深拷贝
b) 可以拷贝对象的数据成员而不仅仅只是确定了是这个类
缺点:
a) 写的比较随性,类和main全在一个cpp里,数据成员扔到了public里方便main操作