在opencv中,我碰巧修改了一个传递给我的函数的变量:
void someFunction(const cv::Mat matrix)
{
double value = 5.0;
matrix /= value;
}
不仅可以编译,而且在调用someFunction(matrix)
后矩阵仍会被修改,即使我没有通过matrix
作为参考。我对此代码进行编译的解释是,我实际上并没有更改matrix
中someFunction
的任何成员,而是data
的指针matrix
指向的地址内容,但我不太确定。
由于我有点困惑,我试图在一个小程序中重现这种情况:
#include <iostream>
using namespace std;
class Foo;
{
private:
int* ptr;
public:
Foo()
{
ptr = new int[1];
ptr[0] = 1;
}
const Foo& operator += ( const Foo& obj) const
{
this->ptr[0] += obj.ptr[0];
return *this;
}
};
void changeConst(const Foo var)
{
Foo foo;
var += foo;
}
int main()
{
Foo obj;
changeConst(obj);
return 0;
}
事实证明,该程序会编译并有效地更改ptr[0]
的{{1}}。我发现如果我以这种方式声明运算符obj
:
+=
它无法编译。为什么是这样?这一发现在我身上产生了许多问题。有没有办法防止函数中指针内容的改变?返回常量引用(Foo& operator += ( const Foo& obj)
)的函数的含义是什么?声明函数const Foo&
时应该注意什么?简而言之,我应该如何在函数声明中使用const
?
答案 0 :(得分:5)
从签名中删除const
会导致编译错误,因为您无法从const
实例调用非const成员函数。
const
版本编译并且似乎违反直觉的原因是您正在修改Mat
指向的对象(假设您的意思是/=
)或{{ 1}} object,但你没有修改数据成员本身,即指针。
设计表现出这种行为的类型是否有意义是另一回事。就个人而言,我会说允许通过Foo
修改const cv::Mat
违反了principle of least astonishment。 operator /=
似乎实现了引用语义(它本质上是一个标头和指向数据块的指针),但它可以实现为提供合理的cv::Mat
正确性。它不会通过我参与的任何代码审查。
答案 1 :(得分:4)
根据文档,cv::Mat
是关于矩阵的元数据,实际数据是共享的和引用计数的,如std::shared_ptr
因此const cv::Mat
与const std::shared_ptr<double>
类似 - 您无法将其绑定到新位置,但您可以修改指向的数据。
你正在寻找相当于std::shared_ptr<const double>
的东西,在阅读the documentation后,看起来并不存在任何这样的东西。更奇怪的是,可以将“只读”InputArray
参数转换为Mat
智能指针。因此,要么创建禁止修改的Mat
智能指针的秘密方法,要么InputArray
根本不是只读。
正如juanchopanza所说,这应该没有进行设计审查。提供具有共享数据所有权的智能指针类都很好,但是:
MPtr
像std::shared_ptr
一样工作,那么没有人会感到困惑。InputArray
到Mat
的转换证明了这一点。答案 2 :(得分:0)
const Foo& operator += ( const Foo& obj) const
{
this->ptr[0] += obj.ptr[0];
return *this;
}
你可以告诉,这个操作员:
1)将const引用返回给Foo;
2)接受对Foo的const引用;
3)不会更改声明的类中的任何成员。
这是合法的,因为在这里你改变了对象,ptr指向,但不是ptr本身(它仍然指向同一个位置)。传递给changeConst函数的值可以被+ =运算符接受。这些函数不会改变Foo类中的任何内容,因为它只有一个指针。但是,指针指向IS的数据已更改。
如果您在努力了解const的工作原理,我建议您观看this视频。