c++运算符=工作异常(函数内部输出正常,但返回后错误)

c++ operator = working weird (output ok inside the function, but wrong after return..)

本文关键字:返回 错误 输出 工作 运算符 异常 内部 函数 c++      更新时间:2023-10-16

我有一个程序,我一直在修改它来提取一些数据
最近我又进行了一次数据提取,由于时间不够,我使用了变通方法
现在,我正试图找出问题出在哪里,因为我有一些空闲时间。

下面有一个函数Act(),它是类Mat的成员函数,它用更改的数据值形成了一个新的矩阵。Mat有一个指向包含实际数据的缓冲区的指针。我发现在返回矩阵的Act()函数之后,会调用"operator="函数。(没有"operator="函数,所以我添加了它。可能使用了默认的"=operator"。无论是否添加"operator="函数,它都不会产生最终的右输出。)这是"="运算符返回数据的部分(类型Mat)。(这是一个c++代码,但我只是使用了printf,因为它无论如何都能工作)

template<typename T>
Mat<T> Mat<T>::operator = (const Mat<T>& rhs)
{
Mat<T> result(rhs.row_, rhs.col_);
int num_bytes = sizeof(T) * rhs.row_ * rhs.col_;
printf("ope = , res val = %x, src val = %x, row = %d, col = %dn", result.val_, rhs.val_, rhs.row_, rhs.col_);
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)rhs.val_)[i].data_);
}
printf("n");
memcpy(result.val_, rhs.val_, num_bytes);
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)result.val_)[i].data_);
}
printf("n");
return result;
}

ope=,res val=12199b0,src val=c07680,行=128,列=128
3be0 bbfc bbf5 3bf8 3af0 bbf6 bbef b29f bbaf 3bfd
3 be0 bbfcbbf5 3 bf8 3af 0 bbf6 bbef b29 f bbaf 3 bfd

这个"3be0 bbfc bbf5…"是正确的模式。

调用部分看起来是这样的。

printf("calling activation..n");
d0_out = d0_out.Act(NN_CELL::AT_TANH);
std::cout << "MLP Layer 0 executed." << std::endl;
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)d0_out.val_)[i].data_);
}
printf("n");

但在赋值之后,从主函数中可以看出,数据很奇怪(在=运算符内部,复制似乎是正确的,但在外部,它不是)。

MLP层0已执行
40de c2e2 c1eb 425e 3d4a c21b c187 b2b8 bfce 4358

正如你所看到的,数据是错误的
我能产生正确输出的唯一方法是使用一个新对象并将Act()函数的结果分配给它。

NN_CELL::Mat<T> d0_out1 = d0_out.Act(NN_CELL::AT_TANH); // tmp work-around  

然后,d0_out1(新对象)包含正确的数据(3be0 bbfc bbf5..),而不是原始

d0_out = d0_out.Act(NN_CELL::AT_TANH);   

原始代码出了什么问题?

a = a.Act()不起作用,因为您错误地实现了operator=。默认的operator=在这种情况下不起作用,因为类中有一个指针,当Act()返回的临时指针被销毁时,它可能会使指针无效。

调用operator=,因为您在代码中编写了=。当您使用"tmp解决方案"时,您将创建一个新对象,该对象不调用operator=,而是调用复制构造函数。这似乎奏效了。

至于如何正确实现operator=:赋值运算符应该修改现有对象,而不是返回新对象。

例如:

struct A {
int row_, col_;
float *val_;
A& operator=(const A& rhs) {
if (this != &rhs) {
const int num_bytes = sizeof(float) * rhs.row_ * rhs.col_;
row_ = rhs.row_;
col_ = rhs.col_;
memcpy(val_, rhs.val_, num_bytes);
}
return *this;
}
};

operator=应该修改*this,而不是创建一个新的时态对象X,将接收到的数据复制到X,然后返回X。您已经创建了一个时态对象,但运行operator=的类的数据尚未修改!

试试这个代码:

template<typename T>
Mat<T> Mat<T>::operator = (const Mat<T>& rhs)
{
int num_bytes = sizeof(T) * rhs.row_ * rhs.col_;
printf("ope = , res val = %x, src val = %x, row = %d, col = %dn", this->val_, rhs.val_, rhs.row_, rhs.col_);
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)rhs.val_)[i].data_);
}
printf("n");
memcpy(this->val_, rhs.val_, num_bytes);
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)this->val_)[i].data_);
}
printf("n");
return *this;
}

由于注释状态正确,Assignment Operator用于修改已存在的对象。你正在创建一个新的并返回。

试试这样的东西:

template<typename T>
Mat<T>& Mat<T>::operator = (const Mat<T>& rhs)
{
row_ = rhs.row_;
col_ = rhs.col_;
delete val_;
val_ = new T[row_*col_];
int num_bytes = sizeof(T) * rhs.row_ * rhs.col_;
printf("ope = , res val = %x, src val = %x, row = %d, col = %dn", val_, rhs.val_, rhs.row_, rhs.col_);
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)rhs.val_)[i].data_);
}
printf("n");
memcpy(val_, rhs.val_, num_bytes);
for(int i=0;i<10;i++){
printf("%04x ",((half_float::half *)val_)[i].data_);
}
printf("n");
return *this;
}