【问题标题】:c++ same code compiles/runs never in Visual Studio and sometimes in Qt Creatorc++ 相同的代码永远不会在 Visual Studio 中编译/运行,有时在 Qt Creator 中
【发布时间】:2015-08-16 14:46:40
【问题描述】:

当我注意到以下问题时,我正在做一些 C++ 练习。 给定的代码不会在 Visual Studio 2013 或 Qt Creator 5.4.1 中运行/编译

给出错误:

invalid types 'double[int]' for array subscript
test[0][0] = 2;
         ^

但是,当您第一次将头文件中的第 16(和第 17)行从 double &operator[];double operator[] 并在源文件中进行相同的更改 -> 然后编译它(同时出现多个错误) -> 最后将其更改回原来的 double &operator[];。然后在 Qt Creator 5.4.1 中,它将编译并运行,同时给出预期的结果。

编辑:这并不总是有效,但是将其更改为 double *operator[] 而不是 double operator[] 总是会重现问题。

为什么会这样?

矩阵.h

#ifndef MATRIX_H
#define MATRIX_H

#include <iostream>

using namespace std;

class Matrix
{
private:
    double** m_elements;
    int m_rows;
    int m_columns;
public:
    Matrix(int rows = 1, int columns = 1);
    double &operator[](int index);
    const double &operator[](int index) const;
    friend ostream &operator<<(ostream &ostr, Matrix matrix);
};

#endif // MATRIX_H

矩阵.cpp

#include "matrix.h"

Matrix::Matrix(int rows, int columns)
{
    m_rows = rows;
    m_columns = columns;
    m_elements = new double*[rows];
    for(int i=0; i<rows; i++)
    {
        m_elements[i] = new double[columns];
        for(int j=0; j<columns; j++)
            m_elements[i][j] = 0;
    }
}

double &Matrix::operator[](int index)
{
    return *(m_elements[index]);
}

const double &Matrix::operator[](int index) const
{
    return *(m_elements[index]);
}

ostream &operator<<(ostream &ostr, Matrix matrix)
{
    for(int i=0; i<matrix.m_rows; i++)
    {
        for(int j=0; j<matrix.m_columns; j++)
        {
            ostr << matrix.m_elements[i][j] << " ";
        }
        ostr << "\n";
    }
    return ostr;
}

主要

#include <iostream>
#include "matrix.h"

using namespace std;

int main()
{
    Matrix test(4,4);
    test[0][0] = 2;
    cout << test;

    return 0;
}

【问题讨论】:

  • 我已经做了几十年了,但从来不敢写*m_elements[index];。你知道是先申请*还是[]?我不! :-) 我会根据预期的顺序写*(m_elements[index]);(*m_elements)[index];
  • 感谢您的提示(它确实像我预期的那样使用了*(m_elements[index]);,但可能在不同的编译器上不会出现这种情况),我编辑了我的文件,但这仍然不能解决问题我有。
  • 我很确定从operator[] 返回一个对双重对象的引用不是你想要做的。如果operator[] 应该在您的矩阵中提供 row 的基数,则您应该返回简单的m_elements[index],返回类型为double*,或者double *&amp;,如果您真的想要直接引用m_elements[index] 处的指针(我不建议这样做)。

标签: c++ qt visual-studio-2013 qt-creator


【解决方案1】:
double &Matrix::operator[](int index)
{
    return *(m_elements[index]);
}

将返回对列中第一个元素的引用,而不是列。所以调用test[0][0] = 2; 会尝试将[] 运算符应用于双精度,而不是双精度数组。

快速解决方案:

double * & Matrix::operator[](size_t index)
{
    return m_elements[index];
}

这将返回对指针的引用(继续阅读以了解我为什么要打扰该引用),您可以在返回的指针上使用 [] 来获取数据元素。

但是……

有更好的方法来做到这一点。

如果可能,使用 std::vector 代替动态数组。

std::vector<std::vector<double> > m_elements(m_rows, std::vector<double>(m_columns, 0.0));

这将解决很多潜在的问题,并一次性将矩阵初始化为 0。但它不会解决 [][] 索引。这仍然需要一些工作。

进行索引的最简单和最安全的方法是根本不使用 [] 运算符。而是定义一个新方法。这样您就可以完全控制所公开的内容,并且可以在超出范围之前完全测试输入的有效性。

double &Matrix::at(size_t row, size_t column) 
{
    // optional overrun defence if desired
    if (row < m_rows || column < m_columns)
    {
        return m_elements[row][column]; 
    } 
    throw std::out_of_range("Matrix indices out of range");
}
double Matrix::at(size_t row, size_t column) const
{
    // put overrun defence here if desired
    return m_elements[row][column]; 
}
matrix.at(2,3) = 2;
constmatrix.at(2,3) = 2; // bad lvalue compiler error

注意使用size_t 代替int。 size_t 是无符号的,不需要对负数进行有效性检查。你不能有一个负数组索引,那为什么要允许这种可能性呢?

