我们收到了来自分包商的代码,该代码主要执行以下操作:
columns()
这让我觉得它是未定义的代码......它们将class Callable
{
public:
void operator()(int x)
{
printf("x = %d\n", x);
}
};
template<typename T>
class UsesTheCallable
{
public:
UsesTheCallable(T callable) :
m_callable(NULL)
{
m_callable = &callable;
}
~UsesTheCallable() {}
void call() { (*m_callable)(5); }
private:
T* m_callable;
};
的值传递给T
构造函数,然后将UsesTheCallable
成员分配给参数的地址,超出构造函数末尾的范围,所以当我调用m_callable
时,我就会对不再存在的对象进行操作。
所以我尝试了这个主要方法:
UsesTheCallable::call()
在我致电int main(int, char**)
{
UsesTheCallable<Callable>* u = NULL;
{
Callable c;
u = new UsesTheCallable<Callable>(c);
}
u->call();
delete u;
return 0;
}
之前,我确保Callable
对象超出了范围,所以我应调用内存上的函数我不会实际上拥有那个点。但代码有效,Valgrind报告没有内存错误,即使我将一些成员数据放入UsesTheCallable::call()
类并使Callable
函数对该成员数据起作用。
我是否认为此代码是未定义的行为? &#34;定义的&#34;是否有任何区别?此代码是基于operator()
是否包含成员数据(例如私有Callable
变量或其他内容)?
答案 0 :(得分:7)
m_callable = &callable;
我是否认为此代码是未定义的行为?
是的,这是胡说八道,因为你给出的原因。
但是代码可以运行
是的,嗯,这就是UB发生的事情......
和Valgrind报告没有内存错误
...特别是当您正在操作的内存仍属于您的进程时。 Valgrind没有在这里发现任何东西;它不验证C ++范围,只验证“物理”†内存访问。程序不会崩溃,因为没有什么机会可以破坏c
过去常常占用的内存。
†“物理”,我指的是操作系统及其内存管理,而不是C ++的抽象概念。它实际上可能是虚拟内存或其他任何内容。
答案 1 :(得分:7)
是的,这是未定义的行为。在构造函数callable
的右大括号被破坏并且你有一个悬空指针之后。
您没有看到不利影响的原因是您在超出范围后确实没有使用该实例。函数调用操作符是无状态的,因此它不会尝试访问它不再拥有的内存。
如果我们将一些状态添加到可调用状态,如
class Callable
{
int foo;
public:
Callable (int foo = 20) : foo(foo) {}
void operator()(int x)
{
printf("x = %d\n", x*foo);
}
};
然后我们使用
int main()
{
UsesTheCallable<Callable>* u = NULL;
{
Callable c(50);
u = new UsesTheCallable<Callable>(c);
}
u->call();
delete u;
return 0;
}
然后你可以看到这个bad behavior。在该运行中,它输出x = 772773112
,这是不正确的。