#include <iostream>
#include <string>
using namespace std;
template<class T> class Sample {
private:
T val;
public:
Sample(T InitialVal=T()) : val(InitialVal)
{
// do nothing
}
~Sample()
{
// do nothing
}
void PrintVal(void)
{
try {
cout << "[" << val << "]" << endl;
} catch(...) {
cout << "exception thrown" << endl;
}
}
};
int main() {
// your code goes here
Sample<int> ints(20), intd;
Sample<char *> chars(const_cast<char*>("Neelakantan")), charsd;
Sample<string> s, ss("neel");
ints.PrintVal();
intd.PrintVal();
chars.PrintVal();
charsd.PrintVal(); // <<- Culprit line. Commenting out this line works as expected.
s.PrintVal();
ss.PrintVal();
return 0;
}
当我运行上面的代码时,我得到以下输出:
sh-4.4$ g++ -o main *.cpp
sh-4.4$ main
[20]
[0]
[Neelakantan]
[sh-4.4$
当我注释掉“charsd.PrintVal();
”行时,我得到以下输出:
[sh-4.4$ g++ -o main *.cpp
sh-4.4$ main
[20]
[0]
[Neelakantan]
[]
[neel]
sh-4.4$
Sample&lt;类型的模板实例的'charsd'对象有什么问题? char *&gt; ?没有例外被抛出。
编译器版本:
sh-4.4$ g++ --version
g++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
sh-4.4$
答案 0 :(得分:5)
问题是val;
对象的字段charsd
是用空指针初始化的。因此,尝试将其传递给operator <<
违反运算符前置条件并导致未定义的行为。
答案 1 :(得分:3)
charsd
包含空指针,将const char*
写入ostream
是未定义的行为。
GCC的标准库实现检查该条件(请参阅the code here)并在流状态中设置badbit
标志。 badbit
表示流状态已损坏,在这种情况下是标准所说的未定义行为的意外空指针。注:对ostream
的空指针的处理没有记录,将来可能会改变。
程序继续运行完成,但由于在流上设置了badbit
,所有后来的写入都会失败,在std::cout
上不再生成输出。
当空指针写入流时,程序不会以静默方式退出。它设置一个错误标志,并继续运行。
如果要检测是否可以执行此操作,例如检查main
末尾的流状态并将消息打印到标准错误:
int main() {
// ...
if (!std::cout)
std::cerr << "An error happened while writing to cout\n";
return 0;
}
或者告诉cout
流您希望将错误转换为例外:
int main() {
std::cout.exceptions(std::ios::badbit | std::ios::failbit);
// ...
return 0;
}
现在程序会在错误发生时立即抛出异常:
terminate called after throwing an instance of 'std::ios_base::failure[abi:cxx11]'
what(): basic_ios::clear: iostream error
Aborted (core dumped)
答案 2 :(得分:1)
[基于新信息/知识的修订答案。另请参阅Jonathan Wakeley的回答(我在看到之前对此进行了更新),谁是gcc / libstdc ++撰稿人,并且接受了我提交的错误报告。非常感谢他让我直截了当,最诚挚的抱歉做出了荒谬的假设ostream
将以我之前描述的方式表现出来。]
在失败时,你实际上在做什么,实际上是这样的:
std::cout << (const char *) nullptr << std::endl;
大多数人都希望得到的(尽管严格来说,你所做的事情的行为是未定义的)将是一个SEGFAULT。
然而, libstdc++,
包含ostream
使用的gcc
的实现,它有所不同。它在流上设置badbit
,只有在您启用badbit
例外(默认情况下已禁用)时才设置,它(也)会抛出异常。感谢Jonathan Wakeley在我提交有关此问题的错误报告时向我指出这一点,当我发布此答案的第一个版本时,我对此一无所知(显然也没有其他人发布到此主题)。
但是你的代码没有启用所述异常,所以所有发生的事情都是badbit
在cout
上设置,所有后续写入随后无声地失败。我以前误解了这个程序,当nullptr被传入时程序悄然退出,但我错了,我向开发人员道歉,做出这样一个无根据的假设。在评论中还有更多相关内容。
因此,要在发生这种情况时抛出异常,您必须在流上启用badbit
异常,您可以这样做:
std::cout.exceptions (std::ostream::badbit | std::ios::failbit);
然后你得到了你所希望的例外。就我个人而言,我不喜欢这种行为,我更喜欢SEGFAULT(事实上,还有clang,you do),但Jonathan告诉我它就是那样的自2002年以来,开发人员有充分的理由不改变它。
有一个新的live demo显示gcc
的行为,并添加了上面的行,现在是输出:
[20]
[0]
[Neelakantan]
terminate called after throwing an instance of 'std::__ios_failure'
what(): basic_ios::clear: iostream error
[
Aborted
所以,请注意,一个疏忽的陷阱在于等待那里,&#34;为什么我的日志记录都没有突然出现?&#34;或类似的东西。