问题的症结在于:
Matrix
析构函数测试
dane->countingReference
但并没有减少它,所以
丹麦->计数参考
永远不要到达被摧毁的点。A.
dane->countingReference--;
使用调试器进行演练,以逐个找出错误,可以解决这一问题,但是
矩阵
和
CountingReference
令人不安,使簿记变得比需要的困难得多。
一个类应该尽可能少地承担责任,最好是一个。这通常使编写、维护和调试更容易。
矩阵
负责矩阵操作、引用计数和内存管理、降级
计数参考
仅仅是一个容器。
如果
矩阵
,坚持只做矩阵操作,只需编写和测试矩阵操作即可。
如果
计数参考
被分拆为一个同等的班级,负责跟踪与引用计数和内存管理密切相关的职责,
矩阵
可以继续做真正的工作:做数学。让我们看看如何让
计数参考
. 首先,我们利用了C++的基本概念资源分配是初始化或RAII(
What is meant by Resource Acquisition is Initialization (RAII)?
)帮助管理我们的记忆。
class CountingReference
{
private:
int * countingReference;
public:
int wiersz;
int kolumna;
double **wsk;
CountingReference();
CountingReference(int, int);
CountingReference(const CountingReference &);
~CountingReference();
CountingReference& operator=(CountingReference rhs);
};
与原始版本类似,但请注意
countingReference
成员是一个指针以及复制和赋值操作符,用于遵守C++的另一个基本概念,即三的规则(
What is The Rule of Three?
).
计数参考
遵守三条规则允许
矩阵
利用
The Rule of Zero
. 现在,我们将忽略五的规则。
没有必要
CountingReference* detach();
注意,这不是线程安全的。如果您试图在没有
using
<atomic>
如果您正在使用
<原子(&T);
你也可以
use
std::shared_ptr
让整个问题消失。
实施:
CountingReference::CountingReference() :
countingReference(new int(1)), wiersz(0), kolumna(0), wsk(NULL)
{
}
CountingReference::CountingReference(int wier, int kol) :
countingReference(new int(1)), wiersz(wier), kolumna(kol), wsk(new double*[wier])
{
for (int i = 0; i < wier; i++)
{
wsk[i] = new double[kol];
}
cout << countingReference << " was created \n";
}
除了使用
member initializer list
,
计数参考
作为指针,以及
计数参考
从1开始,因为如果引用计数与实例数匹配并销毁0上的共享数据,则更直观。
计数参考
是指针,因为
计数参考
共享数据必须具有相同的引用计数器。基本上不是
矩阵
实例共享指向
计数参考
,他们现在都有自己的
计数参考
共享相同的
计数参考
. 这就是RAII发挥作用的地方,为我们节省了很多工作。
还要注意一些调试语句,以便我知道我是否在给您处理错误代码。我可能仍然在提供糟糕的代码,但如果是这样,至少这个错误不是什么愚蠢的事情。
CountingReference::CountingReference(const CountingReference & src) :
countingReference(src.countingReference), wiersz(src.wiersz), kolumna(src.kolumna), wsk(src.wsk)
{
++(*countingReference);
cout << countingReference << " was copied (" << *countingReference << ")\n";
}
复制构造函数。存储相同的数组和
计数参考
作为源和增量
计数参考
跟踪参考文献的数量。三人法则的第一部分。
CountingReference::~CountingReference()
{
--(*countingReference);
cout << countingReference << " was decremented (" << *countingReference << ")\n";
if (!*countingReference)
{
cout << countingReference << " was destroyed\n";
for (int i = 0; i < wiersz; i++)
{
delete[] wsk[i];
}
delete[] wsk;
delete countingReference;
}
}
析构函数。如果有
计数参考
如果引用计数递减,则超出范围;如果计数现在为0,则释放数组并
计数参考
. 这会自动执行所有清理,因此不会泄漏内存。三人法则的第二部分。
CountingReference & Matrix::CountingReference::operator=(CountingReference rhs)
{
cout << countingReference << " was replaced by " << rhs.countingReference << "\n";
swap(countingReference, rhs.countingReference);
swap(wsk, rhs.wsk);
swap(wiersz, rhs.wiersz);
swap(kolumna, rhs.kolumna);
return *this;
}
赋值运算符。使用
Copy and Swap Idiom
. 这不是执行任务的最有效方式,但它是万无一失的。三人法则的第三部分。
矩阵
最后看起来像
class Matrix
{
private:
class CountingReference
{
private:
int * countingReference;
public:
int wiersz;
int kolumna;
double **wsk;
CountingReference();
CountingReference(int, int);
CountingReference(const CountingReference &);
~CountingReference();
CountingReference& operator=(CountingReference rhs);
};
CountingReference dane;
public:
friend ostream& operator<<(ostream& o, const Matrix&);
friend ostream& operator<<(const Matrix&, ostream& o);
Matrix();
Matrix(int, int);
static Matrix clone(const Matrix &);
Matrix operator+(const Matrix &) const;
Matrix operator-(const Matrix &) const;
Matrix operator*(const Matrix &) const;
Matrix operator+=(const Matrix&);
Matrix& operator-=(const Matrix&);
Matrix& operator*=(const Matrix&);
bool operator ==(const Matrix &);
friend Matrix& Random(Matrix&);
};
不需要自定义析构函数、复制构造函数或赋值运算符,因为矩阵没有需要管理的资源(零规则),所以它们由编译器生成。
我已放弃所有异常说明符(
throw(string)
)因为在我的经验中,它们造成了很多痛苦,却没有什么益处。但别只相信我,
read what Herb Sutter has to say on the topic
. 他通常知道自己在说什么。
函数实现大大简化,因为它们不再需要担心
detach
并应用从sbi的优秀
What are the basic rules and idioms for operator overloading?
Paul Mackenzie在上面的评论中描述了另一个提示,我们最终得到的函数如下所示:
Matrix Matrix::operator+=(const Matrix& Object)
{
if (dane.wiersz != Object.dane.wiersz
|| dane.kolumna != Object.dane.kolumna)
{
string wyjatek = "Nie sa rowne.";
throw wyjatek;
}
else
{
for (int i = 0; i < dane.wiersz; i++)
{
for (int j = 0; j < dane.kolumna; j++)
{
dane.wsk[i][j] += Object.dane.wsk[i][j];
}
}
}
return *this;
}
Matrix Matrix::operator+(const Matrix &Object) const
{
return Matrix.clone(*this) +=Object;
}
我们稍微偏离了标准(
return Matrix(*this) +=Object;
)这里是因为
operator+
不应修改源参数。
矩阵
的复制构造函数产生两个
矩阵
,这将允许
+=
更改原件和副本。这会很糟糕。没有人期望x=10+2将10变成12。
clone
看起来像
Matrix Matrix::clone(const Matrix &Object)
{
Matrix result(Object.dane.wiersz,
Object.dane.kolumna);
for (int i = 0; i < result.dane.wiersz; i++)
{
for (int j = 0; j < result.dane.kolumna; j++)
{
result.dane.wsk[i][j] = Object.dane.wsk[i][j];
}
}
return result;
}
Link to complete code and test case.
我没有检查任何与正确管理引用计数数组无关的程序逻辑。所有矩阵操作逻辑都未经检查,可能是虚假的。
不要将此用于生产代码。有一个非常好的
std::shared\u ptr
在标准库中。