收到警告,并非所有控制路径都返回一个值

时间:2017-11-27 19:09:34

标签: c++ class compiler-warnings

我刚刚用object-orinted(cpp)编写了我的第一个迷你项目。程序似乎运行良好,但是当我编译它时,我收到以下警告: :警告C4715:'Collection :: getCircleAt':并非所有控制路径都返回值。 我不明白这个警告的原因是什么。

我想补充一些关于我的计划的话: 程序在整数xy平面中输入一组圆,并检查哪个圆包含平面中的给定点。为了那个任务,我使用了课程。 “Point”类通过其坐标表示一个点。 “Circle”类表示平面中的彩色圆。该类包含一个“Point”类型的变量,它表示圆的中心和半径。该类还包含一个名为“color”的变量,(由整数0或2表示,当0表示圆圈不包括给定的点,2表示相反),以及检查是否包含给定点的函数在一定的圈子里。 最后,我们有了“Collection”类,它代表了一个圆圈集合。 变量“count”是圆圈的数量,变量“circles”是指向集合中圆圈的指针数组。 此类包含函数getCircleAt,它返回包含该点的圆。这就是上面显示警告的功能。

Heres是我的代码:

#include <iostream>
using namespace std;
#include "point.h"
#include "circle.h"
#include "collection.h"

int main()
{
Collection g(4, 3, 2, 0);
cout << "-- before setColor(2) --" << endl;
g.print();
Point p(5, 1);
g.getCircleAt(p).setColor(2);
cout << "-- after setColor(2) --" << endl;
g.print();
return 0;
}

#ifndef POINT_H
#define POINT_H

class Point
{
public:
Point(int x, int y);
int getX() const;
int getY() const;
void setX(int x);
void setY(int y);
void print() const;
private:
int x, y;
};

#endif

#ifndef CIRCLE_H
#define CIRCLE_H
#include "point.h"

class Circle
{
public:
Circle(int x, int y, int r, int color);
int getColor() const;
void setColor(int color);
bool contains(const Point &p) const;
void print() const;
private:
const Point center;
int radius, color;
};

#endif

#ifndef COLLECTION_H
#define COLLECTION_H
#include "circle.h"

class Collection
{
public:
Collection(int radius, int width, int height, int color);
~Collection();
Circle& getCircleAt(const Point &p);
void print() const;
private:
int count;
Circle **circles;
};

#endif

#include <iostream>
using namespace std;
#include "point.h"

Point::Point(int x,int y )
{
 setX(x);
 setY(y);
}

void Point::setX(int x)
{
    this->x=x;
}

void Point::setY(int y)
{
    this->y=y;
}

int Point::getX() const 
{
    return x;
} 

int Point::getY() const 
{
    return y;
}

