【问题标题】:Value Passing, Exceptions/Asserts, and Class Design. Critique/Questions值传递、异常/断言和类设计。批评/问题
【发布时间】:2019-01-03 21:34:25
【问题描述】:

我正在构建一个模板矩阵类,用于我未来的 c++ 代码。关于重载运算符的值传递、异常与断言以及通用类设计,我有几个问题。

  1. 我是否正确传递了这些值?它有效率吗?我还能做些什么来让它变得更好?

  2. 此库的构建考虑了未来的应用程序设计(终端或 gui),用户可以在其中定义自己的矩阵并运行计算。在这种情况下使用异常而不是断言会更好吗?

  3. 我查看了 c++ 的 5 规则,其中指出:

    因为用户定义的析构函数复制构造函数复制赋值操作符的存在阻止了隐式定义移动构造函数和移动赋值运算符,任何需要移动语义的类,都必须声明所有五个特殊成员函数。

    我可以通过不使用这三个规则中的任何一个而侥幸逃脱吗?** 使这个类更实用的标准方法是什么?

我在我的程序中定义了减法、乘法和除法(标量),其结构与提供的加法运算符定义相同/相似,因此此处不需要所有代码。

接受对整体设计的任何硬性建议或批评!

#ifndef MACMATRICES_H
#define MACMATRICES_H

#include <vector>
#include <iomanip>
#include <iostream>
#include <exception>
#include "../../DMF-Terminal.h"


namespace DMF
{
    template <typename T>
    class matrix
    {
    public:

    // Constructors 
    matrix();
    matrix(int p_rows, int p_columns);

    // Operators
    std::vector<T>& operator[] (size_t i) { return m[i]; }
    matrix<T> operator+(const matrix<T>& rhs);
    matrix<T> operator+(const T& rhs);
    matrix<T>& operator+=(const matrix<T>& rhs);
    matrix<T>& operator+=(const T& rhs);


    // Class Methods 
    void print() const;
    matrix<T> inverse();
    T determinant();

    // Observers 
    bool isSquare() const;
    int rowSize() const { return m_rows; } 
    int colSize() const { return m_cols; } 

private:

    int m_rows, m_cols;
    std::vector< std::vector<T> > m;
};

/* 构造函数 -------------------------------------------- ----------------------------------------------------*/

template <typename T>
matrix<T>::matrix(){}

template <typename T>
matrix<T>::matrix(int p_rows, int p_cols) :
    m(p_rows, std::vector<T>(p_cols)), m_rows(p_rows), m_cols(p_cols) {}

/* 加法 -------------------------------------------- -------------------------------------------------------*/

template <typename T>
matrix<T> matrix<T>::operator+(const matrix<T>& rhs)
{
    try
    {
        if((this->rowSize() == rhs.rowSize()) && (this->colSize() == rhs.colSize()))
        {
            matrix<T> sum (this->rowSize(), this->colSize()); 
            for(int i = 0; i < this->rowSize() ; ++i)
            {
                for(int j = 0; j < this->colSize(); ++j)
                    sum.m[i][j] = this->m[i][j] + rhs.m[i][j];
            }
            return sum; 
        }
        else throw std::runtime_error("Cannot add matrices, invalid row/column sizes."); 
    }
    catch (std::exception &e)
    {
        std::cout << "Error: " << e.what(); DMF::wait();
    }
}

template <typename T>
matrix<T> matrix<T>::operator+(const T& rhs)
{
    matrix<T> sum (this->rowSize(), this->colSize()); 
    for(int i = 0; i < this->rowSize() ; ++i)
    {
        for(int j = 0; j < this->colSize(); ++j)
            sum.m[i][j] = this->m[i][j] + rhs;
    }
    return sum; 
}

template <typename T>
matrix<T>& matrix<T>::operator+=(const matrix<T>& rhs)
{
    try
    {
        if((this->rowSize() == rhs.rowSize()) && (this->colSize() == rhs.colSize()))
        {
            for(int i = 0; i < this->rowSize() ; ++i)
            {
                for(int j = 0; j < this->colSize(); ++j)
                    this->m[i][j] += rhs.m[i][j];
            }
            return *this; 
        }
        else throw std::runtime_error("Cannot add matrices, invalid row/column sizes."); 
    }
    catch (std::exception &e)
    {
        std::cout << "Error: " << e.what(); DMF::wait();
    }
}

template <typename T>
matrix<T>& matrix<T>::operator+=(const T& rhs)
{
    matrix<T> sum (this->rowSize(), this->colSize()); 
    for(int i = 0; i < this->rowSize() ; ++i)
    {
        for(int j = 0; j < this->colSize(); ++j)
            this->m[i][j] += rhs;
    }
    return *this; 
}
}
#endif /* MACMATRICES_H */

