首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Kattis利用多态关联“击中目标”

Kattis利用多态关联“击中目标”
EN

Code Review用户
提问于 2016-08-03 00:37:33
回答 1查看 172关注 0票数 7

为了学习C++,我编写了一个卡蒂斯“击中目标”问题解决方案

基本上,程序是在std in和一组点上接受一些形状(圆圈和矩形),并输出包含每个点的形状数。

鉴于这是我的第一个C++程序,我肯定我犯了重大错误。

代码语言:javascript
复制
#include <iostream>
#include <regex>
#include <string>
#include <vector>
class Shape
{
public:
    Shape()
    {
    }
    ~Shape()
    {
    }
    virtual bool contains(int, int) = 0;
};

class Circle : public Shape
{
private:
    int x1, y1, r;

public:
    Circle(const int _x1,const int _y1,const int _r)
    {
        x1 = _x1;
        y1 = _y1;
        r = _r;
    }
    bool contains(const int x,const int y) override
    {
        int x_dist = std::abs(x - x1);
        int y_dist = std::abs(y - y1);
        return x_dist * x_dist + y_dist * y_dist <= r * r;
    }
    ~Circle()
    {
    }
};

class Rectangle : public Shape
{
private:
    int x1, x2, y1, y2;

public:
    Rectangle(const int _x1,const int _y1,const int _x2,const int _y2)
    {
        x1 = _x1;
        x2 = _x2;
        y1 = _y1;
        y2 = _y2;
    }
    bool contains(const int x,const int y) override
    {
        return x >= x1 && x <= x2 && y >= y1 && y <= y2;
    }
    ~Rectangle()
    {
    }
};

int main(void)
{
    std::string line;
    std::getline(std::cin, line);
    int num_shapes = std::stoi(line);
    std::vector<std::unique_ptr<Shape> > shapes;
    std::regex rect_pat{ R"(rectangle (-?\d+) (-?\d+) (-?\d+) (-?\d+))" };
    std::regex circ_pat{ R"(circle (-?\d+) (-?\d+) (-?\d+))" };
    for(int a = 0; a < num_shapes; a++) {
        std::getline(std::cin, line);
        std::smatch matches;
        if(std::regex_search(line, matches, rect_pat)) {
            std::unique_ptr<Shape> rect(new Rectangle(std::stoi(matches[1]), std::stoi(matches[2]), std::stoi(matches[3]), std::stoi(matches[4])));
            shapes.push_back(std::move(rect));

        } else if(std::regex_search(line, matches, circ_pat)) {

            std::unique_ptr<Shape> circ(
                new Circle(std::stoi(matches[1]), std::stoi(matches[2]), std::stoi(matches[3])));
            shapes.push_back(std::move(circ));
        }
    }
    std::getline(std::cin, line);

    int num_points = std::stoi(line);
    for(int a = 0; a < num_points; a++) {
        int x, y;
        std::cin >> x >> y;
        int num_containing = 0;

        for(std::vector<int>::size_type i = 0; i != shapes.size(); i++) {
            if((shapes[i]->contains)(x, y)) {
                num_containing++;
            }
        }
        std::cout << num_containing << '\n';
    }
    return 0;
}

为了提高可读性,我更希望使用count_if和lambda,而不是for循环来计算包含点的形状数,但无法使其工作。

我也不确定我对unique_ptr的使用以及我如何解析输入是否真的是最优的。

EN

回答 1

Code Review用户

回答已采纳

发布于 2016-08-03 06:37:50

虚拟析构函数

作为一个简单的经验规则,如果一个基类包含任何virtual函数,那么它的析构函数也应该是virtual。声明虚拟函数的主要原因是派生类可以覆盖该虚拟函数。如果您有公共派生,您几乎总是希望析构函数是虚拟的--否则,通过指向基类的指针销毁派生对象会产生未定义的行为。

定义空函数

作为另一个简单的经验法则,如果您对函数应该做什么没有一个具体的概念,那么最好不要定义它。在您的例子中,基类的构造函数没有做任何事情,所以可能根本不需要它。析构函数目前也没有执行任何操作,但是应该将其定义为虚拟(参见前面的内容)。

如果派生类在其析构函数中没有任何要做的事情,则可能不需要定义它们的析构函数。

工厂

您可能需要考虑将从文件中读取数据并从该数据中创建对象的所有代码单独移动到类(可能只是一个函数)中。这类东西通常叫做工厂。

错误处理

