我们从一个分包商处收到代码,主要执行以下操作:
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;
}
在调用 UsesTheCallable::call()
之前,我确保 Callable
对象超出范围,所以我应该调用那时我实际上并不拥有的内存功能。但是代码有效,并且 Valgrind 没有报告内存错误,即使我将一些成员数据放入 Callable
类并使 operator()
函数作用于该成员数据。
我认为此代码是未定义行为是否正确?根据 Callable
是否有成员数据(例如私有(private) int
变量或其他东西),此代码的“定义性”是否有任何差异?
最佳答案
是的,这是未定义的行为。在构造函数 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
,这是不正确的。
关于c++ - 按值传递可调用对象,将其分配给指针成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38774955/