按值传递可调用对象,将其分配给指针成员

时间:2016-08-04 18:35:18

标签: c++

我们收到了来自分包商的代码,该代码主要执行以下操作:

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变量或其他内容)?

2 个答案:

答案 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,这是不正确的。