还值得注意的是,这种方法可以轻松地将矩阵的存储定义为一维数组,如下所示:

std::vector<double> m_elements(m_rows * m_columns, 0.0);

或者如果你必须使用数组

double m_elements = new double[m_rows* m_columns];

并像这样访问它:

double &Matrix::at(size_t row, size_t column) 
{
    return m_elements[row * m_rows + column]; 
}

为什么?有很多很好的理由。比 m_rows +1 更容易创建、维护和清理一个对象对我来说是一个足够好的理由。另一个极好的原因是地方性。整个矩阵保证在一个连续的块中,而不是一个数组在这里,另一个数组,还有另一个在相当于马里亚纳海沟底部的 RAM 中。缓存命中(以及性能)的几率会大大提高。

如果您更喜欢数组的外观和感觉,则 operator() 重载非常接近。

double &Matrix::operator()(size_t row, size_t column) 
{
    return m_elements[row][column];
 }
double Matrix::operator()(size_t row, size_t column) const
{
    return m_elements[row][column];
}
matrix(2,3) = 2;

如果你必须有 [][]

[] 运算符的建议形式返回对索引数据的引用,在本例中为向量或指向行数组的指针。

std::vector<double> & Matrix::operator[](size_t index)
{
    return m_elements[index];
}

double * & Matrix::operator[](size_t index)

数组和向量内部是相同的。

警告:这允许用户处理返回的向量或指针引用的各种麻烦。以matrix[0].clear();matrix[0] = NULL; 为例。

double * Matrix::operator[](size_t index)

将通过返回指针的副本来防止大多数滥用。不幸的是,这不能保护向量,因为向量的副本将是与源内容的副本完全不同的向量。更新它并期待持久性将是徒劳的。向量必须在包装类中对用户隐藏,这很快就会变得太多工作。

此外,返回副本或包装器也会阻止引用的合法使用,并违反最小意外法则:Matrix [] 运算符与其他 [] 运算符的工作方式不同,如果出现以下情况,可能会导致意外行为毫无戒心的编码人员将其用作常规 [] 运算符。

我的意见是返回未受保护的引用,如果使用 Matrix 类的人想朝自己的脑袋开枪......好吧,你只能做这么多。如果必须保护用户,请使用上述at 方法或operator() 方法。

Const [] 运算符对于向量是相似的

std::vector<double> const & Matrix::operator[](size_t index) const

但数组不同,因为指针和指向的值都应该是 const

double const * const & Matrix::operator[](size_t index) const

我建议的实现方式:

矩阵.h

#ifndef MATRIX_H
#define MATRIX_H

#include <iostream>
#include <vector>

// note that the using namespace std; is gone. One should never put it in the header
// and one should also think hard about putting it in the implementation file
class Matrix
{
private:
    std::vector<double> m_elements;
    size_t m_rows;
    size_t m_columns;
public:
    Matrix(int rows = 1, int columns = 1);

    double &operator()(size_t row, size_t column);

    double operator()(size_t row, size_t column) const;

    friend std::ostream &operator<<(std::ostream &ostr, const Matrix & matrix);
};

#endif // MATRIX_H

矩阵.cpp

#include <stdexcept>
#include "Matrix.h"

Matrix::Matrix(int rows, int columns):
        m_elements(rows * columns, 0.0),
        m_rows(rows),
        m_columns(columns)
{
}

std::ostream &operator<<(std::ostream &ostr, const Matrix &matrix)
{
    for(size_t i=0; i<matrix.m_rows; i++)
    {
        for(size_t j=0; j<matrix.m_columns; j++)
        {
            ostr << matrix(i,j) << " ";
        }
        ostr << std::endl;
    }
    return ostr;
}

double &Matrix::operator()(size_t row, size_t column)
{
    if (row < m_rows && column < m_columns)
    {
        return m_elements[row * m_rows + column];
    }
    throw std::out_of_range("Matrix indices out of range");
 }

double Matrix::operator()(size_t row, size_t column) const
{
    if (row < m_rows && column < m_columns)
    {
        return m_elements[row * m_rows + column];
    }
    throw std::out_of_range("Matrix indices out of range");
}

【讨论】:

  • 没有理由返回一个引用,它允许像test[0]++(增加指向第一行的指针)这样可能导致UB的奇怪事情。对 const 指针的引用也不是没有意义的。
  • @Oktalist 这是我的懒惰。我不想尝试解释返回 const 指针引用的编译器警告背后的类型安全问题,我什至没有考虑在非 const 版本中返回非 const 引用的缺点。
  • 这个答案一团糟,也许你可以重写它以给出具体、正确的建议
【解决方案2】:

只因回归的意义。

当您返回引用 &amp; 时,您可以随意更改它,例如 test[2][2] = 2;因为它会返回它曾经引用过的源代码

当你按值返回时,它只是一个临时对象,使用后会被销毁,也就是说,你不能改变它!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多