c++ 相同的代码 compiles/runs 从不在 Visual Studio 中,有时在 Qt Creator 中
c++ same code compiles/runs never in Visual Studio and sometimes in Qt Creator
当我注意到以下问题时,我正在做一些 C++ 练习。
给定的代码不会 run/compile 在 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[]
总是会重现问题。
为什么会这样?
matrix.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
matrix.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]);
}
将return引用列中的第一个元素而不是列。因此,调用 test[0][0] = 2;
会尝试将 []
运算符应用于双精度数,而不是双精度数组。
快速解决方案:
double * & Matrix::operator[](size_t index)
{
return m_elements[index];
}
此 return 是对指针的引用(请继续阅读以了解我为什么要打扰该引用),您可以在 returned 指针上使用 [] 来获取数据元素.
但是...
有更好的方法可以做到这一点。
如果可能,请使用 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;
如果你必须有[][]
[] 运算符的建议形式 return 是对索引数据的引用,在本例中是向量或指向行数组的指针。
std::vector<double> & Matrix::operator[](size_t index)
{
return m_elements[index];
}
或
double * & Matrix::operator[](size_t index)
数组和向量的内部结构相同。
警告:这允许用户使用 returned 向量或指针引用遇到各种麻烦。例如考虑 matrix[0].clear();
或 matrix[0] = NULL;
。
double * Matrix::operator[](size_t index)
将通过 return 指针的副本来防止大多数滥用。不幸的是,这不能保护向量,因为向量的副本将是与源内容副本完全不同的向量。更新它并期望持久性是徒劳的。该矢量必须在包装器 class 内对用户隐藏,而这很快就会变得太多工作。
此外,return使用副本或包装器也会阻止引用的合法使用并违反最小意外法则:Matrix [] 运算符与其他 [] 运算符的工作方式不同如果毫无戒心的编码人员将其用作常规 [] 运算符,则可能会导致意外行为。
我的意见是 return 未受保护的引用,如果使用 Matrix 的人 class 想朝自己的脑袋开枪......好吧,你只能做这么多。如果必须保护用户,请使用上述 at
方法或 operator()
方法。
Const[] 运算符与向量相似
std::vector<double> const & Matrix::operator[](size_t index) const
但数组不同,因为指针和指向的值都应该是 const
double const * const & Matrix::operator[](size_t index) const
我建议的实施方式:
Matrix.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
Matrix.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");
}
只是因为 Return.
的含义
当你return引用&
时,你可以随心所欲地改变它,比如test[2][2] = 2;
因为它会回到它曾经引用的来源
当你 return 按值,它只是一个临时对象,使用后将被销毁,这意味着你无法更改它!
当我注意到以下问题时,我正在做一些 C++ 练习。 给定的代码不会 run/compile 在 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[]
总是会重现问题。
为什么会这样?
matrix.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
matrix.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]);
}
将return引用列中的第一个元素而不是列。因此,调用 test[0][0] = 2;
会尝试将 []
运算符应用于双精度数,而不是双精度数组。
快速解决方案:
double * & Matrix::operator[](size_t index)
{
return m_elements[index];
}
此 return 是对指针的引用(请继续阅读以了解我为什么要打扰该引用),您可以在 returned 指针上使用 [] 来获取数据元素.
但是...
有更好的方法可以做到这一点。
如果可能,请使用 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;
如果你必须有[][]
[] 运算符的建议形式 return 是对索引数据的引用,在本例中是向量或指向行数组的指针。
std::vector<double> & Matrix::operator[](size_t index)
{
return m_elements[index];
}
或
double * & Matrix::operator[](size_t index)
数组和向量的内部结构相同。
警告:这允许用户使用 returned 向量或指针引用遇到各种麻烦。例如考虑 matrix[0].clear();
或 matrix[0] = NULL;
。
double * Matrix::operator[](size_t index)
将通过 return 指针的副本来防止大多数滥用。不幸的是,这不能保护向量,因为向量的副本将是与源内容副本完全不同的向量。更新它并期望持久性是徒劳的。该矢量必须在包装器 class 内对用户隐藏,而这很快就会变得太多工作。
此外,return使用副本或包装器也会阻止引用的合法使用并违反最小意外法则:Matrix [] 运算符与其他 [] 运算符的工作方式不同如果毫无戒心的编码人员将其用作常规 [] 运算符,则可能会导致意外行为。
我的意见是 return 未受保护的引用,如果使用 Matrix 的人 class 想朝自己的脑袋开枪......好吧,你只能做这么多。如果必须保护用户,请使用上述 at
方法或 operator()
方法。
Const[] 运算符与向量相似
std::vector<double> const & Matrix::operator[](size_t index) const
但数组不同,因为指针和指向的值都应该是 const
double const * const & Matrix::operator[](size_t index) const
我建议的实施方式:
Matrix.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
Matrix.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");
}
只是因为 Return.
的含义当你return引用&
时,你可以随心所欲地改变它,比如test[2][2] = 2;
因为它会回到它曾经引用的来源
当你 return 按值,它只是一个临时对象,使用后将被销毁,这意味着你无法更改它!