大家好!我是C ++的新手,现在正在进行C ++项目。整个结构已经完成。但我一直想知道
如何构建不同对象的向量以及如何从一开始就读取文件。
在我的问题中,首先我必须阅读一个带有readObstacles(std :: istream& fs)的txt文件,其格式如下:
RECTANGLE 1 2.5 2 2 0.2
CIRCLE 1 2.5 1.2
RECTANGLE 4 2 2 2 0.3
(每个障碍开始一条新线)
我需要读取不同障碍物的数据信息并将这些障碍物存储在载体中 Class Obstacle是具有两个子类CIRCLE和RECTANGLE的基类 我尝试将这些不同的障碍(我认为应该有他们的数据信息)放在障碍物矢量中,然后调用他们都拥有的虚拟函数。
以下是我尝试使用的代码:
vector<Obstacle> obsdata;
Myworld::readObstacles(std::istream &fs)
{
std::string shape;
double num1,num2,num3,num4,num5;
while(fs>>shape>>num1>>num2>>num3>>num4>>num5)
{
if(shape=="CIRCLE") {
CIRCLE c;
c.m_Xc=num1;
c.m_Yc=num2;
c.m_Radius=num3;
obsdata.push_back(c);
}
if(shape=="RECTANGLE") {
RECTANGLE r;
r.center_x=num1;
r.center_y=num2;
r.width=num3;
r.height=num4;
r.angle=num5;
obsdata.push_back(r);
}
}
}
MyWorld::writeMatlabDisplayCode(std::ostream &fs)
{
for( i = 0; i < obsdata.size(); i++ )
obsdata[i].writeMatlabDisplayCode(fs);
}
我知道它不起作用,但我不知道该怎么做。
任何言语都会有所帮助。谢谢!
答案 0 :(得分:2)
您遇到一个称为切片的问题:如果复制到包含基类实例的向量,则会丢失派生类中的数据。
您可以使用指向基础的指针向量:
std::vector<Obstacle*> obsdata;
RECTANGLE r = new RECTANGLE;
// set properties
obsdata.push_back(r);
// use it later, e.g.:
obsdata[0]->writeMatlabDisplayCode(fs);
// clean up when you don't need obstacles anymore:
for(std::vector<Obstacle*>::iterator it = obsdata.begin(); it != obsdata.end(); ++it)
delete *it;
您还应避免每次尝试读取5个数字。相反,您可以阅读障碍物描述,然后阅读与特定障碍物需要的数量相同的数字。
答案 1 :(得分:1)
如果你有一个指针向量,你可以这样做:
vector<Obstacle *> obsdata;
然后你“新”你的后续CIRCLE和RECTANGLE:
if(shape=="CIRCLE") {
CIRCLE *c = new CIRCLE;
c->m_Xc=num1;
c->m_Yc=num2;
c->m_Radius=num3;
obsdata.push_back(c);
}
等。
答案 2 :(得分:1)
您可以尝试一次只读取整行,然后将值标记化......
vector<Obstacle*> obsdata;
string line;
while(getline(fs, line)) {
char *token = strtok(line.c_str(), " ");
string shape(token);
vector<double> numbers;
stringstream ss;
while(token = strtok(NULL, " ")) {
double d;
ss << token;
ss >> d;
numbers.push_back(d);
}
if(shape == "CIRCLE") {
CIRCLE *c = new CIRCLE();
c->m_Xc=numbers[0];
c->m_Yc=numbers[1];
c->m_Radius=numbers[2];
obsdata.push_back(c);
} else if(shape == "RECTANGLE") {
RECTANGLE *r = new RECTANGLE();
r->center_x=numbers[0];
r->center_y=numbers[1];
r->width=numbers[2];
r->height=numbers[3];
r->angle=numbers[4];
obsdata.push_back(r);
}
}
我会在你的MyWorld析构函数中删除这个指针向量:
~MyWorld() {
// loop thanks to gf
for(std::vector<Obstacle*>::iterator it = obsdata.begin(); it != obsdata.end(); ++it)
delete *it;
}
答案 3 :(得分:1)
这里的所有建议都很好,但我想提出两项改进建议:
shared_ptr
,但ptr_vector
也会有效)这将使您的解决方案更加健壮(而不是泄漏内存)并且更具可扩展性(您可以删除随着障碍物数量增加而成为编译器瓶颈的“上帝”功能)。
您的代码可能类似于:
vector<shared_ptr<Obstacle> > obsdata;
Myworld::readObstacles(std::istream &fs)
{
std::string shape;
while(fs >> shape)
{
try
{
obsdata.push_back(shared_ptr<Obstacle>(m_ObstacleCreator.Create(shape, fs));
}
catch(ObstacleNotKnownException& e)
{
// Error handling here
}
}
}
唯一剩下的就是填充m_ObstacleCreator
,了解如何根据形状名称和istream创建各种对象。一个例子:
Obstacle * CreateCircleObstacle(istream &fs)
{
CIRCLE *c = new CIRCLE();
fs >> c->m_Xc >> c->m_Yc >> c->m_Radius;
}
m_ObstacleCreator.Register("CIRCLE", &CreateCircleObstacle);
您可能还想让m_ObstacleCreator
成为单身人士(对象工厂是单身人士IMO为数不多的真正用途之一)。
有关Object Factory实现细节的精彩描述,请获取Alexandrescu的Modern C ++ Design的副本,并查看第8章。
答案 4 :(得分:0)
你的while循环的条件是个问题。 Circle比矩形少2个条目。我建议你阅读形状,然后根据类型读取剩下的数据。你知道你可以读取圆形的3个数据点和矩形的5个数据点。
一些伪代码
while( fs >> shape )
{
if( shape == "CIRCLE" )
{
CIRCLE c;
fs >> c.m_Xc;
fs >> c.m_Yc;
fs >> c.m_Radius;
obsdata.push_back(c);
}
if( shape == "RECTANGLE" )
{
RECTANGLE r;
fs >> r.center_x;
fs >> r.center_y;
fs >> r.width;
fs >> r.height;
fs >> r.angle;
obsdata.push_back(r);
}
}
正如gf在评论中所建议的那样,有关标准库的优秀C ++书籍或教程会有所帮助。