点积是标量,公式为a.xb.x + a.yb.y + a.z*b.z,推荐手写表达式或用std::inner_product配合std::array;叉积是向量,必须手写标准公式并注意顺序与符号;封装为vec3结构体更安全高效,避免std::vector存储三维坐标。

点积直接用 std::inner_product 或手写三元乘加
点积是标量,公式为 a.x*b.x + a.y*b.y + a.z*b.z。C++ 没有内置向量类型,所以要么自己定义结构体,要么用 std::array<float></float> 或 std::vector<float></float>(但后者不推荐,因为长度固定且需保证 size==3)。std::inner_product 可用,但要注意迭代器范围必须精确对应三个元素,否则行为未定义。
常见错误:用 std::vector 存坐标却没检查 size,导致越界或计算错误;或者把点积结果误当成向量继续做叉积运算。
- 推荐手写表达式,清晰、零开销:
float dot = a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; - 若用
std::inner_product,确保两个容器都是std::array<float></float>,调用形式为std::inner_product(a.begin(), a.end(), b.begin(), 0.0f) - 浮点精度问题一般不影响点积本身,但后续用于归一化或角度计算时,要注意
acos(dot / (len_a * len_b))中除零或超范围(|dot| > len_a*len_b)
叉积必须手写,顺序决定方向
叉积结果是向量,且满足右手定则:a × b ≠ b × a,事实上 a × b == -(b × a)。标准公式为:c.x = a.y*b.z - a.z*b.yc.y = a.z*b.x - a.x*b.zc.z = a.x*b.y - a.y*b.x
容易踩的坑是记混符号顺序,尤其第二项总是负号开头;另一个问题是把叉积结果直接当单位向量用——它模长等于 |a||b|sinθ,除非 a、b 正交且单位化,否则不是单位向量。
- 务必按标准公式逐项写,不要试图用循环简化——三维叉积没有通用循环形式
- 如果封装成函数,参数顺序要明确文档,比如
cross(a, b)返回a × b,别和数学库(如 GLM)的约定冲突 - 零向量参与叉积会得零向量,但若 a 和 b 平行(包括同向/反向),结果也是零向量,这是正常现象,不是 bug
用 struct vec3 封装比裸数组更安全
裸 float[3] 或 std::array 缺少语义,容易传错顺序(比如把法向量当位置传)。定义一个轻量 struct 能避免多数低级错误,还能自然支持操作符重载。
示例:
struct vec3 {
float x, y, z;
vec3 operator+(const vec3& o) const { return {x+o.x, y+o.y, z+o.z}; }
vec3 operator*(float s) const { return {x*s, y*s, z*s}; }
// 点积可作为成员函数
float dot(const vec3& o) const { return x*o.x + y*o.y + z*o.z; }
// 叉积也一样
vec3 cross(const vec3& o) const {
return {y*o.z - z*o.y,
z*o.x - x*o.z,
x*o.y - y*o.x};
}
};
这样调用就直观:v1.dot(v2)、v1.cross(v2),不易混淆。注意不要在 cross 里返回局部数组或指针——必须返回值对象。
- 成员函数方式比全局函数更易维护命名空间,也避免 ADL 意外触发
- 如果项目已用 Eigen 或 GLM,优先用它们的
Vec3类型,别重复造轮子;但纯 C++ 无依赖场景,struct是最简可靠选择 - 别给
vec3加虚函数或动态内存——它应该就是 POD 类型,保证 sizeof == 12 且可 memcpy
性能与 ABI 兼容性:别用 std::vector<float></float> 存三维点
有人图省事用 std::vector<float></float> 表示向量,但这是严重误用:每次构造都涉及堆分配,访问慢,且无法保证长度为 3。编译器几乎无法对此做向量化优化,ABI 层也不稳定(不同 STL 实现 layout 不同)。
真正影响性能的地方在于:点积和叉积本身极快,瓶颈往往出在数据布局上。比如处理大量向量时,struct vec3 数组是 SoA(结构体数组)布局,而三个分离的 std::vector(x、y、z 各一个)是 AoS(数组结构体)变体,前者 cache 友好性差,后者可能利于 SIMD,但需手动对齐和向量化。
- 单次计算:用
struct vec3或std::array<float></float>,二者性能无差别 - 批量计算(>1000 个向量):考虑用
alignas(16) std::array<float></float>数组,并确保内存对齐,便于后续手写 SIMD(如 SSE)加速 - 绝对不要在热路径里用
std::vector<float></float>或std::list存三维坐标——不只是慢,还增加调试复杂度
实际工程中,点积和叉积本身几条指令就搞定,真正的复杂点在于数据怎么来、往哪去、是否需要误差控制、以及和坐标系约定(比如 OpenGL 是右手系,DirectX 默认左手系)是否一致——这些比算子实现本身更容易出错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











