我不是程序员,但通过观察别人学到了很多东西。我正在编写包装器类,以使用我正在使用的真正技术性的 API 来简化事情。它的例程返回错误代码,我有一个将这些代码转换为字符串的函数:
static const char* LibErrString(int errno);
为了保持一致性,我决定让我的类成员在遇到错误时抛出异常。我创建了一个类:
struct MyExcept : public std::exception {
const char* errstr_;
const char* what() const throw() {return errstr_;}
MyExcept(const char* errstr) : errstr_(errstr) {}
};
然后,在我的一门课中:
class Foo {
public:
void bar() {
int err = SomeAPIRoutine(...);
if (err != SUCCESS) throw MyExcept(LibErrString(err));
// otherwise...
}
};
整个过程完美无缺:如果 SomeAPIRoutine
返回一个错误,围绕调用 Foo::bar
的 try-catch block 会捕获一个带有正确错误的标准异常what()
中的字符串。
然后我希望成员(member)提供更多信息:
void Foo::bar() {
char adieu[128];
int err = SomeAPIRoutine(...);
if (err != SUCCESS) {
std::strcpy(adieu,"In Foo::bar... ");
std::strcat(adieu,LibErrString(err));
throw MyExcept((const char*)adieu);
}
// otherwise...
}
但是,当SomeAPIRoutine
返回错误时,异常返回的what()
字符串只包含垃圾。我突然想到,问题可能是由于调用 throw
后 adieu
超出了范围。我通过将 adieu
移出成员定义并使其成为类 Foo
的属性来更改代码。在此之后,整个事情完美地进行了:围绕调用 Foo::bar
的 try-call block 捕获了一个异常,在 what()
中有正确的(扩展的)字符串>.
最后,我的问题是:当堆栈“展开”时在 if block 中抛出异常时,到底从堆栈中弹出了什么(按顺序)?正如我上面提到的,我是一名数学家,而不是程序员。当这个 C++ 被转换成运行的机器代码时,我可以使用一个非常清晰的解释来解释什么进入堆栈(按顺序)。
最佳答案
你是对的:异常构造函数采用指向字符串的指针,它不存储字符串内容的拷贝。该内容存储在局部变量中
char adieu[128];
从 Foo::bar 方法退出时超出范围。
堆栈包含当前正在执行的函数的“激活记录”(也称为“堆栈帧”)。每个函数调用都在该堆栈上为该函数中声明的所有局部变量分配内存(在机器级别,这可以实现为“推送”或任何其他推进堆栈指针的命令)。函数的每次返回——无论是正常返回还是通过抛出异常退出都无关紧要——释放进入函数时分配的内存(在机器级别,这被实现为“pop”或“恢复堆栈指针到输入函数时它的值')。
因此,当堆栈展开时,“函数调用链”或“堆栈”中抛出异常的函数和捕获异常的函数之间的所有“激活记录”都将被释放。
关于c++ - 寻求明确的解释 : throw() and stack unwinding,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5270882/