按值传递可调用对象,将其分配给指针成员
Pass callable object by value, assign it to pointer member
我们从一个分包商那里收到了代码,该分包商主要执行以下操作:
class Callable
{
public:
void operator()(int x)
{
printf("x = %dn", 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
是否具有成员数据(例如,私有int
变量或其他什么),此代码的"定义性"是否存在差异?
是的,这是未定义的行为。构造函数callable
的右大括号被破坏后,您就有了一个悬空指针。
您没有看到不良影响的原因是,在实例超出范围后,您确实没有使用实例。函数调用操作符是无状态的,因此它不会试图访问它不再拥有的内存。
如果我们像一样向可调用对象添加一些状态
class Callable
{
int foo;
public:
Callable (int foo = 20) : foo(foo) {}
void operator()(int x)
{
printf("x = %dn", x*foo);
}
};
然后我们使用
int main()
{
UsesTheCallable<Callable>* u = NULL;
{
Callable c(50);
u = new UsesTheCallable<Callable>(c);
}
u->call();
delete u;
return 0;
}
然后你就会看到这种恶劣的行为。在该运行中,它输出不正确的x = 772773112
。
m_callable = &callable;
我认为这个代码是未定义的行为,这是正确的吗?
是的,这太夸张了!t、 因为你给出的理由。
但是这个代码是
是的,好吧,UB就是这样…
Valgrind报告没有内存错误
……特别是当你正在操作的内存仍然";属于";您的流程。Valgrind在这里没有什么可探测的;它不验证C++作用域;物理"†内存访问。该程序不会崩溃,因为还没有什么东西有机会破坏c
曾经占用的内存。
†";物理";我指的是操作系统及其内存管理,而不是C++的抽象概念。它实际上可能是虚拟内存或其他什么
- 通过双指针分配指针
- 在不工作的情况下为数组分配指针,但反过来也可以
- 无法使用"std::make_shared"分配指针
- 分配指针时出现分段错误
- 为什么有时仅使用Ampersand运算符在C 中分配指针
- 重新分配指针阵列的一部分的安全方法
- 为什么在执行增量操作之前分配指针值
- 当指针指向的对象被另一个类的实例删除时,重新分配指针
- 在堆上分配指针的原因是什么?
- 有没有办法防止分配指针?
- 将运算符重写应用于具有动态分配指针的类
- 在堆上分配指针数组
- 在另一个结构中的结构内解除分配指针
- 分配指针后,是否可以为指针指向的事物分配变量名称?
- 重新分配指针后,将类功能与指针一起使用
- 为什么我的新分配指针会自动在程序退出上删除
- 分配指针的方法之间有什么区别?
- 如何声明和分配指针
- 内存分配(指针和堆栈)
- 通过参数分配指针的值