谁能帮我解决这个问题,我给他100分!!!

WilliamJ 2005-12-26 03:16:10
假如有一个基类:Shape 表示形状,而有两个派生类:Box 和Sphere 分别表示 盒子和球体,他们各自有各自的成员变量,比如Box 有 长,宽,高,而 球体Sphere 有半径。
现在有一个数组:Vector<Shape *> v1 用来储存基类的指针,然后指向相应的派生类的指针,比如v1[0] = new Box();v1[1] = new Sphere();等等。
有一个棘手的问题,就是,由于长宽高 和 半径是不同的量,比如长宽高用数组,而半径用一个float就可以储存,这就需要使用返回不同值的函数,一个返回数组的函数在类Box中,而另一个返回float的函数在Sphere中,这里就存在一个大问题了:由于Vector中存的是Shape基类的指针,以上的两个函数都不在Shape基类中声名过,虽然知道了每一个Vector元素是Box,还是Sphere,但我在这无法调用这两个相应的函数!!!因为我还没得到 Box 或 Sphere 的指针,而使用RTTI就可以强制将基类指针转换为相应的派生类的指针,进而调用相应函数返回相应地值。但我不想使用RTTI,因为这样付出的效率代价太大了!我想这可能是设计问题,那位大侠再帮我看看。
...全文
432 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
cky41 2005-12-27
  • 打赏
  • 举报
回复
>>回:csk41兄,你的程序编译不过除去一些小错误不谈,主要就是提示,get_lwh 函数和 get_radius函数无法解析.............怎么回事?不明白....

我那个只是个示例,并不是真正的程序。至于那两个函数get_lwh和get_radius只是模拟的你程序中的需要,他们只是个占位符。你难道没看见get_lwh和get_radius这两个函数,没有返回类型,没有参数吗?如果你要用这个示例程序,那么需要用你所用到的函数来代替。
sinall 2005-12-27
  • 打赏
  • 举报
回复
cky41兄,请问你所说的强制转换是这样吗:(Box*)v1[0]...,但我试验了一下好像不行。
——————————————————————————————————————————
cky41的方法可以,但是这种方法本质上也是RTTI!

我来几个数据结构:
typedef vector< Shape* > vec_shape; // 某一类Shape,比如Box。
enum ShapeType
{
box;
sphere;
};
typedef map< ShapeType, vec_shape > vec_shape_array; // 所有各类Shape的集合。

同样,这个方法无法保证,基类指针指向的到底是哪个派生类对象。
所以,实际上,
1)可以人为地做类型转换,但不能保证把Box的基类指针赋给你认为是Sphere的基类指针。
2)RTTI,或者变相的RTTI,如cky41的方法。
WilliamJ 2005-12-27
  • 打赏
  • 举报
回复
看来到目前为止,cky41兄的方法最好,但不知有没有模板高手能用模板解决一下这个问题。
sinall 2005-12-27
  • 打赏
  • 举报
回复
get_lwh和get_radius无法解析。
——————————————————————————————————————————
下面的程序gcc下编译没有问题。问题是你没有写get_lwh和get_radius的实现!

#include <vector>
using namespace std;
typedef enum
{
box = 0,
sphere
}SHAPE;

struct Box_LWH
{
float fL,fW,fH;
};
class Shape
{
public:
SHAPE _type;
};

class Box : public Shape
{
public:
Box(){_type = box;}
Box_LWH get_lwh ()
{
return Box_LWH();
}
};

class Sphere : public Shape
{
public:
Sphere() {_type = sphere;}
float get_radius ()
{
return 0;
}
};

int main ()
{
vector<Shape*> v;
Shape* s1 = new Box;
Shape* s2 = new Sphere;
v.push_back (s1);
v.push_back (s2);
Box_LWH b1;
float r1;
// get length, width, height
for (int i=0; i<v.size (); ++i)
{
if (v[i]->_type == box)
b1 = ((Box*) v[i])->get_lwh ();
else if (v[i]->_type == sphere)
r1 = ((Sphere*) v[i])->get_radius ();
}
return 0;
}
ietj 2005-12-27
  • 打赏
  • 举报
回复
长/宽/高与半径是各个具体Shape的特性,有提取为接口的必要吗?

如果实在想得到的话,可以使用
virtual int GetType() const=0;
virtual std::vector<float> GetPropertys() const=0;
WilliamJ 2005-12-27
  • 打赏
  • 举报
回复
回:csk41兄,你的程序编译不过除去一些小错误不谈,主要就是提示,get_lwh 函数和 get_radius函数无法解析.............怎么回事?不明白....
WilliamJ 2005-12-27
  • 打赏
  • 举报
