我希望A::~A()
在这个程序中被调用,但它不是:
#include <iostream>
struct A {
~A() { std::cout << "~A()" << std::endl; }
};
void f() {
A a;
throw "spam";
}
int main() { f(); }
Run Code Online (Sandbox Code Playgroud)
但是,如果我将最后一行更改为
int main() try { f(); } catch (...) { throw; }
Run Code Online (Sandbox Code Playgroud)
然后A::~A()
被称为.
我正在从Visual Studio 2005编译"Microsoft(R)32位C/C++优化编译器版本14.00.50727.762 for 80x86".命令行是cl /EHa my.cpp
.
编译器像往常一样吗?标准对此事有何评价?
该文档mem::uninitialized
指出了使用该函数危险/不安全的原因:调用drop
未初始化的内存是未定义的行为.
因此,我相信这段代码应该是未定义的:
let a: TypeWithDrop = unsafe { mem::uninitialized() };
panic!("=== Testing ==="); // Destructor of `a` will be run (U.B)
Run Code Online (Sandbox Code Playgroud)
但是,我编写了这段代码,它在安全的Rust中运行,并且似乎没有受到未定义的行为的影响:
#![feature(conservative_impl_trait)]
trait T {
fn disp(&mut self);
}
struct A;
impl T for A {
fn disp(&mut self) { println!("=== A ==="); }
}
impl Drop for A {
fn drop(&mut self) { println!("Dropping A"); }
}
struct B;
impl T for B {
fn disp(&mut self) { println!("=== B ==="); }
}
impl …
Run Code Online (Sandbox Code Playgroud) 为什么在下面详述的场景中,堆栈空间在x64中增加但在x32中使用相同的代码减少?
背景:
我们的客户可以使用域语言编写脚本,该语言在运行时使用递归技术解释并在Web服务器上执行.它们可能会在抛出异常的脚本中出错,此异常会被捕获并记录.
由于这种递归技术,我们通过检查用作解释器执行脚本的堆栈空间来防止堆栈溢出异常,并在我们实际耗尽堆栈之前终止脚本.
在32位模式下,一切运行良好,当脚本编写器Exception
生成错误时,它被记录,堆栈展开,在此期间堆栈上剩余的空间增加,脚本很好地终止.
在64位模式下,一切都不太好,当脚本编写Exception
器生成错误时,它被记录,堆栈展开,在此期间堆栈上剩余的空间减少.这是非常糟糕的,因为有可能如果脚本碰巧使用了大量的堆栈空间并抛出然后解除堆栈并记录错误本身的行为导致StackOverflowException
隐藏原始异常,软管IIS并杀死所有机上要求(坏,非常糟糕,非常糟糕).
重新创建问题:
这是一个控制台应用程序,它模拟我在生产中使用的代码,并在设置为x64时重新创建问题,并在x32中正常工作.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ConsoleApplication16
{
class Program
{
const Int32 MaxNumberRecursions = 10;
static Int32 _currentRecursionDepth;
static UInt64 _lastSpaceUsed;
static void Main(string[] args)
{
System.Diagnostics.Debug.WriteLine(String.Format("Is64BitProcess = {0}", System.Environment.Is64BitProcess));
try
{
_lastSpaceUsed = GetStackBytesLeft();
RecurseXTimes();
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}
unsafe static void RecurseXTimes()
{
System.Diagnostics.Debug.WriteLine("--> RecurseXTimes()");
ReportStackSpaceUsage();
try
{
_currentRecursionDepth++;
if (_currentRecursionDepth > MaxNumberRecursions)
{ …
Run Code Online (Sandbox Code Playgroud) 是否有主要的C/C++实现,其中longjmp
函数"展开",即它与自动存储对象的析构函数__attribute__((__cleanup__(...)))
,POSIX线程取消处理程序等交互,而不仅仅是恢复由setjmp
?保存的寄存器上下文?我对使用此属性的POSIX实现的存在(或不存在)特别感兴趣,但C/C++通常也很有趣.
对于赏金,我正在寻找符合POSIX或至少类似POSIX的系统,而不是已经提到过的Windows.
我通过代码处理SIGSEGV:
int C()
{
int *i = NULL;
*i = 10; // Crash there
}
int B()
{
return C();
}
int A()
{
return B();
}
int main(void)
{
struct sigaction handler;
memset(&handler,0,sizeof(handler));
handler.sa_sigaction = handler_func;
handler.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV,&handler,NULL);
return(C());
}
Run Code Online (Sandbox Code Playgroud)
处理程序代码在哪里:
static int handler_func(int signal, siginfo_t info, void* rserved)
{
const void* stack[MAX_DEPTH];
StackCrowlState state;
state.addr = stack;
state.count = MAX_DEPTH;
_Unwind_Reason_Code code = _Unwind_Backtrace(trace_func,&state);
printf("Stack trace count: %d, code: %d\n",MAX_DEPTH - state.count, code);
kill(getpid(),SIGKILL);
}
static …
Run Code Online (Sandbox Code Playgroud) 据我所知,在未捕获异常的情况下,C++会立即销毁局部变量,Java会释放引用并将其余部分留给垃圾收集器.
这是正确的吗?Java和C++在这个问题上究竟有什么区别?换句话说,就堆栈展开问题而言,这两种语言中的哪一种被认为更好?:)
这是一个跟进的原因为什么Alexandrescu不能使用std :: uncaught_exception()在ScopeGuard11中实现SCOPE_FAIL?
我想检测是否有人MyClass
在另一个类的析构函数中创建(或者在调用堆栈中的某个地方使用了一个活动的析构函数).
class MyClass
{
public:
MyClass(){
assert(???what to put here????);
}
}
void f(){
MyClass m; //whether this asserts should be context dependant
}
class OtherClass{
~OtherClass(){
MyClass m; //this should assert
f(); //this should too;
}
}
int main()
{
MyClass m; //this should not assert
f(); //this should also not assert
}
Run Code Online (Sandbox Code Playgroud)
一次尝试可能是:
assert(!std::uncaught_exception());
Run Code Online (Sandbox Code Playgroud)
但是只有在因为异常而被调用析构函数时才会有效,而不是因为对象超出范围而被调用.
我正在学习PHP类和异常,并且,从C++背景来看,以下内容让我感到奇怪:
当派生类的构造函数抛出异常时,似乎基类的析构函数不会自动运行:
class Base
{
public function __construct() { print("Base const.\n"); }
public function __destruct() { print("Base destr.\n"); }
}
class Der extends Base
{
public function __construct()
{
parent::__construct();
$this->foo = new Foo;
print("Der const.\n");
throw new Exception("foo"); // #1
}
public function __destruct() { print("Der destr.\n"); parent::__destruct(); }
public $foo; // #2
}
class Foo
{
public function __construct() { print("Foo const.\n"); }
public function __destruct() { print("Foo destr.\n"); }
}
try {
$x = new Der;
} …
Run Code Online (Sandbox Code Playgroud) 我正在为Duktape JavaScript解释器开发Rust包装器.在正常的用例中,调用堆栈将如下所示:
如果(5)调用会发生什么panic!
?根据IRC上的各种Rust开发人员,尝试panic!
从非Rust调用帧(如(3))内部可能会导致未定义的行为.
但根据Rust文档,捕获a的唯一方法panic!
是使用std::task::try
,这会产生额外的线程.还有rustrt::unwind::try
,除了其他限制之外,它不能在单个线程中嵌套两次.
Benjamin Herr提出的一个解决方案是,如果(5)中的代码恐慌,则中止该过程.我已经将他的解决方案打包abort_on_panic
为"工作"的价值,它似乎有效,包括"崩溃整个程序,但至少不会巧妙地破坏事物":
abort_on_panic!("cannot panic inside this block", {
panic!("something went wrong!");
});
Run Code Online (Sandbox Code Playgroud)
但是,这是一种模拟std::task::try
没有线程/任务创建的开销的方法吗?
根据我的理解,throw
是一个灵长类似的jvm命令.调用它时,JVM"检查当前调用堆栈是否可以捕获它".如果它不能,那么java只是简单地弹出调用栈,就好像调用了一个返回一样.然后jvm"检查当前调用堆栈是否可以捕获它"等等递归.
我的问题:JVM在算法上如何知道调用堆栈中哪些地方可以捕获给定的异常?每个调用堆栈条目中是否存储了元数据,将异常映射到代码块?堆中是否有一个静态数据结构以某种方式跟踪它?因为某处必须有数据跟踪.
stack-unwinding ×10
c++ ×4
exception ×4
destructor ×3
c ×2
java ×2
rust ×2
.net ×1
c# ×1
ffi ×1
gcc ×1
jvm ×1
longjmp ×1
php ×1
signals ×1
stack-trace ×1
visual-c++ ×1