c++ 相同的代码从不在 Visual Studio 中编译/运行,有时在 Qt Creator 中编译/运行

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

当我注意到以下问题时,我正在做一些 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;
}

最佳答案

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

将返回对列中第一个元素的引用,而不是该列。因此调用 test[0][0] = 2; 尝试将 [] 运算符应用于 double ,而不是 double 数组。

快速解决方案:

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 更容易创建、维护和清理一个对象,这对我来说是一个足够好的理由。另一个极好的原因是地方。整个矩阵保证在一个连续的 block 中,而不是这里一个数组,那里另一个数组,还有一个在 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;

如果你必须有[][]

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

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

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

数组和 vector 的内部结构是相同的。

警告:这允许用户使用返回的 vector 或指针引用来解决各种问题。以 matrix[0].clear();matrix[0] = NULL; 为例。

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

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

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

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

Const[] 运算符对于 vector 是类似的

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");
}

关于c++ 相同的代码从不在 Visual Studio 中编译/运行,有时在 Qt Creator 中编译/运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32036345/

相关文章:

C#无法添加MySQL数据源

c++ - 使用非原子 bool 时的未定义行为

c++ - Qt5 是否有类似于 StringBuilder 或 StringBuffer 的类?

c++ - 如何在其参数中使用带有变量的 QProcess?

c# - 如何让 Visual Studio 只查找单斜杠而不查找双斜杠?

jquery - 检查 HTML 5 视频元素是否获取帧

c++ - QSystemSemaphore::acquire() 中止

c++ - 如何在 C++ 项目中使用非托管 .dll、.lib、.exp

c++ - 流附加到程序内存中的字符串

c++ - 有和没有模板的功能相同