【问题标题】:Overriding assignment operators in proxy class for vector object in a matrix?覆盖矩阵中向量对象的代理类中的赋值运算符?
【发布时间】:2019-04-16 23:26:23
【问题描述】:

我有一个小问题。我有一个 Matrix 类定义如下(以行为主的形式):

template<typename T>
class Matrix {
    private:
        class RowVector {
            private:
                T *_vec;
                std::size_t _l; 

            public:
                RowVector(T *vec, std::size_t l); 

                const T &operator[](std::size_t index) const;
                T &operator[](std::size_t index);

                operator std::vector<T>() const;
        };

        std::vector<T> _data;
        std::size_t _m; 
        std::size_t _n; 

    public:
        Matrix(std::size_t m, size_t n, const T &elem = T());

        const RowVector operator[](std::size_t index) const;
        RowVector operator[](std::size_t index);

        std::size_t getm() const;
        std::size_t getn() const;
        void fill(const T &elem);
        void fillRow(std::size_t index, const T &elem);
        void fillCol(std::size_t index, const T &elem);

        Matrix &transpose(unsigned int i = 1);

        const std::vector<T> &data() const;
};

并希望重载两个 RowVector 运算符=

typename Matrix<T>::RowVector &operator=(const std::vector<T> &vec);
typename Matrix<T>::RowVector &operator=(const Matrix<T> &mat);

所以我可以使用 A[0] 返回 RowVector &amp; 并使用向量或矩阵重新分配其值。请记住,我(大概)可以忽略三个规则,因为我没有为客户端提供构造 RowVector 对象的明确方法。

但是,在尝试为重载编写函数体时,我遇到了一个问题:

(1) 我无法复制构造一个将在 operator= 范围之外持续存在的向量/矩阵对象,以便我可以将其 data() 分配给 _vec 并将其 size() 分配给 _l

(2) 我不能直接修改_data,因为它不是静态变量;即使可以,我也无法找到索引,因此我可以覆盖封闭 Matrix 对象中的相关内存区域。

你知道有什么方法可以做到吗?这对我的班级来说是两个非常有用的资产。

我希望能够写出这样的东西:

Matrix<int> A(3, 4);
std::vector<int> v {1, 2, 3, 4};
Matrix<int> row(1, 4, 3);
// *****************
A[0] = v;
A[1] = row;
// *****************

(希望我的变量名是不言自明的) 我认为我的原型是正确的,但我就是找不到这样做的方法。

谢谢!

【问题讨论】:

  • 为什么不在行类中使用 std::vector?
  • 忽略三个规则:谁对您的行类中指向的数据拥有所有权(即谁负责删除它)?

标签: c++ oop vector reference operator-overloading


【解决方案1】:
T *_vec;
std::size_t _l;

这是一个有问题的设计!我并不是说它本身是不正确的,但是您需要自己正确管理内存。忽视三(五)规则在这方面是非常危险的。您有一个指向(可能?)动态分配内存的指针,因此必须有 some 实例负责删除它(不一定是您的RowVector,但还有什么?)。

从纯技术方面来看,您甚至可以让 _vec 指向某个向量的数据,只要您保证只要您想访问数据,这个其他向量就存在通过指针——不过,这通常需要相当多的努力。

最安全的方法是让每一行都维护自己的数据,从另一个向量复制(或移动)它。然后最简单的方法是将数据存储在自己的std::vector 中(替换原始指针)。

如果您想避免复制数据,而是在不同矩阵及其行之间共享数据,那么您可以通过 std::shared_ptr 维护数据 - 维护原始数组甚至可能堆分配的std::vector

如果您选择std::vectorstd::shared_ptr,那么复制和移动构造函数和赋值运算符将变得非常简单:

class C
{
public:
    C(C const&) = default;
    C(C&&) = default;
    C& operator= (C const&) = default;
    C& operator= (C&&) = default;
};

所有这些默认值都将根据成员进行复制/移动,std::vectorstd::shared_ptr 都已经有适当的构造函数和运算符可用,所以你会没事的 - 现在你可以 em> 违反五规则,删除析构函数,因为默认的(调用所有成员的析构函数)就足够了。

如果您考虑共享指针:请注意,您不能将std::vector 的数据分配给:std::vector 进行自己的内存管理,您最终会被双重删除,所以在这种特定情况下,你仍然需要创建一个副本。您可能最终会使用多个构造函数和赋值运算符:

std::shared_ptr<std::vector<int>> _data;

// assign shared pointers
RowVector(RowVector const&) = default;
RowVector(RowVector&&) = default;

// need to create copies of: we never know about the scope of the vector passed!
RowVector(std::vector<int> const& data) : _data(new std::vector<int>(data)) { }
RowVector(std::vector<int>&& data) : _data(new std::vector<int>(std::move(data))) { }

// we *are* sharing already -> no need to copy:
RowVector(std::shared_ptr<std::vector<int>& data) : _data(data) { }

赋值运算符类似。

旁注:如果您想要一个数学 nxm 矩阵,那么您肯定不想要一个锯齿状数组。我假设您的 Matrix 类的构造函数已经创建了一个适当的向量向量,然后对于分配,您还没有检查长度:

// defaults not suitable any more!
RowVector& RowVector::operator=(RowVector const& other)
{
    // still assuming shared pointer:
    // (for vector, replace -> with .)
    if(other._data->size() != _data.size())
    {
        throw SomeException();
    }
    _data = other._data;
}
RowVector(RowVector&& other)
{
    if(other._data->size() != _data.size())
    {
        throw SomeException();
    }
    _data = std::move(other._data);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 2016-02-16
    • 2016-04-14
    • 2010-10-13
    • 2014-10-16
    • 2016-12-23
    • 1970-01-01
    相关资源
    最近更新 更多