回复
我那个只是个示例,并不是真正的程序。至于那两个函数get_lwh和get_radius只是模拟的你程序中的需要,他们只是个占位符。你难道没看见get_lwh和get_radius这两个函数,没有返回类型,没有参数吗?如果你要用这个示例程序,那么需要用你所用到的函数来代替。
------------------------------------------------------------------------------------
这一点,我相信谁都明白,我是说帮您把程序改成了以下这样,除去无关紧要的问题,最重要的是get_lwh和get_radius无法解析。

#include <vector>
using namespace std;
typedef enum
{
box = 0,
sphere
}SHAPE;

struct Box_LWH
{
float fL,fW,fH;
};
class Shape
{
public:
SHAPE _type;
};

class Box : public Shape
{
public:
Box(){_type = box;}
Box_LWH get_lwh ();
};

class Sphere : public Shape
{
public:
Sphere() {_type = sphere;}
float get_radius ();
};

int main ()
{
vector<Shape*> v;
Shape* s1 = new Box;
Shape* s2 = new Sphere;
v.push_back (s1);
v.push_back (s2);
Box_LWH b1;
float r1;
// get length, width, height
for (int i=0; i<v.size (); ++i)
{
if (v[i]->_type == box)
b1 = ((Box*) v[i])->get_lwh ();
else if (v[i]->_type == sphere)
r1 = ((Sphere*) v[i])->get_radius ();
}
return 0;
}
xlsue 2005-12-26
  • 打赏
  • 举报
回复
sorry,搞错了:)
xlsue 2005-12-26
  • 打赏
  • 举报
回复
如果虚拟函数和转型都不符合你的需要。不如用仿真虚拟函数表格的方法试试?
guyanhun 2005-12-26
  • 打赏
  • 举报
回复
觉得用 抽象类 好。
xlsue 2005-12-26
  • 打赏
  • 举报
回复
同意 goodluckyxl、whyglinux的。把接口做成虚的,虽然很多时候我们都避免在继承体系中做向下转型,但偶尔还是必须dynamic_cast来转型。
cky41 2005-12-26
  • 打赏
  • 举报
回复
比如这样:
typedef enum
{
box;
sphere;
}SHAPE;

class Shape
{
public:
SHAPE _type;
};

class Box : public Shape
{
public:
Box(){_type = box;}
get_lwh ();
}

class Sphere : public Shape
{
public:
Sphere() {_type = sphere;}
get_radius ()
}

int main ()
{
vector<Shape*> v;
Shape* s1 = new Box;
Shape* s2 = new Sphere;
v.push_back (s1);
v.push_back (s2);

// get length, width, height
for (int i=0; i<v.size (); ++i)
{
if (v[i]._type == box)
((Box*) v[i])->get_lwh ();
else if (v[i]._type == sphere)
((Sphere*) v[i])->get_radius ();
}
return 0;
}
WilliamJ 2005-12-26
  • 打赏
  • 举报
回复
回:sinall兄,你使用的就是RTTI.................
whyglinux兄,你的方法不错,但这样太危险,容易导致各种各样的指针问题。
piaochen兄,你的方法我很早就想过了,但有一个严重的问题,我在这里举例的只是两个形状box和sphere,但未来可能会定义几百个形状,那么使用这个方法就太恐怖了..........
cky41兄,请问你所说的强制转换是这样吗:(Box*)v1[0]...,但我试验了一下好像不行。
snailbreak兄,你很幽默........我要知道长、宽、高的和值干什么........
snailbreak 2005-12-26
  • 打赏
  • 举报
回复
//上面的漏了两个const

#include <iostream>
#include <vector>

using namespace std;

class Shape{
public:
virtual double Area() = 0;
};

class Box:public Shape{
public:
Box(double a,double b,double c):len(a),wid(b),hei(c){ }

double Area()
{
return 2*len*wid+2*len*hei+2*wid*hei;
}

private:
double len,wid,hei;
};

class Sphere:public Shape{
public:
Sphere(double r):radius(r){ }

double Area()
{
return 4*PI*radius*radius;
}
private:
double radius;
static double const PI;//这里一个
};

double const Sphere::PI = 3.141592654;//这里一个


int main(int argc,char** argv)
{
vector<Shape *> shape;

Shape* b = new Box(1.0,2.0,3.0);
Shape* s = new Sphere(4.0);

shape.push_back(b);
shape.push_back(s);

cout<<shape[0]->Area()<<endl;
cout<<shape[1]->Area()<<endl;

return 0;
}
snailbreak 2005-12-26
  • 打赏
  • 举报
回复
#include <iostream>
#include <vector>

using namespace std;

class Shape{
public:
virtual double Area() = 0;
};

class Box:public Shape{
public:
Box(double a,double b,double c):len(a),wid(b),hei(c){ }

double Area()
{
return 2*len*wid+2*len*hei+2*wid*hei;
}

private:
double len,wid,hei;
};

