抛出、捕获、界定 stl::string.c_str() 异常
假设我编写了一些可以抛出 const char* 异常的函数 myFunc:
void myFunc()
{
int returnCode = whatever();
if (!returnCode)
{
std::string msg;
msg.append("whatever function failed");
std::cerr << msg << std::endl; // print warning message
throw msg.c_str(); // throw warning message as exception
}
}
后来我像这样使用它:
void myProgram()
{
try
{
myFunc();
}
catch(const char* str)
{
// is 'str' string memory valid here?
}
}
我意识到这对于异常使用来说并不是一个好的策略:更好地抛出和捕获异常类,而不是字符串。但我对这里涉及的范围感到好奇。
Say I've written some function myFunc that can throw const char* exceptions:
void myFunc()
{
int returnCode = whatever();
if (!returnCode)
{
std::string msg;
msg.append("whatever function failed");
std::cerr << msg << std::endl; // print warning message
throw msg.c_str(); // throw warning message as exception
}
}
And later I'm using it like so:
void myProgram()
{
try
{
myFunc();
}
catch(const char* str)
{
// is 'str' string memory valid here?
}
}
I realize this isn't really a good strategy for exception usage: better to throw and catch exception classes, not strings. But I'm curious about the scoping involved here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
msg.str()
返回一个临时std::string
。由于临时变量在语句;
末尾被删除,因此当抛出 ... ;< 时,
c_str()
返回的字符串内容变为未定义。 /code> 语句通过异常机制离开作用域来终止。(
const char*
临时的生命周期显然已延长到到达catch
处理程序,但这并没有帮助,因为底层缓冲区已经消失了)。抛出
std::string
(即throw msg.str();
)会起作用,临时的生命周期将按预期延长。msg.str()
returns a temporarystd::string
. As temporaries are deleted at the end of a statement;
, the contents of the character string returned byc_str()
become undefined when thethrow ... ;
statement terminates by leaving the scope via the exception mechanism.(The lifetime of the
const char*
temporary is obviously extended to reach to thecatch
handler, but that does not help since the underlying buffer is gone).Throwing
std::string
(i.e.throw msg.str();
) would work, the lifetime of the temporary would be extended as intended.事实上,
c_str()
调用作用于一个临时(string
)对象,当您捕获它时,指针将无效。不仅如此,由于
stringstream
和string
可以进行分配,因此您需要确保不会因为堆问题而抛出异常。如果您由于内存不足而处于这一点,您可能会在尝试创建异常时遭遇更糟糕的情况。您通常希望避免在特殊情况下进行堆分配。您是否无法使用
runtime_error
或创建自己的异常类型?Indeed the
c_str()
call is acting on a temporary (string
) object and the pointer will be invalid when you catch it.Not only that, but since the
stringstream
andstring
could do allocation, you need to make sure that you're not throwing because of heap problems. If you're at that point due to being out of memory you may bomb out even worse trying to create your exception. You generally want to avoid heap allocation during exceptional cases.Are you unable to use say
runtime_error
or create your own exception type?请注意,如果您说:
那就没问题,因为字符串文字的生命周期就是程序的生命周期。但无论如何,不要这样做!
Note that if you had said:
you would be OK because the lifetime of the string literal is the lifetime of the program. But don't do it, anyway!
亚历山大·盖斯勒(Alexander Gessler)在他的回答中没有提到的事情是他的答案的可能性。 com/a/134640/629493">在创建临时字符串对象期间,
std::string
本身抛出异常 。std::exception
保证在构造过程中不会抛出异常,std::string
没有这样的保证。另一种方法(对于类)是在类中声明一个私有
std::string
对象。在抛出
之前组装错误消息,然后抛出c_str()
。这将抛出一个 const char* 异常,错误消息在该类抛出下一个异常之前一直有效(这可能会再次修改错误字符串。)可以在此处找到一个简单的示例:< a href="http://ideone.com/d9HhX" rel="nofollow noreferrer">http://ideone.com/d9HhX
Something Alexander Gessler did not mention in his answer, is the possibility of an exception being thrown by by
std::string
itself during the creation of the temporary string object.std::exception
is guaranteed not to throw an exception during construction,std::string
has no such guarantee.An alternative approach (for classes) is to declare a private
std::string
object in your class. Assemble the error message prior to thethrow
, and then throw thec_str()
. This will throw aconst char*
exception, the error message being valid until the next exception is thrown from that class (which would presumably modify the error string again.)A simple example can be found here: http://ideone.com/d9HhX
虽然这个问题在十多年前就已经回答过,但我会给出一些补充:
https://learn.microsoft.com/en-us/cpp/cpp/exceptions-and-stack-unwinding-in-cpp
在 C++ 异常机制中,控制权从throw 语句指向可以处理抛出类型的第一个 catch 语句。当到达 catch 语句时,在 throw 和 catch 语句之间范围内的所有自动变量都将在称为堆栈展开的过程中被销毁。
也就是说,堆栈中的任何变量,包括对象变量或基本类型(int、char等)都将被销毁。
在c++中抛出一个对象时,c++实际上会通过调用对象复制构造函数来创建一个克隆对象,克隆对象的生存期将通过throw-catch有效。
参考:
C++:引发异常会调用复制构造函数?
现在,回到OP问题,所以,“throw msg.c_str();”无效,因为 msg 对象
被删除(它是一个自动变量),它恰好可以工作,因为内存仍然存在但实际上被释放了。
所以,正如 @AlexanderGessler、@MarkB 建议的那样,
最好的c++实践是:总是抛出字符串对象runtime_error,而不是抛出string.c_str()
Although this was answered over ten years ago, I would give some supplement:
https://learn.microsoft.com/en-us/cpp/cpp/exceptions-and-stack-unwinding-in-cpp
In the C++ exception mechanism, control moves from the throw statement to the first catch statement that can handle the thrown type. When the catch statement is reached, all of the automatic variables that are in scope between the throw and catch statements are destroyed in a process that is known as stack unwinding.
That's to say, any variables in the stack, including object variable, or basic type(int, char, etc) will be destroyed.
When throw an object in c++, c++ will actually make a cloned object by invoking the object copy constructor, the cloned object lifetime will valid through throw-catch.
reference:
C++: Throwing an exception invokes the copy constructor?
Now, return to the OP question, so, "throw msg.c_str();" is invalid because the msg object
is deleted (it's a auto variable), it happens to work because the memory is still there but actually freed.
So, just as @AlexanderGessler, @MarkB suggested,
the best c++ practice is: always throw string object, runtime_error, instead of throwing string.c_str()