我喜欢在 c++ 中使用哨兵类,但我似乎有一种精神上的痛苦,导致反复编写如下错误:
{
MySentryClass(arg);
// ... other code
}
不用说,这会失败,因为哨兵在创建后立即死亡,而不是按预期在范围结束时死亡。有什么方法可以防止 MySentryClass 被实例化为临时的,这样上面的代码要么无法编译,要么至少在运行时中止并显示错误消息?
我喜欢在 c++ 中使用哨兵类,但我似乎有一种精神上的痛苦,导致反复编写如下错误:
{
MySentryClass(arg);
// ... other code
}
不用说,这会失败,因为哨兵在创建后立即死亡,而不是按预期在范围结束时死亡。有什么方法可以防止 MySentryClass 被实例化为临时的,这样上面的代码要么无法编译,要么至少在运行时中止并显示错误消息?
我想不出一种自动的方法来检测你是否犯了这个错误。如果您一直错误地使用它,您总是可以创建一个扩展为正确事物的宏,并使用它来声明哨兵。
#define MY_SENTRY_CLASS(_X) MySentryClass _sentry(_X)
然后使用
MY_SENTRY_CLASS(arg);
或者在你的显示器上贴一张便利贴来提醒你。
您唯一能做的就是将构造函数设为私有并通过辅助函数强制访问。这与初始构造语法远不相似,并且不太可能出错。您也可以在堆上分配(仍然是浪费),但更容易发现。但是,如果你希望你的类是可构造的,你不能阻止人们构造这种类型的右值。
编辑:如果你知道 MySentryClass 总是接受一个参数,你可以禁止构造 AND 并且只允许 operator=(arguments)。这会迫使你做
MySentryClass x;
x = arg;
你可以为它做某种方法链。
MySentryClass x;
x.SetArg1(arg).SetArg2(arg2).construct();
不,这个问题没有出口。要在堆栈上创建对象,您必须有公共构造函数,如果您有公共构造函数,您可能会犯您报告的错误。
不确定你会喜欢这个解决方案,但解决方案很可能是grep
:
find /path/to/project -type f -name \*.cpp -print0 | xargs grep -0 'MySentryClass('
您可以做的另一件事是使用sed
或perl
预处理您的源文件,替换MySentryClass(
为\n#error MySentryClass used incorrectly\n
,这有望为您提供一个接近错误位置的行号。如何做到这一点取决于您的构建系统。
我认为#define 是最好的方法。
但就像不使用#define的一个选项:
int main()
{
try
{
S arg1;
// This will not compile
// MySentry x1 = MySentry::CreateSentry(arg1);
S arg3;
MySentry x2(MySentry::CreateSentry(arg3));
S arg2;
// This will not compile
// MySentry(arg2);
S arg4;
// This will generate a runtime exception
// It will never call start() or end()
//MySentry::CreateSentry(arg4);
}
catch(std::exception const& e)
{
std::cout << "Exception : " << e.what() << "\n";
}
}
#include <stdexcept>
#include <iostream>
class S
{
public:
void start() {std::cout << "Start\n";}
void end() {std::cout << "End\n";}
};
class MySentry
{
struct Init
{
Init(S& s) : arg(s),bad(true) {}
~Init() {if (bad) {throw std::runtime_error("Bad usage of MySentry");}}
S& arg;
mutable bool bad;
};
public:
static Init CreateSentry(S& arg) { return Init(arg);}
explicit MySentry(Init const& arg)
: obj(arg.arg)
, bad(false)
{
arg.bad = false;
std::cout << "Created\n";
obj.start();
}
MySentry(MySentry const& rhs)
: obj(rhs.obj)
, bad(false)
{
std::cout << "Copied (this may not appear)\n";
std::cout << "If the optimizer kicks in then the copy may be elided.\n";
// But if it did not optimize out then
// We have to mark the temporaty as bad
// And not call end() in its destructor.
// Note: Never call start() here as it will always be called in the
// main private constrctor above
rhs.bad = true;
}
~MySentry()
{
if (!bad)
{
// Everything working
obj.end();
}
std::cout << "Destroyed\n";
}
private:
S& obj;
mutable bool bad;
};
你试图做的在 C++ 中是完全合法的,我认为没有办法禁止它。