截至目前,此代码可在迷你终端程序中运行。我还重载了 matrix * matrix 和 matrix *= matrix 运算符,它似乎工作正常,结果矩阵大小正确。

【问题讨论】:

  • 这很有意义。我只是在最后一秒把它们扔进去了。谢谢!
  • 为了性能,不要使用向量的向量。使用带有成员函数的单个内存块来访问它。并在尝试将其转换为模板之前将其编写为int 的非模板类。
  • 您忘记在默认构造函数中初始化所有成员。但是,存在一个空矩阵是否有意义?
  • @molbdnilo 默认构造函数用于测试我的乘法运算符的赋值。我猜我真的不需要它,但是我想像使用 std::vector 一样使用列表初始化来实现构造函数。最简单的方法是什么?
  • @NeilButterworth 我希望我可以在我的代码中进一步使用 std::vector 成员函数。您的意思是我应该将矩阵作为动态分配的二维数组吗?

标签: c++ class templates matrix operator-overloading


【解决方案1】:

我是否正确传递了值?它有效率吗?我还能做些什么来让它变得更好?

你通过 (const) 引用传递你的矩阵,所以你避免复制,所以很好。

您在operator+= 中有一些“错字”作为变量,用作matrix&lt;T&gt; sum

你复制了一些信息,std::vector 知道它的大小。

std::vector&lt;std::vector&lt;T&gt;&gt; 线性化为std::vector&lt;T&gt;&gt; 对缓存更友好,但需要一个代理类(带有另一个operator[])来处理operator[],或者您可以使用operator()(int, int) 或@987654329 等访问器来代替@。

这个库的构建考虑了未来的应用程序设计(终端或 gui),用户可以在其中定义自己的矩阵并运行计算。在这种情况下使用异常而不是断言会更好吗?

异常是将错误传达给用户,但目前,您直接捕获它以通过一些日志忽略错误......因此,您可以直接记录当前的错误,而不是抛出。

有几个可以解决这个问题:

  • 在模板参数中有矩阵大小并使用类型系统在编译时检查这些错误。不过需要在编译时知道大小。

  • 如果您认为用户可能能够忽略/从错误中恢复,那么让我们传播异常。您可能有专门的例外。

  • 如果您认为用户可能无法忽略错误/从错误中恢复,那么assert/terminate/UB 是可能的方式。

我查看了 C++ 的 5 规则,其中指出:“因为用户定义的析构函数、复制构造函数或复制赋值运算符的存在阻止了移动构造函数和移动赋值的隐式定义运算符,任何需要移动语义的类,都必须声明所有五个特殊成员函数。”我可以通过没有这三个中的任何一个来逃避不执行这条规则吗?使这个类更实用的标准方法是什么?

3 的规则有变体:

  • 规则 5 还包括移动构造函数和移动赋值。
  • 0 规则,所有成员都对 RAII 友好,因此默认实现很好。

使用 std::vector 允许使用 0 规则 :-) 所以你很好。

【讨论】:

  • 非常感谢您的回复!在接下来的几天里,我将编辑我的整体课程设计。等我清理干净后,你能不能再快速看一下?
猜你喜欢
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
相关资源
最近更新 更多