当我尝试初始化大多数矩阵对象时,我的代码给出了运行时错误。我测试了几种情况,将在下面展示。它只是说运行时错误,并没有解释测试系统中的情况,并且在我的 Windows 计算机上正确运行,返回退出代码 0。
我将首先给出导致错误的函数,并在底部给出相关的构造函数。这段代码通常应该执行卷积运算,但我禁用了一些部分来查找错误的根本原因。这就是我得到的:
// Test case 1
ImageMatrix Convolution::convolve(const ImageMatrix& input_image) const {
int width = input_image.get_width();
int height = input_image.get_height();
ImageMatrix result(height, width, 0); // Gives runtime error
// Some convolution logic disabled by commenting
return result;
}
// Test case 2
ImageMatrix Convolution::convolve(const ImageMatrix& input_image) const {
int width = input_image.get_width();
int height = input_image.get_height();
ImageMatrix result(1, 1, 0); // Runs correctly without error
return result;
}
// Test case 3
ImageMatrix Convolution::convolve(const ImageMatrix& input_image) const {
int width = input_image.get_width();
int height = input_image.get_height();
ImageMatrix result(3, 3, 0); // Gives runtime error
return result;
}
// Test case 4
ImageMatrix Convolution::convolve(const ImageMatrix& input_image) const {
int width = input_image.get_width();
int height = input_image.get_height();
ImageMatrix result(1, 1, 0);
ImageMatrix kernel(2, 2, 1); // Runs correctly without error after the necessary change in the constructor
return result;
}
// The constructors of the ImageMatrix class
// Constructor currently in use
ImageMatrix::ImageMatrix(int height, int width, double value) : height(height), width(width) {
this->height = height;
this->width = width;
data = new double*[height];
for (int i = 0; i < height; ++i) {
data[i] = new double[width];
}
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; j++) {
data[i][j] = value;
}
}
} for (int j = 0; j < width; j++) {
data[i][j] = 0;
}
}
}
// Destructor
ImageMatrix::~ImageMatrix() {
if (data) {
for (int i = 0; i < height; ++i) {
delete[] data[i];
}
delete[] data;
data = nullptr;
}
}
正如 @Eljay 在评论中报告的那样,您的构造函数和析构函数几乎没有问题。 Eljay 的说法可能是正确的,您的问题可能出在其他地方。
您提供的测试用例是一个可能的候选者。我无法理解它们的意义。在我看来,你对函数有四种不同的定义
Convolution::convolve
。这些是对函数convolve
的调用,而不是定义吗?
无论如何,就使班级
Convolution
工作而言,班级ImageMatrix
似乎是一种干扰,所以我在下面的程序中删除了它。
我发现的唯一缺陷是,如果调用运算符
new
抛出 std::bad_alloc
对象,构造函数将泄漏内存。我在下面修复了这个问题。
// The constructors of the ImageMatrix class
// Constructor currently in use
ImageMatrix::ImageMatrix(int height, int width, double value)
: height(height), width(width)
{
//this->height = height; // redundant
//this->width = width; // redundant
if (height <= 0)
throw std::invalid_argument("ERROR: height is zero.");
if (width <= 0)
throw std::invalid_argument("ERROR: width is zero.");
// This could throw std::bad_alloc.
data = new double* [height];
// Each call to `new` in the loop below could throw.
// If any one of them does, then you must back out
// (i.e., delete) all of the allocations that were
// successful.
//
// If there is a throw, then variable `i` will hold the
// subscript that was not allocated. All subscripts less
// than `i` must have their allocations deleted.
//
// In addition, after a throw, you must deallocate the
// memory assigned to pointer `data`.
//
// Those deallocations happen in the catch block.
int i{};
try {
for (; i < height; ++i) {
data[i] = new double[width];
}
}
catch (std::bad_alloc const&) {
while (i--)
delete[] data[i];
delete[] data;
throw;
}
// Initialize
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; j++) {
data[i][j] = value;
}
}
}
我坚持你对下标的选择
int
,而不是 std::size_t
。然而,我更喜欢使用 std::size_t
Class
ImageMatrix
是一个“3 或 5 规则”类,因此请务必提供我在代码中列出的缺少的“特殊”函数。
使用下面的程序,我能够分配测试用例中的所有矩阵。
// main.ixx
export module main;
import std;
class ImageMatrix
{
double** data{ nullptr };
int height{}, width{};
public:
ImageMatrix() = default;
ImageMatrix(int height, int width, double value = 0.0);
// Rule of 5 (plus a noexcept swap function)
// 1. destructor
// 2. copy constructor - not yet coded
// 3. move constructor - not yet coded
// 4. copy-assignment operator - not yet coded
// 5. move-assignment operator - not yet coded
// 6. swap - not yet coded
~ImageMatrix();
int get_height() const {
return height;
}
int get_width() const {
return width;
}
double& at(int const row, int const col) {
check(row, col);
return data[row][col];
}
double const& at(int const row, int const col) const {
check(row, col);
return data[row][col];
}
private:
void check(int const row, int const col) const {
if (row < 0 || height <= row)
throw std::out_of_range("ERROR: row is out of range.");
if (col < 0 || width <= col)
throw std::out_of_range("ERROR: col is out of range.");
}
};
// The constructors of the ImageMatrix class
// Constructor currently in use
ImageMatrix::ImageMatrix(int height, int width, double value)
: height(height), width(width)
{
//this->height = height; // redundant
//this->width = width; // redundant
if (height <= 0)
throw std::invalid_argument("ERROR: height is zero.");
if (width <= 0)
throw std::invalid_argument("ERROR: width is zero.");
// This could throw std::bad_alloc.
data = new double* [height];
// Each call to `new` in the loop below could throw.
// If any one of them does, then you must back out
// (i.e., delete) all of the allocations that were
// successful.
//
// If there is a throw, then variable `i` will hold the
// subscript that was not allocated. All subscripts less
// than `i` must have their allocations deleted.
//
// In addition, after a throw, you must deallocate the
// memory assigned to pointer `data`.
//
// Those deallocations happen in the catch block.
int i{};
try {
for (; i < height; ++i) {
data[i] = new double[width];
}
}
catch (std::bad_alloc const&) {
while (i--)
delete[] data[i];
delete[] data;
throw;
}
// Initialize
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; j++) {
data[i][j] = value;
}
}
}
// Destructor
ImageMatrix::~ImageMatrix() {
if (data) {
for (int i{ height }; i--;) {
delete[] data[i];
}
delete[] data;
data = nullptr;
}
}
void display(ImageMatrix const& m)
{
for (int row{}; row < m.get_height(); ++row) {
for (int col{}; col < m.get_width(); ++col) {
std::cout << std::setw(12) << m.at(row, col);
}
std::cout.put('\n');
}
std::cout.put('\n');
}
void test(
std::string_view heading,
int const rows,
int const cols,
double const value)
{
ImageMatrix m{ rows, cols, value };
std::cout << std::format(
"{}"
"\n"
"\n height: {}, width: {}"
"\n value: {}"
"\n\n"
, heading
, m.get_height()
, m.get_width()
, value
);
display(m);
}
export int main()
{
test("Example 1", 1, 1, 0);
test("Example 2", 1, 1, 1.1);
test("Example 3", 2, 2, 1);
test("Example 4", 2, 2, 2.2);
test("Example 5", 3, 3, 0);
test("Example 6", 3, 3, 3.3);
return 0;
}
// end file: main.ixx
这是输出:
Example 1
height: 1, width: 1
value: 0
0
Example 2
height: 1, width: 1
value: 1.1
1.1
Example 3
height: 2, width: 2
value: 1
1 1
1 1
Example 4
height: 2, width: 2
value: 2.2
2.2 2.2
2.2 2.2
Example 5
height: 3, width: 3
value: 0
0 0 0
0 0 0
0 0 0
Example 6
height: 3, width: 3
value: 3.3
3.3 3.3 3.3
3.3 3.3 3.3
3.3 3.3 3.3
这是另一种可能的解决方案。我只是半开玩笑地提出这个!
// ImageMatrix.h
#ifndef IMAGEMATRIX_H
#define IMAGEMATRIX_H
// Class Array2D by @PaulMacKenzie
// https://godbolt.org/z/4zsoqnb11
#include "Array2D.h"
using ImageMatrix = Array2D<double>;
// end file: ImageMatrix.h