模板类继承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_
相关产品推荐
相关产品推荐