class Sphere:public Shape{
public:
Sphere(double r):radius(r){ }

double Area()
{
return 4*PI*radius*radius;
}
private:
double radius;
static double PI;
};

double Sphere::PI = 3.141592654;


int main(int argc,char** argv)
{
vector<Shape *> shape;

Shape* b = new Box(1.0,2.0,3.0);
Shape* s = new Sphere(4.0);

shape.push_back(b);
shape.push_back(s);

cout<<shape[0]->Area()<<endl;
cout<<shape[1]->Area()<<endl;

return 0;
}
piaochen_2002 2005-12-26
  • 打赏
  • 举报
回复
#include<iostream>
#include<vector>
using namespace std;
class Shape
{

public :

Shape(){};
virtual double GetH()=0;
virtual double GetW()=0;
virtual double GetR()=0;
virtual ~Shape(){};


};
class Box :public Shape
{
private:
double hight;
double width;
public:
virtual double GetH(){ return hight;};
virtual double GetW(){ return width;};
virtual double GetR(){ return 0;}
Box(float a=0.0,float b=0.0)
{
hight=a;
width=b;
}
virtual ~Box(){};


};
class Sphere : public Shape
{
private:

double r;
public:

Sphere(double c=0.0) { r=c;}
virtual double GetH(){return 0.0;};
virtual double GetW(){ return 0.0;};
virtual double GetR()
{
return r;
};
virtual ~Sphere(){};





};

void main()
{


Sphere a(1.1);
Box b(2.3,1.1);
vector<Shape *> v1(2);

v1[0]=(&b);

cout<<v1[0]->GetH()<<endl;
cout<<(v1[0]->GetW())<<endl;
v1[1]=(&a);
cout<<v1[1]->GetR()<<endl;



return;
}
cky41 2005-12-26
  • 打赏
  • 举报
回复
whyglinux(山青水秀)的说法很科学,不能为了方便就在基类里尤其是接口类里胡作非为。

他这个问题应该这样理解。不论返回长宽高还是返回半径,都是返回一个规则体在3个坐标上的量度。那么这就可以看作是他们的共性操作了,经后再加一个派生类应该还是有这样的需求。
那么就可以写这样一个接口函数
virtual void get3d(float& l, float& w, float& h)=0;
对于球体来说,不同的是这三个值其实是相等的。当然也可以写利用返回值的函数。


单独处理非公共结构部分的时候,除了c++提供的dynamic_cast转换(也就是rtti之外),我们可以自己为每个类标明类型。
比如可以在基类中加入一个成员变量 _type
在box的构造函数中加入
_type = Box; // _type可能是字符串,也可能是一个枚举类型,自己定义。
在Sphere的构造函数中加入
_type = Sphere;

这样在调用非公共函数前先判断这个值,在将基类指针强制转换成相应指针就可以了。


piaochen_2002 2005-12-26
  • 打赏
  • 举报
回复
class Shape
{

public :
Shape(){};

virtual float GetH()=0;
virtual float GetW()=0;
virtual float GetR()=0;
virtual ~Shape(){};

};
可以把Shape定义成这样的结构,在Box 和Sphere里面分别实现.
sinall 2005-12-26
  • 打赏
  • 举报
回复
虽然知道了每一个Vector元素是Box,还是Sphere,但我在这无法调用这两个相应的函数
——————————————————————————————————————————
为什么不可以?
在MSDN搜索“dynamic_cast”。

class B { ... };
class D : public B { ... };

void f()
{
B* pb = new D; // unclear but ok
B* pb2 = new B;

D* pd = dynamic_cast<D*>(pb); // ok: pb actually points to a D
...
D* pd2 = dynamic_cast<D*>(pb2); //error: pb2 points to a B, not a D
// pd2 == NULL
...
}
whyglinux 2005-12-26
  • 打赏
  • 举报
回复
把表示 Box 和 Sphere 的 Shape 指针放到一起(vector)的目的是对它们的共性——公共接口(定义在Shape中的界面)进行操作。这样,即使以后再增加了一个Shape的派生类,比如Triangle,这部分代码也不会受到影响,不需要修改。

如果派生类的操作是非公共接口,则需要单独处理,或者设法将其列为公共接口。比如对于你的程序,可以将返回值类型设置为字符串,然后在根据类型的不同转换为对应的数据;或者返回一个共用体类型等。但是这样做的代价比动态类型转换的代价可能还要高得多,所以对于非共性操作强行做成公共接口一般不是好的选择。

单独处理非公共结构部分的时候需要获取相应类型的指针,又有两种情况:其一,通过dynamic_cast转换;其二,事先保存原来对象的指针。如果不想使用动态类型转换的话,可以把相同类型的对象的指针组织在一起使用。不过,这样的话一个对象就有两个或者以上的指针指向它,特别是在删除对象的时候要注意更新各个列表。
加载更多回复(5)

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