You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

模板类继承IOutput导致射线追踪器程序出现段错误

问题描述

我正在开发一款射线追踪器,尝试同时支持多种渲染结果输出方式(目前仅实现PPM格式和SFML输出)。为此编写了IOutput接口,让不同输出方式的类继承该接口。但实例化PPM_file<800,600>时程序直接触发段错误,移除继承及相关代码后段错误消失,无法定位原因。

问题头文件代码:

#ifndef PPM_FILE_HPP_
#define PPM_FILE_HPP_

// #include "../Core/math.hpp"

#include <tuple>
#include <array>

#include <string>
#include <fstream>
#include <sstream>

namespace Math {

    class Vector3D {
    public:
        double x, y, z;

        Vector3D() : x(0), y(0), z(0) {}

        Vector3D(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {}
    };
}

template <int width, int height>
class IOutput {
    public:
        IOutput() : is_rendered(false) {}
        virtual void refresh() = 0;
        virtual void set_pixel(std::tuple<int, int> location, Math::Vector3D color) = 0;
        virtual void set_as_rendered() = 0;

    protected:
        std::array<std::array<Math::Vector3D, width>, height> grid;
        bool is_rendered = false;
};

template <int width, int height>
class PPM_file : public IOutput<width, height> {
    public:
        PPM_file(std::string filename) : IOutput<width, height>(), file(filename), color_depth(255)
        {
            if (!file.is_open())
                throw std::runtime_error("Unable to open file: " + filename);
            file << "P3\n" << width << " " << height << "\n" << color_depth << "\n";
        }
        ~PPM_file()
        {
            if (!this->is_rendered)
                set_as_rendered();
            if (file.is_open())
                file.close();
        }
        void refresh() override {};
        void set_pixel(std::tuple<int, int> location, Math::Vector3D color) override
        {
            int x = std::get<0>(location);
            int y = std::get<1>(location);

            if (x >= 0 && x < width && y >= 0 && y < height) {
                this->grid[y][x] = color;
            } else {
                std::cerr << "Error: Pixel location (" << x << ", " << y << ") is out of bounds." << std::endl;
            }
        }
        void set_as_rendered() override
        {
            this->is_rendered = true;
            for (const auto& row : this->grid) {
                for (const auto& pixel : row) {
                    file << vec3_to_string(pixel);
                }
            }
            file.close();
        }

    protected:
        std::ofstream file;
        int color_depth;
    private:
        std::string vec3_to_string(Math::Vector3D vector)
        {
            std::ostringstream ss;
            ss << static_cast<int>(vector.x * 255) << " "
            << static_cast<int>(vector.y * 255) << " "
            << static_cast<int>(vector.z * 255) << "\n";
            return ss.str();
        }
};

#endif /* !PPM_FILE_HPP_ */

测试主函数代码:

#include "src/Output/PPM_file.hpp"

int main()
{
    std::cout << "This line will never get executed, the program crashes as soon as a function containing the problematic piece of code is called" << std::endl;
    PPM_file<800, 600> test_file("image.ppm");
    return 0;
}
解决方案

核心原因:栈溢出

IOutput类中的grid是栈上分配的二维数组,当实例化PPM_file<800,600>时,这个数组的总内存占用约11MB,而大多数操作系统的默认栈大小仅为8MB左右,直接在栈上分配这么大的内存会撑爆栈空间,触发段错误。

修复方法

方法1:改用动态容器std::vector

把IOutput中的grid从栈数组改为堆上分配的std::vector,避免占用栈空间:

template <int width, int height>
class IOutput {
    public:
        // 构造函数中初始化vector的行列大小
        IOutput() : is_rendered(false), grid(height, std::vector<Math::Vector3D>(width)) {}
        virtual void refresh() = 0;
        virtual void set_pixel(std::tuple<int, int> location, Math::Vector3D color) = 0;
        virtual void set_as_rendered() = 0;

    protected:
        // 替换array为vector
        std::vector<std::vector<Math::Vector3D>> grid;
        bool is_rendered = false;
};

方法2:将数组放到堆上

如果想保留std::array的类型,可以用智能指针管理堆上的数组:

template <int width, int height>
class IOutput {
    public:
        IOutput() : is_rendered(false), grid(std::make_unique<std::array<std::array<Math::Vector3D, width>, height>>()) {}
        virtual void refresh() = 0;
        virtual void set_pixel(std::tuple<int, int> location, Math::Vector3D color) = 0;
        virtual void set_as_rendered() = 0;

    protected:
        std::unique_ptr<std::array<std::array<Math::Vector3D, width>, height>> grid;
        bool is_rendered = false;
};

注意使用时需要通过->访问数组元素,例如this->grid->at(y).at(x) = color;。

额外优化

PPM_file的析构函数中,set_as_rendered()已经调用了file.close(),后续再次调用file.close()属于重复关闭文件,建议移除析构中的file.close()——std::ofstream的析构函数会自动关闭打开的文件。

内容的提问来源于stack exchange,提问作者1ups_

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:57:04