3

对于我的矩阵类,我做了:

template<typename T, std::uint32_t Height, std::uint32_t Width>
class Matrix
{
    private:
        std::array<std::array<T, Width>, Height> Elements;
        static_assert(std::is_arithmetic<T>::value, "Argument T must be of arithmetic type.");

    public:
        Matrix();
        Matrix(T* Data);
        Matrix(T** Data);
        Matrix(T Data[Height][Width]);
        Matrix(const std::array<std::array<T, Width>, Height> &Data);

        inline int size() {return Width * Height;}
        inline const int size() const {return Width * Height;}

        inline int width() {return Width;}
        inline const int width() const {return Width;}

        inline int height() {return Height;}
        inline const int height() const {return Height;}

        std::array<T, Width>& operator[](int Index);
        const std::array<T, Width>& operator[](int Index) const;

        Matrix& operator = (const Matrix &M);

        Matrix& operator + (const Matrix &M);

        Matrix& operator - (const Matrix &M);

        Matrix& operator * (const Matrix &M);
};

template<typename T, std::uint32_t Height, std::uint32_t Width>
Matrix<T, Height, Width>::Matrix() {std::memset(&Elements, 0, sizeof(T) * Width * Height);}

template<typename T, std::uint32_t Height, std::uint32_t Width>
Matrix<T, Height, Width>::Matrix(T* Data) {if (Data) std::memcpy(&Elements, Data, sizeof(T) * Width * Height);}

template<typename T, std::uint32_t Height, std::uint32_t Width>
Matrix<T, Height, Width>::Matrix(T** Data) {if (Data) std::memcpy(&Elements, &Data[0][0], sizeof(T) * Width * Height);}

template<typename T, std::uint32_t Height, std::uint32_t Width>
Matrix<T, Height, Width>::Matrix(T Data[Height][Width]) {std::memcpy(&Elements, &Data[0][0], sizeof(T) * Width * Height);}

template<typename T, std::uint32_t Height, std::uint32_t Width>
Matrix<T, Height, Width>::Matrix(const std::array<std::array<T, Width>, Height> &Data) {std::memcpy(&Elements, &Data[0][0], sizeof(T) * Width * Height);}

template<typename T, std::uint32_t Height, std::uint32_t Width>
std::array<T, Width>& Matrix<T, Height, Width>::operator[](int Index) {return Elements[Index];}

template<typename T, std::uint32_t Height, std::uint32_t Width>
const std::array<T, Width>& Matrix<T, Height, Width>::operator[](int Index) const {return Elements[Index];}

因为我在网上阅读了很多评论,说不要使用向量,而是使用数组或使用 std::valarray..

现在我问的原因是因为我想重写我的矩阵类,这样我就不必继续这样做了:Matrix<Type, Width, Height>每次..我宁愿在构造函数中这样做一次,而不必输入它对于每个功能.. 如上所述。例如,我必须为每个函数和每个 Matrix 参数写出很长的模板声明。另外我不知道如何删除向量的调整大小/后推,以便当用户索引向量时,他们将无法调整它的大小,所以我使用了数组。

我打算使用一维数组并对其进行索引 (I * Width + J) 但后来我失去了我的 [][] 运算符。

使用向量的向量是不是很糟糕?有什么想法可以改善我的课程并使其符合 RAII 标准吗?我不太明白如何使用 valarray 并且上面的维护很烦人。任何想法表示赞赏。

4

3 回答 3

12

确实,向量的向量不是最好的方法。假设数据是矩形的(不是锯齿状的),向量的向量方法完成的分配效率低下,更不用说它阻止了一些常见的“重塑”操作(“将我的 2x3 矩阵视为 3x2 或 6x1复制”)。

我打算使用一维数组并对其进行索引 (I * Width + J) 但后来我失去了我的 [][] 运算符。

当然,使用一维向量。那太棒了。然后,您可以调整它的大小、重塑它等等,并为许多操作提供近乎最佳的性能。你保留RAII。

但是你不想失去双下标([x][y])的能力吗?好的。只需让您operator[]返回一个代理对象(您实现)。代理将拥有自己的代理operator[],它将在第二个轴上运行。也就是说,第一个[]将返回一个轻量级对象,该对象知道足以实现第二个[]

于 2013-06-09T02:13:27.520 回答
0

如果您使用一维数组,您仍然可以实现 [][] 运算符,放弃一点安全性。

假设您的内部数据是 T*(最基本的形式)。您可以将指针返回到行的第一个元素:

template<typename T>
class Matrix
{
    private:
        int height_, width_;
        T* data_;
    public:
        Matrix(int height, int width):
                height_(height),
                width_(width) {
            data_ = new T[height * width];
            std::fill(data_, data_ + height * width, T());
        }
        T* operator[](const int &row) { // i'm returning a pointer to the start of the row
            return data_ + width_ * row;
        }

        const T* operator[](const int &row) const {
            return data_ + width_ * row;
        }
};

int main() {
    Matrix<int> M(5, 5);

    M[3][3] = 1;
    M[3][5] = 2; // this is bad but will work and actually fill the next cell

    std::cout << M[4][0] << std::endl;
}

这将打印 2

于 2013-06-10T10:52:29.843 回答
0

vector<T>用于密集矩阵类的后备存储非常好,但使用 onevector<T>而不是 naive vector<vector<T>>。还要使宽度、高度和行/列主要排序矩阵类的运行时属性。

问题vector<vector<T>>在于它将使用 N 个动态分配,其中 N 是行数或列数。将矩阵存储在一维主要顺序块中更有效,因为它使用 1 个动态分配。

此外,通过使行/列主要排序成为运行时属性,您的转置操作是恒定时间的。

对于您的类上的实际矩阵运算,有一个称为 BLAS 的标准接口,您可以传递操作数和结果实例的后备存储。每个平台都有高度优化的 BLAS 实现,这些实现使用特定 CPU 的非常低级的性能特性,并且比标准 C++ 中可以表达的要快得多。

于 2013-06-09T11:14:27.707 回答