【手撕】点到直线距离&&点到线段距离(C++11代码规范)

  • 首先实现下面两个功能:
    1. 计算点到直线的距离;
    2. 计算点到线段的距离;
  • 然后列举当前代码中 C++ 11 代码规范(希望能养成良好习惯);

C++ 代码实现:

#include <iostream>
#include <cmath>
 
namespace {
  constexpr double kEps = 1e-6;
}  // namespace
 
class Vec2d {
 public:
  Vec2d() = default;
  Vec2d(double x, double y) : x_(x), y_(y) {}
 
  double x() const {
    return x_;
  }
 
  double y() const {
    return y_;
  }
 
  Vec2d operator - (const Vec2d& others) const { // 重载减法运算符
    return {x_ - others.x(), y_ - others.y()};
  }
 
  double Length() const {
    return std::sqrt(x_ * x_ + y_ * y_);
  }
 
  double CrossProd(const Vec2d &other) const { // 叉乘
    return x_ * other.y() - y_ * other.x();
  }
 
  double PotProd(const Vec2d &other) const { // 点乘
    return x_ * other.x() + y_ * other.y();
  }

 private:
  double x_{0.0};
  double y_{0.0};
};
 
bool CalPointToSegDis(const Vec2d& p, const Vec2d& a, 
                      const Vec2d& b, double* const dis) {
#if 0 // 功能1:计算点到直线的距离
  if((a - b).Length() < kEps) {
    return false;
  }
 
  *dis = std::abs((p - a).CrossProd(p - b)) / (a - b).Length(); // 距离 = 平行四边形面积 / 底边
  return true;

#else // 功能2:计算点到线段的距离

  const Vec2d ab = b - a;
  const Vec2d ap = p - a;

  if (ab.Length() < kEps) {
    *dis = ap.Length(); // a b 两点重合
    return true;
  }

  const double t = ap.PotProd(ab) / (ab.Length() * ab.Length()); // 计算投影比例 t

  if (t < 0.0) {
    *dis = ap.Length();
  } else if (t > 1.0) {
    *dis = (p - b).Length();
  } else {
    *dis = std::abs(ap.CrossProd(ab)) / ab.Length();
  }

  return true;

#endif
}
 
int main() {
  Vec2d point{-3.0, 3.0};
  Vec2d segment_start{0.0, 0.0};
  Vec2d segment_end{2.0, 0};
  double distance = 0.0;

  if(!CalPointToSegDis(point, segment_start, segment_end, &distance)) {
    std::cout << "Error : The Segment is a point" << std::endl;
    return -1;
  }
  std::cout << "The distance from a point to a straight line is : "
            << distance << std::endl;
  return 0;
}

C++ 11 代码规范:

列表初始化

  •  普通构造函数调用:

    Vec2d point(3.0, 5.1961524);
  • 列表初始化:

    Vec2d point{3.0, 5.1961524}; // 调用构造函数 Vec2d(double x, double y)
  • 优点:

  • 更安全:

    • 避免了窄化转换(narrowing conversion)问题。例如,不允许用花括号初始化将浮点数隐式转换为整数。
      int a{3.5}; // 错误:编译时错误,避免浮点数到整数的窄化转换
    • 传统构造函数调用 int a = 3.5; 会直接截断浮点数,产生潜在的错误。
  • 支持统一初始化:

    • 花括号 {} 既可用于类对象初始化,也可以用于内置类型、结构体或数组的初始化。
  • 简洁易读:

    • 和构造函数调用 Vec2d point(3.0, 5.1961524); 相比,列表初始化的形式更简单,并且不容易混淆为函数声明。

传参中的指针常量

指针常量,不能更改指向,可以更改指向内容;

bool CalPointToSegDis(const Vec2d& p, const Vec2d& a, 
                      const Vec2d& b, double* const dis) {}

重载减法运算符

Vec2d operator - (const Vec2d& others) const {
    return {x_ - others.x(), y_ - others.y()};
  }

constexpr

使用 constexpr 保证这个常量的值在编译时确定,提高性能和安全性,可以减少运行时的计算量;

constexpr double kEps = 1e-6;

匿名命名空间

通过匿名命名空间,限制它的作用范围在当前文件,避免在其他文件中出现命名冲突;

namespace {
  constexpr double kEps = 1e-6; 
}  // namespace

比较浮点数时的精度控制

 double kEps = 1e-6;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值