现在,如果您遇到错误的输入(“圆圈”或“矩形”模式都不是由一行输入匹配的),您只需忽略这一行输入,然后继续下一行输入。对于您在这里处理的站点来说,这是很好的,但是在现实世界中,这很少是好的行为,在现实世界中,糟糕的输入通常会向用户发出一些信息,让他们知道发生了什么。

标准算法

您提到使用std::count_if进行计数。你可以用这样的方法:

代码语言:javascript
复制
std::cout << std::count_if(shapes.begin(), shapes.end(),
        [=](std::unique_ptr<Shape> &s) { return s->contains(x, y); }) << "\n";

请注意,在本例中,unique_ptr实际上工作得不太好,因此我们最终通过引用传递它。否则,指针将被移动(传输)到算法中,因此向量中的指针将释放它;在第二次迭代中,向量将只包含空指针。

这可以用shared_ptrs来解决。另一个(可能更好的)解决方案是查找(并使用) Boost ptr_向量

添加点类

至少应该考虑添加Point类(或者struct)来封装X/Y坐标对。

更喜欢成员初始化程序列表而不是赋值

当您有一个从参数初始化成员的构造函数时,通常最好在成员初始化程序列表中进行初始化。例如,您的Circle构造函数可能如下所示:

代码语言:javascript
复制
Circle(const int _x1, const int _y1, const int _r) : x1(_x1), y1(_y1), r(_r) {
}

请注意,像这样的空体的ctor是很常见的,而且是预期的。

更喜欢make_unique而不是unique_ptr<T>(new T(arguments))

使用朴素的new来分配对象(S)的理由很少(如果有的话),这样做可以引入例外安全问题 (在这段代码中并不会出现这种情况,但我建议通常使用make_unique,因为它消除了这种可能性)。

将其中的大部分放在一起,我们最终可能会得到这样的代码:

代码语言:javascript
复制
#include <iostream>
#include <memory>
#include <regex>
#include <string>
#include <vector>

class Shape {
public:
    virtual ~Shape() { }
    virtual bool contains(int, int) = 0;
};

class Circle : public Shape {
    int x1, y1, r;
public:
    Circle(const int _x1, const int _y1, const int _r) 
        : x1(_x1), y1(_y1), r(_r) 
    { }

    bool contains(const int x, const int y) override {
        int x_dist = std::abs(x - x1);
        int y_dist = std::abs(y - y1);
        return x_dist * x_dist + y_dist * y_dist <= r * r;
    }
};

class Rectangle : public Shape {
    int x1, x2, y1, y2;
public:
    Rectangle(const int _x1, const int _y1, const int _x2, const int _y2) 
        : x1(_x1), y1(_y1), x2(_x2), y2(_y2) 
    { }

    bool contains(const int x, const int y) override {
        return x >= x1 && x <= x2 && y >= y1 && y <= y2;
    }
};

struct Factory {
    std::unique_ptr<Shape> build(std::string const &line) {
        static const std::regex rect_pat{ R"(rectangle (-?\d+) (-?\d+) (-?\d+) (-?\d+))" };
        static const std::regex circ_pat{ R"(circle (-?\d+) (-?\d+) (-?\d+))" };

        std::smatch matches;
        if (std::regex_search(line, matches, rect_pat)) {
            return std::make_unique<Rectangle>(std::stoi(matches[1]), std::stoi(matches[2]), std::stoi(matches[3]), std::stoi(matches[4]));
        }
        else if (std::regex_search(line, matches, circ_pat)) {
            return std::make_unique<Circle>(std::stoi(matches[1]), std::stoi(matches[2]), std::stoi(matches[3]));
        }
        // Probably want to add something here to deal with bad data (e.g., throw an exception).
    }

};

int main(void)
{
    std::string line;
    std::getline(std::cin, line);
    int num_shapes = std::stoi(line);
    std::vector<std::unique_ptr<Shape> > shapes;

    Factory f;

    for (int a = 0; a < num_shapes; a++) {
        std::getline(std::cin, line);
        shapes.push_back(f.build(line));
    }
    std::getline(std::cin, line);

    int num_points = std::stoi(line);
    for (int a = 0; a < num_points; a++) {
        int x, y;
        std::cin >> x >> y;

        std::cout << std::count_if(shapes.begin(), shapes.end(),
            [=](std::unique_ptr<Shape> &s) { return s->contains(x, y); }) << "\n";
    }
}
票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/136715

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档