- 主题:lambd表达式对已析构对象为啥不抛异常
这种场景就是Rust出彩的地方,不会让你编译过去。
C++是“相信码农”(其实不能完全相信码农,先不说水平有高低,高手在复杂代码面前可能也有犯迷糊的时候)
靠码农要么把被捕捉的变量放在更大的stack scope里或者堆里保证其生命周期够长(shared_ptr变量本身、share_ptr里的裸指针指向的数据,都要保证生命周期够长),要么传值。
你这个例子,chatgpt4能完全分析对。
--
修改:z16166 FROM 123.118.184.*
FROM 123.118.184.*
这个例子,chatgpt4分析的是延迟绑定问题,不是栈回收问题:
这段代码的主要问题在于 Python 的闭包延迟绑定。下面是具体的解析:
在这段代码中,我们看到在 main() 函数中创建了一个名为 a 的列表,用来存放一组返回 ai.p() 的 lambda 函数。然后在一层 for 循环里,把构建好的 lambda 函数分别放入列表 a 中。然后在下一层 for 循环中,尝试调用这些存储在列表中的函数。
这里存在的问题是,这些函数在被存储起来的时候不会立即执行。而当我们尝试去调用这些函数的时候,由于 Python 的闭包延迟绑定的特性,这些函数会去尝试获取最新的 ai 的值,也就是最后一次 for 循环的变量,而不是当时的限定值。
在这个例子中,所有的 lambda 函数都是在 ai 被赋值 2 之后才被调用的,所以,无论我们调用哪个 lambda 函数,它们都会去获取 ai 的当前值,也就是 2,然后尝试执行 ai.p()。
这就是为什么只会有最后一个对象的 p() 方法被调用出来,并且所有的输出都是 ">> 2" 的原因。
我们可以通过如下方式修改代码,避免这一问题:
python
def main():
a = []
for i in range(3):
ai = A(i)
a.append(lambda ai=ai: ai.p())
for c in a:
c()
通过将 lambda 函数中的 ai 指定为 i 时的 ai,我们可以在每次循环时都固定住 ai 的值,从而在后续调用函数时能够正确地获取到当时的 ai。这就避免了上述的问题,每个对象的 p() 方法都会被正确地调用,并且输出的结果也会分别是 ">> 0", ">> 1", ">> 2"。
【 在 VincentGe 的大作中提到: 】
: 和gc无关,这个就是一个基本错误。在指针指向栈空间变量,但是它最后被回收,下面是一个Python等价示例。
: class A:
: def __init__(self, i):
: ...................
--
FROM 123.118.184.*
楼主列的C++的例子是非常简单而且典型的,看看汇编代码也能知道
python的代码,不好去看解释器的实现,顶多是加点打印变量地址的代码输出看看,然后就是一些解释器的实现的原则性描述。Ned Batchelder总结了几条,比较清楚,帖子标题是Facts and myths about Python names and values,贴不了URL了
【 在 VincentGe 的大作中提到: 】
: 我大概理解问题在哪了。
: 我使希望使用Python代码来说明CPP代码存在的问题的,但是两个在这里有些差异,我混用了一些术语造成误解。
: 对于Python,在这个示例中,虽然和CPP不同,但在形式上是相同的,在循环中,创建了一个自由变量ai,它被分配在堆上,lambda引用了自由变量ai,但经过三次循环,ai的值被不断改变,最终调用时指向最后一次创建的ai,但如果我们提前del ai或 ai=None,就会导致指向的在堆上的空间被回收,出现错误,你给出的写法,也是Python文档里面的写法,通过复制值的方式,将其传递给lambda,虽然看似相同,但实际上此时ai,被分配在栈上,lambda被调用时,此时ai在新的栈帧中,因此没有问题。
: ...................
--
FROM 123.118.184.*
C++编译器是不做变量的生命周期检查的,都是由码农来做这件事情,顶多有一些静态、动态检查工具可能会做。
所以编译期并不会发现这种问题。Rust编译器通过生命周期标记、类型推导以及move语义来检查这种问题。
运行期如果出问题,主要是靠CRT代码以及OS提供的异常处理API,但有些问题不一定能被CRT和OS API捕捉到,比如引用了已经被释放的内存但是并未导致access violation异常。
【 在 VincentGe 的大作中提到: 】
: 我阅读了那个帖子,非常棒,他讲述的很清晰,感谢你提供的这些。
: 对于这段Python,我贴出来的原因是它和CPP所犯的错误是相似的,而对于Python的使用者而言,它在Python的文档中的的常见问题中被回答,对于期望深入了解的开发者,可以使用dis包的dis来获取字节码,希望更想深入解释器,那这需要另行讨论。
: 我期望用此说明楼主所提供的CPP代码中的错误并非由使用带有gc语言所造成的惯性而带来的。
: ...................
--
FROM 123.118.184.*
你不是大陆的啊?大陆一般不说呼叫,哈哈
【 在 ziqin (子青|会挽雕弓如满月|西北望|射天狼) 的大作中提到: 】
: 讲真,很多所谓的程序员,连函数是被同步呼叫还是异步呼叫都不知道,就疯狂用智能指针,疯狂传引用。
:
: 然后反过来抱怨,不检查生命周期,这jb怎么检查?
:
--
FROM 114.246.237.*