如何在具有动态大小数组的模板类中重载 operator=
Posted
技术标签:
【中文标题】如何在具有动态大小数组的模板类中重载 operator=【英文标题】:How to overload operator= in a templated class with a dynamically sized array 【发布时间】:2013-10-25 01:27:05 【问题描述】:我是使用 C++ 创建自己的模板类的新手,在网上搜索了几个小时的答案并玩弄了函数及其参数后,我放弃了。我在使用以下类“=”运算符时遇到了运行时问题:
在matrix.h中:
template <class datatype> class Matrix
datatype** element;
unsigned int m,n;
public:
Matrix(unsigned int M, unsigned int N,datatype x)
m=M; // # of rows
n=N; // # of cols
element=new datatype*[m];
for(int i=0;i<m;i++) element[i]=new datatype[n];
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
element[i][j]=x;
void print()
for(int i=0;i<m;i++)
for(int j=0;j<n;j++) cout<<element[i][j]<<" ";
cout<<"\n";
Matrix operator=(Matrix A)
for(int i=0;i<m;i++) delete[] element[i];
delete[] element;
m=A.m;
n=A.n;
element=new datatype*[m];
for(int i=0;i<m;i++) element[i]=new datatype[n];
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
element[i][j]=A.element[i][j];
return *this;
;
当我去测试这个时,编译和链接运行顺利,没有错误,我得到一个完全有效的打印。但是当试图将一个矩阵分配给另一个矩阵的值时,程序崩溃并显示消息“matrix_test 已停止工作”。这是我在 matrix_test.cpp 中的测试代码:
Matrix<int> M(5u,3u,0);
Matrix<int> P(2u,7u,3);
int main()
M.print();
cout<<"\n";
P.print();
cout<<"\n";
P=M;
P.print();
提前感谢您的帮助!
【问题讨论】:
您的赋值运算符签名应该类似于Matrix& operator=(const Matrix& A)
。您还需要一个复制构造函数和析构函数。见:What is The Rule of Three?
@Blastfurnace:实际上,我认为签名绝对是正确的!但是,执行不! ;-)
@DietmarKühl:我同意改用Matrix& operator=(Matrix A)
。
@Blastfurnace:好的,我没有注意到Matrix
也是按值返回的:这确实是个坏主意。按值传递参数是非常合理的,因为无论如何都需要副本(请参阅我的回答)。
@Dietmar Kühl:那么我将如何更改实现?我确实已经有一个析构函数和一个复制构造函数,我只是懒得在这里展示它们,认为它们不会影响我的问题的答案。但是实施有什么问题?再次感谢您的帮助!
【参考方案1】:
首先,复制分配的实现在一个相当基本的方面存在缺陷:当您 delete[]
表示然后分配新副本时,分配可能会抛出,在这种情况下您的原始矩阵是 delete[]
d并且无法恢复。因此,分配不是异常安全的。
复制赋值运算符的最佳实现是利用复制构造和swap()
成员。当然,您的课程中缺少这两个成员,但让我们稍后再谈:
Matrix& Matrix::operator= (Matrix other)
other.swap(*this);
return *this;
当按值传递参数时,它实际上是被复制的。要复制对象,您需要一个复制构造函数。通常,如果您需要复制分配,您通常还需要复制构造函数和析构函数(在某些情况下,您只需要复制分配即可使复制分配具有强异常安全性,但这是一个不同的讨论)。
复制构造函数的目的是复制另一个对象,例如,当对象通过值传递时:
Matrix::Matrix(Matrix const& other)
: element(new datatype*[other.m])
, m(other.m)
, n(other.n)
int count(0);
try
for (; count != m; ++count)
this->element[count] = new datatype[m];
std::copy(other.element[count], other.element[count] + m,
this->element[count]);
catch (...)
while (count--)
delete[] this->element[count];
delete[] this->element;
throw;
我不确定从异常中恢复是否真的正确:我无法处理应对所有这些指针的复杂性!在我的代码中,我将确保所有资源立即构造一个专门用于自动释放它们的对象,但这需要更改对象的类型。给定类型的定义,还需要一个析构函数:
Matrix::~Matrix()
for (int count(this->m); count--; )
delete[] this->element[count];
delete[] this->element;
最后,对于较大的对象,swap()
成员通常很方便。 swap()
的目的只是交换两个对象的内容。实现它的方法是做一个会员std::swap()
:
void Matrix::swap(Matrix& other)
using std::swap;
swap(this->element, other.element);
swap(this->n, other.n);
swap(this->m, other.m);
鉴于这个类的所有成员都是内置类型(尽管它们可能不应该是),using
-dance 并不是真正需要的。但是,如果在 std::swap()
之外的其他命名空间中针对用户定义的类型存在专门的 swap()
重载,则上述方法可确保通过参数相关查找找到这些重载。
【讨论】:
谢谢!这绝对有帮助。我没有考虑在按值传递的对象上使用 std::swap 。一开始我实际上是在使用异常处理,但由于我声明(非常)小数组来测试它,我只是想确保基本操作首先工作。以上是关于如何在具有动态大小数组的模板类中重载 operator=的主要内容,如果未能解决你的问题,请参考以下文章
如何管理具有递归函数调用的模板类中的数组(以数组的长度作为参数)?