这可能只是我在某种程度上是愚蠢的,但我对C++相对较新,所以请原谅我白痴.我正在努力教自己如何使用运算符.我已经定义了一个非常基本的运算符如下:
Matrix::Matrix operator+(Matrix a1, Matrix a2) {
if(a1.rows != a2.rows || a1.cols != a2.cols) {
std::cout << "ERROR: Attempting to add matrices of non-equal size." << std::endl;
exit(6);
}
int i, j;
Matrix c(a1.rows, a1.cols);
for(i = 0; i < a1.rows; i++)
for(j = 0; j < a1.cols; j++)
c.val[i][j] = a1.val[i][j] + a2.val[i][j];
return c;
}
Run Code Online (Sandbox Code Playgroud)
Matrix类表示一个矩阵,并且有一个构造函数,它将两个int作为输入(分别是矩阵中的行数和列数),并创建一个具有适当大小的双精度数组(名为val).此函数的作用应该是因为c的值是正确的,但它似乎也会破坏a1和a2.也就是说,如果我写的话
Matrix c = a + b;
Run Code Online (Sandbox Code Playgroud)
它给出了正确的结果,但是a和b不再可用,并且在代码结束时我得到一个glibc错误声称我试图在它们已经被破坏时破坏a和b.为什么是这样?
你的签名是错的:
Matrix operator+(Matrix a1, Matrix a2)
Run Code Online (Sandbox Code Playgroud)
它应该是
Matrix operator+(const Matrix& a1, const Matrix& a2)
Run Code Online (Sandbox Code Playgroud)
这似乎破坏的原因a1和a2是因为,它是,因为这些是在方法范围内创建的临时副本.
如果原始值被破坏,您可能违反了三条规则.当a1和a2被销毁时,析构函数被调用,你可能正在删除析构函数中的指针.但由于默认的拷贝构造函数只做一个浅拷贝,复制a2,并a1会删除原始的记忆.
假设:
struct A
{
int* x;
A() { x = new int; *x = 1; }
~A() { delete x; }
};
//option 1:
A operator + (A a1, A a2)
{
A a; return a; //whatever, we don't care about the return value
}
//option 2:
A operator + (const A& a1, const A& a2)
{
A a; return a; //again, we don't really care about the return value
}
Run Code Online (Sandbox Code Playgroud)
在第一个示例中,未实现复制构造函数.
复制构造函数由编译器生成.此复制构造函数将复制x到新实例中.所以如果你有:
A a;
A b = a;
assert( a.x == b.x );
Run Code Online (Sandbox Code Playgroud)
重要提示,指针是相同的.
调用option 1将在内部创建副本operator +,因为值是按值传递的:
A a;
A b;
a + b;
//will call:
A operator + (A a1, A a2)
// a1.x == a.x
// a2.x == n.x
Run Code Online (Sandbox Code Playgroud)
当operator +退出时,它会调用delete x的对象a1和a2,这将删除也指向内存a.x和b.x.这就是你得到内存损坏的原因.
调用option 2,但是由于因为你按引用传递没有创建新的对象,内存不会被函数返回时被删除.
但是,这不是解决问题的最简洁方法.它解决了这个问题,但潜在的问题更为重要,正如康拉德指出的那样,我的原始答案(尽管没有给予足够的重视,但我承认).
现在,解决这个问题的正确方法是遵循三条规则.也就是说,有一个实现destructor,copy constructor和assignment operator:
struct A
{
int* x;
A() { x = new int; *x = 1; }
A(const A& other) //copy constructor
{
x = new int; // this.x now points to a different memory location than other.x
*x = other.(*x); //copy the value though
}
A& operator = (const A& other) //assignment operator
{
delete x; //clear the previous memory
x = new int;
*x = other.(*x); //same as before
}
~A() { delete x; }
};
Run Code Online (Sandbox Code Playgroud)
使用这个新代码,让我们重新运行option 1之前的问题:
A a;
A b;
a + b;
//will call:
A operator + (A a1, A a2)
// a1.x != a.x
// a2.x != n.x
Run Code Online (Sandbox Code Playgroud)
由于副本a1和a2现在指向不同的存储单元,它们被破坏时,原有的记忆是完好的原始对象- a和b仍然有效.
唷!希望这可以解决问题.
我假设您正在Matrix使用类内部分配动态内存,new并且没有实现自定义复制构造函数,因此违反了三条规则.
然后,错误的原因是Matrix实例的本地副本重用参数实例的动态内存,并且它们的析构函数在方法结束时释放它.
解决方案非常简单:不要使用指针.相反,您可以使用嵌套std::vector来存储数据.
此外,操作员的头部受损.根据您声明函数的位置,如果您在类中定义运算符,它必须看起来像这样:
ReturnType operator +(Arg1)
Run Code Online (Sandbox Code Playgroud)
或者,如果您在类之外定义它,它需要如下所示:
ReturnType operator +(Arg1, Arg2)
Run Code Online (Sandbox Code Playgroud)
你的两者都是混合的.我假设您的运算符定义应如下所示:
Matrix operator +(Matrix a, Matrix b) { … }
Run Code Online (Sandbox Code Playgroud)
如果您的代码编译然后这是编译器中的错误或您确实使用非常奇怪的类结构.此外,正如Luchian指出的那样,传递参数const&而不是价值更有效.但是,您应首先使代码正确运行.