void Point::print() const
{
        cout <<"x="<< this->x <<"  "<<"y" << this->y ;
        cout <<"  ";

#include <iostream>
using namespace std;
#include "circle.h"

Circle::Circle(int x=0,int y=0,int r=0,int color=0):center(x, y),radius(r),color(color)
{  

}

int Circle::getColor() const
{
    return color;
}

void Circle::setColor(int color)
{
    this->color=color;
}

bool Circle::contains(const Point &p) const
{
    int distX, distY;

    distX=p.getX()-center.getX();
    distY=p.getY()-center.getY();

    if ((distX*distX + distY*distY) > (radius*radius))
        return false;
    return true;
}

void Circle::print() const
{  
    cout<<endl<<"the center of the circle is ("<<center.getX()<<" ,"<<center.getY()<<")"<<" radius "<<radius<<" color "<<color<<endl;  
}


#include <iostream>
using namespace std;
#include "collection.h"

Collection::Collection(int radius, int width, int height, int color)   
{
  int i ,j; 

  count=height*width;
  circles=new Circle* [count];

  for(i=0;i<height;i++)
   for(j=0;j<width;j++)
       circles[j+(i*width)]=new Circle (j*2*radius,i*2*radius,radius,color);

}

Collection::~Collection()
{
    delete []circles; 
}

Circle& Collection::getCircleAt(const Point &p)
{
    for(int i=0;i<count;i++)     
        if(circles[i]->contains(p) ) 
          return *(circles)[i];
}

void Collection::print() const 
{
    for (int i=0;i<count;i++)
       circles[i]->print();

}

预期输出为:

-- before setColor(2) --
Circle center=(0,0) radius=4 color=0
Circle center=(8,0) radius=4 color=0
Circle center=(16,0) radius=4 color=0
Circle center=(0,8) radius=4 color=0
Circle center=(8,8) radius=4 color=0
Circle center=(16,8) radius=4 color=0
-- after setColor(2) --
Circle center=(0,0) radius=4 color=0
Circle center=(8,0) radius=4 color=2
Circle center=(16,0) radius=4 color=0
Circle center=(0,8) radius=4 color=0
Circle center=(8,8) radius=4 color=0
Circle center=(16,8) radius=4 color=0

2 个答案:

答案 0 :(得分:2)

在此代码中:

Circle& Collection::getCircleAt(const Point &p)
{
    for (int i = 0; i < count; i++)
        if (circles[i]->contains(p))
          return *(circles)[i];
}

当没有圆圈包含p时,不执行任何返回语句。这意味着当发生这种情况时,函数的返回值是未定义的。

有几种方法可以解决这个问题。最简单的方法是返回指针而不是引用,并在没有找到圆时返回nullptr

Circle* Collection::getCircleAt(const Point &p)
{
    for (int i = 0; i < count; i++)     
        if (circles[i]->contains(p)) 
          return circles[i];
    return nullptr;
}

这里建议的另一个解决方案是抛出异常。但是,我不建议这样做,因为例外情况除外。 getCircleAt()在某个给定位置找不到圆圈没有什么特别之处。这是正常的事情。这不是错误。

更好的解决方案(在我看来)是完全更改API,而是返回圆的索引,而不是圆本身。找不到圆圈时,返回-1:

int Collection::getIndexAt(const Point &p)
{
    for (int i = 0; i < count; i++)     
        if (circles[i]->contains(p)) 
          return i;
    return -1;
}

然后添加operator []重载以通过索引访问圈子:

class Collection
{
public:
    // ...
    Circle& operator [](size_t index)
    {
        return *circles[i];
    }
};

然后函数的调用者需要检查是否找到了圆圈:

auto index = g.getIndexAt(p);
if (index >= 0) {
    g[index].setColor(2);
    cout << "-- after setColor(2) --" << endl;
    g.print();
} else {
    cout << "-- circle not found at position --" << endl;
}

作为旁注,您的Collection析构函数不正确。你没有释放你分配的圈子。您只能释放动态数组。你需要:

Collection::~Collection()
{
    // Free each circle.
    for (int i = 0; i < count; ++i)
        delete circles[i];
    // Free the array.
    delete[] circles;
}

代码中的另一个(无关的)问题是,您在构造函数的实现中为Circle::Circle()构造函数提供默认参数值,而不是在其声明中。你应该改变它。应在声明中指定默认参数。所以这样做:

class Circle {
public:
    Circle(int x = 0, int y = 0, int r = 0, int color = 0);

并从实现中删除默认参数值:

Circle::Circle(int x, int y, int r, int color)

此外,除非这是内存分配和指针练习,否则您应切换到std::vector<Circle>,而不是使用newdelete的指针。手动内存管理容易出错。例如,你在上面的Collection析构函数中弄错了并且泄漏了内存。使用像vector这样的标准容器将为您处理内存管理。一个好的经验法则是永远不要使用newdelete,除非你真的需要。

答案 1 :(得分:1)

在所有情况下,

Collection::getCircleAt都不会返回。这是未定义的行为,即使未触发错误,也可能导致一些非常奇怪的行为。具有非void返回类型的函数必须在每个路径返回离子。这在评论中被打死了。

没有涵盖的是如何修复它。

选项1:抛出异常

Circle& Collection::getCircleAt(const Point &p)
{
    for(int i=0;i<count;i++)     
        if(circles[i]->contains(p) ) 
          return *(circles)[i];

    throw std::runtime_error("No circle at P")
}

如果p经常与圈子无关,则会造成严重的性能损失。因为它不是例外的行为。您只想对异常行为使用异常。如果在Circle处应该有一个p并且在没有特殊报告的情况下这是一个罕见且不寻常的事件,那么我们就是例外。

g.getCircleAt(p).setColor(2);
如果没有Circle

将完全失败,这表明这是一个不错的选择。您可以在此处捕获并处理异常,将其留给调用堆栈中的另一个函数来处理(在提问者的示例中没有任何内容)或让程序崩溃并收集捆绑的消息看看原因的例外。有关详细信息,请阅读文本有关异常处理的部分。

如果Circle处的p不是常规预期事件,则异常可能是错误的工具。继续选择2

选项2:返回金丝雀值

Circle* Collection::getCircleAt(const Point &p)
{
    for(int i=0;i<count;i++)     
        if(circles[i]->contains(p) ) 
          return (circles)[i];

    return nullptr;
}

请注意,返回类型已更改为指针,因此返回null是合法的。即使编译器允许,也不要将自己置于返回空引用的位置。空引用是一个令人讨厌的惊喜,没有人应该处理。指针至少会给你一些警告,告诉你可以使用null,所以请注意:

g.getCircleAt(p)->setColor(2);

刚取消引用并访问空指针。 KABOOM。如果程序没有崩溃,并且它没有必要,程序就会疯狂。你需要做更多。例如,

Circle * c = g.getCircleAt(p)
if (c != nullptr)
{
    c->setColor(2);
}
else
{
    // do error handling
}

你现在程序员必须自己完成所有的错误处理,但你有完全的控制权并且你知道成本。如果它不重要,请保持冷静并继续。如果需要处理,请处理它。

选项2A:std::optional

optional使金丝雀价值正式化。您可以测试它以确保您得到响应然后继续。首先,一些文档:http://en.cppreference.com/w/cpp/utility/optional

optional字面上刚刚出现在C ++ 17标准修订版中。我认为它在9月或10月正式批准,因此除非您使用最新的编译器,否则它可能无法使用。

事实上,我没有这样的编译器,也无法提供我已经测试过的代码,目前可以使用。这就是选项2A的原因。你自己就是这个人。我还没有机会玩它。