向 const 字符串参数发送 0 int 文本时的访问冲突
Access Violation when sending a 0 int literal to a const string parameter
在VS2015和VS2017上,编译时没有警告,并生成无法捕获的访问冲突并使应用程序崩溃。显然,int 0 被静默地转换为空指针,然后假定该指针指向一个字符串,因此崩溃。
#include <string>
#include <iostream>
void crash(const std::string& s) {}
int main()
{
try
{
crash(0);
}
catch (const std::exception& ex)
{
// never gets here!
std::cout << "got" << ex.what() << std::endl;
}
}
如何捕获此类异常并从中恢复? 如果我从函数参数中删除 const,它不会编译 - 所以这可能是防止用户滥用的一种方式,但我会失去 const 提供的保护,还是会? 编写避免此问题的原型的最佳实践是什么?
对于这种特定情况,您可以使用 C++11std::nullptr_t
获得编译时错误,只需添加以下已删除的重载:
void crash(std::nullptr_t) = delete;
当然,这不会保护您传递空(或非以空结尾)字符*指针...您违反了 std::string 构造函数前提条件,导致未定义的行为;根据定义,这是不可恢复的。
或者,如果您确实需要在运行时以可能可恢复的方式捕获这些错误,则可以编写一个const char*
重载,如果给定 null 指针,则会引发该重载,否则会调用std::string const&
版本。
如果你的实函数需要多个字符串参数,并且重载所有可能的组合似乎不可行,你可以重新编写一个函数模板,在事后对推导的类型执行所有检查。
namespace safer {
template<class CharT,
class Traits = ::std::char_traits<CharT>,
class Allocator = ::std::allocator<CharT>,
class Base = ::std::basic_string<CharT, Traits, Allocator>
>
struct basic_string:
Base
{
using Base::Base;
basic_string( CharT const* ptr ):
Base( ptr?Base(ptr):Base() )
{}
};
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
}
此safer::string
与std::string
基本相同,但在从空指针构造时不会崩溃。 相反,它将其视为空字符串。
只需从代码库中清除所有提及std::string
的内容,并替换为safer::string
,以及类似的std::wstring
和std::basic_string
。
void crash(const safer::string& s) {}
您可以选择抛出而不是默默地消耗值。
我们也可以在编译时检测0
:
namespace safer {
template<class CharT,
class Traits = ::std::char_traits<CharT>,
class Allocator = ::std::allocator<CharT>,
class Base = ::std::basic_string<CharT, Traits, Allocator>
>
struct basic_string:
Base
{
using Base::Base;
basic_string( CharT const* ptr ):
Base( ptr?Base(ptr):Base() )
{}
template<class T,
// SFINAE only accepts prvalues of type int:
std::enable_if_t<std::is_same<T, int>::value, bool> = true
>
basic_string(T&&)=delete; // block 0
};
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
}
现在传递0
会得到一个编译时错误,传递nullptr
或空char const*
会得到一个空字符串。
活生生的例子。
所以,有些人对我告诉你从std
中的非多态类型继承的事实感到紧张。 有一些理由不从非多态类型继承,但在这里都不适用。
但是,一般来说,从不是为多态性设计的类型(如std::basic_string<CharT>
)继承时要小心。 在这种特殊情况下,将safer::basic_string<T>
存储在std::basic_string<T>*
中,然后对其调用delete
是未定义的行为(或在调用delete
的std::unique_ptr<std::basic_string<T>>
中)。 但是动态分配basic_string
通常首先是一个错误,因此不太可能发生。
此外,此继承必须遵守 LSP,而不会更改基类的任何方法的行为。 在这种情况下,我们正在调整结构,并且构造永远不会是多态的。 如果我们有任何不是构造的写入操作,我们希望在降序类中保持不变,我们将遇到麻烦。
- 如何将文本文件的特定行读取到 int 类型的数组中C++?
- 逐字读取文本文件中的每一行并转换为 int(无限循环或崩溃?
- 如何检测将文本文件读入 int 数组的新行
- 如何将 qml 的文本转换为 float 和 int
- C++快速将 int 数组内容转储到文本文件中
- 如何将来自文本文件的数据读取到结构的int阵列中
- 函数读取最大和min int值,并用文本字符串返回
- 在文本文件中搜索int号码
- C 从文件中读取文本,然后将字符串解析为int数组
- 向 const 字符串参数发送 0 int 文本时的访问冲突
- 内部 for 循环:将文本和 int 转换为 const char* 并传递给函数
- C 从文本文件中将数据输入INT矢量
- 将简短的int保存在二进制文件中而不是文本文件中
- 从一个文本文件加载多个 char 和 int 类型的数组
- 在文本文件中一起打印十六进制和 int
- 引用参数何时可以接受 int 文本
- 在C++中一次一行地从文本文件中读取int(好的字符,然后转换)时出现问题
- 打开文本文件并读取一个带有空格的字符串,然后将两个独立的int放入一个结构中
- 构造函数中的 const int 引用是否可以安全地绑定到文本
- 为什么QLineEdit::setCursorPosition(int)选择(突出显示)文本