仅允许修改稀疏矩阵的非零元素
Allow modification of only non-zero elements of a sparse matrix
我正在实现一个三对角矩阵,我必须尽可能高效。显然,我只会保存包含数据的元素。我重载了operator()
以充当矩阵中的索引器,但我希望此运算符返回引用,以便用户可以修改矩阵。但是,我不能只return 0;
非三对角线元素,因为零不是参考。如何让用户修改三对角线上的数据,但当operator()
用于检查非三对角线元素时,仅返回 0 而不是对 0 的引用?
以下是相关的类定义
template <class T>
class tridiagonal
{
public:
tridiagonal();
~tridiagonal();
T& operator()(int i, int j);
const T& operator()(int i, int j) const;
private:
//holds data of just the diagonals
T * m_upper;
T * m_main;
T * m_lower;
};
的一个技巧是让non-const operator()(int, int)方法返回一个小的辅助对象。帮助程序用于区分赋值到矩阵中和仅提取值。这使您对这两个操作具有不同的行为。特别是,如果有人试图将值分配给必须为零的值,则可以抛出。
这段代码至少在 VC10 中为我编译,但显然没有链接。
template <class T>
class tridiagonal
{
public:
// Helper class that let's us tell when the user is
// assigning into the matrix and when they are just
// getting values.
class helper
{
tridiagonal<T> &m_parent;
int m_i, m_j;
public:
helper(tridiagonal<T> &parent, int i, int j)
: m_parent(parent), m_i(i), m_j(j)
{}
// Converts the helper class to the underlying
// matrix value. This doesn't allow assignment.
operator const T & () const {
// Just call the const operator()
const tridiagonal<T> &constParent = m_parent;
return constParent(m_i, m_j);
}
// Assign a value into the matrix.
// This is only called for assignment.
const T & operator= (const T &newVal) {
// If we are pointing off the diagonal, throw
if (abs(m_i - m_j) > 1) {
throw std::exception("Tried to assign to a const matrix element");
}
return m_parent.assign(m_i, m_j, newVal);
}
};
tridiagonal();
~tridiagonal();
helper operator()(int i, int j)
{
return helper(*this, i,j);
}
const T& operator()(int i, int j) const;
private:
T& assign(int i, int j, const T &newVal);
//holds data of just the diagonals
T * m_upper;
T * m_main;
T * m_lower;
};
int main(int argc, const char * argv[])
{
tridiagonal<double> mat;
std::cout << mat(0,0) << std::endl;
const tridiagonal<double> & constMat = mat;
std::cout << mat(2,3) << std::endl;
// Compiles and works
mat(2,3) = 10.0;
// Compiles, but throws at runtime
mat(1, 5) = 20.0;
// Doesn't compile
// constMat(3,3) = 12.0;
return 0;
}
自从我这样做以来已经有一段时间了,所以你可能会发现你需要向帮助程序类添加更多的东西,这取决于你如何使用矩阵。
实际上,解决这个问题是一个很好的C++练习。 :)
您在这里遇到的问题是界面不合适。如果你对矩阵的定义是一个二维数字数组,使得矩阵的每个元素都可以单独设置,那么稀疏的三诊断矩阵矛盾地不是矩阵(就像正方形不是一个可修改的矩形 - 一个不遵守Liskov替换原则的不适当继承的典型例子)。
简而言之,您最好更改界面以适应稀疏的三对角矩阵,而不是试图破解它以使用您拥有的界面。也就是说,如果你必须这样做,那么你最好做两件事:
- 修改您的
const
访问器以返回T
而不是const T&
(我假设我们在这里只处理数字矩阵)。然后,您可以为对角线以外的元素返回0
。 - 修改非
const
访问器以返回对虚拟元素的引用,以查找对角线以外的位置,并将手指交叉:)或者,在这种情况下,您可以将规范更改为throw
,但这可能有点不友好。
另一种替代方法(无需正确重新设计接口)可能是返回代理对象而不是 T
s。虚拟元素的代理将在您尝试使用它设置值时throw
。
通过引用返回需要返回指定类型的有效对象。完成所需操作的最简单方法是保留表示 0 的静态T
对象,并改为返回它。
或者,您可以返回指针。
只需添加一个代表某个虚拟值的额外成员,并确保它始终显示为 0。
template<typename T>
class tridiagonal
{
// usual stuff...
T& operator() (int j, int j)
{
// if not explicitly stored, reset to default before returning.
return stored(i,j)? fetch(i,j) : (m_dummy=T());
}
private:
// dummy element used to "reference" elements outside the 3 diagonals.
T m_dummy;
// check if (i,j) is on 3 diagonals.
bool stored (int i, int j) const;
// access element on 3 diagonals. precondition: stored(i,j)==true.
T& fetch (int i, int j);
//holds data of just the diagonals
T * m_upper;
T * m_main;
T * m_lower;
};
请注意,从技术上讲,有人可能会这样欺骗您:
tridiagonal<int> m(4,4);
T * dummy = &m(3,0); // *dummy == 0.
*dummy = 1; // *dummy == 1.
std::cout << *dummy; // prints 1.
但这不一定是问题。
- 如何将字节数组元素替换为修改的十六进制 ASCII 符号?
- C++ std::vector语言 - 如何修改迭代器指定的元素?
- 打印/修改类对象的特定成员变量,其类定义列表 (STL) 包含的元素类型
- 如何修改所有文本元素;Visual Studio c++
- C++ 当容器在使用前被破坏/修改时发出警告(通过引用元素或迭代器使用)
- 如何使用初始化列表循环修改元素
- 为什么基于范围的循环不修改容器元素?
- 通过const_cast修改常量 std::vector 的元素<T>
- 修改 std::D efined 行为的元素?
- 从equal_range查询中筛选和修改 boost::multi_index 中的元素
- 如何转换一个类方法以修改另一个类的私有元素?
- 如何使用用户输入正确修改 2D 数组中的字符数组元素?专门用于电影院座位
- Armadillo C :如何使用来自另一个矩阵的多个元素(特别是在立方体结构中)修改矩阵的多个数组元素
- 如何在不知道大小的情况下编写过程来修改动态数组的元素和大小
- 如何修改 Kadane 算法以找出贡献最大总和的子数组元素?
- 如何在不修改现有函数的情况下反转数组中除第一个和最后一个元素之外的所有元素
- 使用特征元素修改零表达的值
- 在 c++ 中修改字符串向量的元素
- 迭代一个巨大的 std::vector of std::vectors 并修改元素
- 随机修改二维数组元素