C++分段函数初始化问题:局部对象超作用域致未定义行为
Great catch on the undefined behavior—you're exactly right about the dangling pointers from local LinearFunction objects going out of scope. Let's fix this properly so you can handle up to 20 points (or more) without issues.
The Root Problem
When you create auto f = LinearFunction(...) inside the loop, that object lives only for the duration of the loop iteration. Storing a pointer to it in the map means once the loop moves on, that pointer points to garbage memory. Accessing it later leads to the weird output you saw.
Fixed Solution: Store Copies Instead of Pointers
The simplest and safest fix is to store actual LinearFunction objects in your map instead of pointers. Since each LinearFunction only holds two doubles (slope and intercept), this is incredibly memory-efficient even for 20 points (19 segments total). Here's the revised code:
#include <iostream> #include <vector> #include <map> #include <algorithm> struct Point { double x; double y; }; class LinearFunction { public: LinearFunction(const Point &A, const Point &B) { double den = A.x - B.x; m = (A.y - B.y) / den; c = (A.x * B.y - A.y * B.x) / den; } // Marked const since evaluating doesn't modify the function double operator()(double x) const { return m * x + c; } private: double m = 0; double c = 0; }; class PiecewiseFunction { public: // Take points by value so we can sort them safely without modifying the original explicit PiecewiseFunction(std::vector<Point> points) { // Critical: sort points by x-coordinate to ensure valid, non-overlapping segments std::sort(points.begin(), points.end(), [](const Point& a, const Point& b) { return a.x < b.x; }); for (size_t i = 0; i < points.size() - 1; ++i) { // Insert a copy of the LinearFunction directly into the map fns.emplace( std::make_pair(points[i].x, points[i+1].x), LinearFunction(points[i], points[i+1]) ); } } double operator()(double x) const { // Find the segment where x falls in [lb, ub) auto iter = std::find_if(fns.cbegin(), fns.cend(), [x](const auto &entry) { const auto& bounds = entry.first; return x >= bounds.first && x < bounds.second; }); if (iter == fns.end()) { // Handle out-of-bounds cases (adjust this to your needs: throw, return NaN, etc.) return 0.0; } // Call the stored LinearFunction directly (no pointer dereferencing needed!) return iter->second(x); } private: // Store copies of LinearFunction instead of pointers std::map<std::pair<double, double>, LinearFunction> fns; }; int main() { // Test the original case std::vector<Point> points {{0,0}, {0.5,1}, {1,0}}; PiecewiseFunction f{points}; std::cout << "x = 0.5; f(x) = " << f(0.5) << std::endl; // Outputs 1.0 as expected // Test with 20 points (linear function y=2x) std::vector<Point> manyPoints; for (int i = 0; i < 20; ++i) { manyPoints.push_back({static_cast<double>(i), static_cast<double>(i*2)}); } PiecewiseFunction f20{manyPoints}; std::cout << "x = 15.3; f(x) = " << f20(15.3) << std::endl; // Outputs 30.6 return 0; }
Key Improvements
- No More Dangling Pointers: By storing copies of
LinearFunction, each segment's function persists for the lifetime of thePiecewiseFunctionobject. - Sorted Points: Added sorting to ensure input points are in order of increasing x—this prevents invalid overlapping segments if someone passes unsorted points.
- Const-Correctness: Marked
operator()as const since evaluating the function doesn't modify its state, making the code safer and more idiomatic. - Efficient Insertion: Used
emplaceto constructLinearFunctionobjects directly in the map, avoiding unnecessary copies.
Alternative: Smart Pointers (If You Prefer Heap Allocation)
If you need to use heap allocation (e.g., for more complex function types later), you can use std::unique_ptr to manage memory safely:
// Inside PiecewiseFunction's private section: std::map<std::pair<double, double>, std::unique_ptr<LinearFunction>> fns; // In the constructor: for (size_t i = 0; i < points.size() - 1; ++i) { fns.emplace( std::make_pair(points[i].x, points[i+1].x), std::make_unique<LinearFunction>(points[i], points[i+1]) ); } // In operator(): return (*iter->second)(x);
This works because unique_ptr takes ownership of the heap-allocated LinearFunction and automatically cleans it up when the map is destroyed—no more manual memory management!
内容的提问来源于stack exchange,提问作者Oleg

