You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Boost Geometry中值返回多边形对象原地迭代时首元素出现垃圾值的问题咨询

Boost Geometry中值返回多边形对象原地迭代时首元素出现垃圾值的问题咨询

我最近在项目中集成Boost Geometry时遇到了一个奇怪的问题:当从自定义类中返回多边形几何对象并直接原地迭代其外层点集时,第一个点会出现垃圾值,但如果先把返回的多边形存到本地变量再迭代就一切正常。以下是我复现问题的最小示例和相关疑问:

复现代码

头文件 shape.hpp

#pragma once
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/version.hpp>

using my_point_t = boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>;
using my_polygon_t = boost::geometry::model::polygon<my_point_t>;

class Shape {
private:
    my_polygon_t polygon_;
public:
    Shape() = default;
    Shape(const std::vector<my_point_t>& poly){
        for (const auto& pt : poly) {
            polygon_.outer().push_back(pt);
        }
        // Close the polygon by adding the first point again
        if (!polygon_.outer().empty()) {
            polygon_.outer().push_back(polygon_.outer().front());
        }
    }
    my_polygon_t getPolygon() const {
        return polygon_;
    }
};

有问题的 main.cpp

#include <iostream>
#include "shape.hpp"

int main() {
    std::cout << BOOST_LIB_VERSION << std::endl; // Or BOOST_VERSION
    std::vector<my_point_t> my_points;
    my_points.push_back({0.0, 0.0});
    my_points.push_back({0.0, 10.0});
    my_points.push_back({10.0, 10.0});
    my_points.push_back({10.0, 0.0});

    Shape shape = Shape(my_points);
    for (my_point_t pt : shape.getPolygon().outer()) {
        std::cout << "x: " << boost::geometry::get<0>(pt) << ", y: " << boost::geometry::get<1>(pt) << '\n';
    }
    return 0;
}

有问题的输出

1_74 -> Version of Boost Geometry
x: 1.30295e-313, y: 1.23525e-76
x: 0, y: 10
x: 10, y: 10
x: 10, y: 0
x: 0, y: 0

修正后的 main.cpp

#include <iostream>
#include "shape.hpp"

int main() {
    std::cout << BOOST_LIB_VERSION << std::endl; // Or BOOST_VERSION
    std::vector<my_point_t> my_points;
    my_points.push_back({0.0, 0.0});
    my_points.push_back({0.0, 10.0});
    my_points.push_back({10.0, 10.0});
    my_points.push_back({10.0, 0.0});

    Shape shape = Shape(my_points);
    auto result = shape.getPolygon();
    for (my_point_t pt : result.outer()) {
        std::cout << "x: " << boost::geometry::get<0>(pt) << ", y: " << boost::geometry::get<1>(pt) << '\n';
    }
    return 0;
}

正确的输出

1_74 -> Version of Boost Geometry
x: 0, y: 0
x: 0, y: 10
x: 10, y: 10
x: 10, y: 0
x: 0, y: 0

我的疑问

我原本理解返回值会在整个循环的生命周期内保持有效,但实际情况却出现了悬空引用导致的未定义行为。想请教这是我对C++临时对象生命周期的理解有误,还是Boost Geometry的实现存在问题?


问题原因分析

这并不是Boost Geometry的bug,而是C++临时对象生命周期与引用结合时的常见陷阱:

  • shape.getPolygon()返回的是一个临时的my_polygon_t对象,这个临时对象的生命周期仅在创建它的表达式中有效。
  • outer()方法返回的是该多边形对象内部外层容器的引用。在有问题的循环中,范围for循环迭代的是这个引用指向的容器,但临时的多边形对象在表达式shape.getPolygon().outer()结束后就会被销毁,导致引用悬空,访问的是已经被释放的内存空间,因此出现垃圾值(后续元素可能未被覆盖属于未定义行为,不能依赖)。
  • 当将返回值存储到本地变量result时,result的生命周期覆盖整个循环过程,outer()返回的引用指向的是result内部的有效容器,因此访问完全正常。

解决方案

方案1:显式存储临时对象(已验证有效)

像你已经尝试的那样,先将getPolygon()的返回值存储到一个本地变量,确保其生命周期覆盖整个迭代过程,避免引用悬空。

方案2:修改getPolygon返回常量引用

如果不需要返回多边形的副本,可以修改Shape类的getPolygon方法,返回常量引用而非值:

const my_polygon_t& getPolygon() const {
    return polygon_;
}

这样shape.getPolygon().outer()返回的引用指向的是shape对象内部的polygon_,而shape在循环期间始终有效,因此不会出现悬空引用问题,原有的有问题的main.cpp代码也能正常运行。

注意:使用这个方案时,要确保Shape对象的生命周期不短于迭代过程,避免出现新的悬空引用。

内容来源于stack exchange

火山引擎 最新活动