10

我喜欢在 c++ 中使用哨兵类,但我似乎有一种精神上的痛苦,导致反复编写如下错误:

{
  MySentryClass(arg);
  // ... other code
}

不用说,这会失败,因为哨兵在创建后立即死亡,而不是按预期在范围结束时死亡。有什么方法可以防止 MySentryClass 被实例化为临时的,这样上面的代码要么无法编译,要么至少在运行时中止并显示错误消息?

4

6 回答 6

8

我想不出一种自动的方法来检测你是否犯了这个错误。如果您一直错误地使用它,您总是可以创建一个扩展为正确事物的宏,并使用它来声明哨兵。

#define MY_SENTRY_CLASS(_X) MySentryClass _sentry(_X)

然后使用

MY_SENTRY_CLASS(arg);

或者在你的显示器上贴一张便利贴来提醒你。

于 2011-01-03T22:40:00.463 回答
4

您唯一能做的就是将构造函数设为私有并通过辅助函数强制访问。这与初始构造语法远不相似,并且不太可能出错。您也可以在堆上分配(仍然是浪费),但更容易发现。但是,如果你希望你的类是可构造的,你不能阻止人们构造这种类型的右值。

编辑:如果你知道 MySentryClass 总是接受一个参数,你可以禁止构造 AND 并且只允许 operator=(arguments)。这会迫使你做

MySentryClass x;
x = arg;

你可以为它做某种方法链。

MySentryClass x;
x.SetArg1(arg).SetArg2(arg2).construct();
于 2011-01-03T22:32:20.107 回答
3

不,这个问题没有出口。要在堆栈上创建对象,您必须有公共构造函数,如果您有公共构造函数,您可能会犯您报告的错误。

于 2011-01-03T22:32:40.083 回答
2

不确定你会喜欢这个解决方案,但解决方案很可能是grep

find /path/to/project -type f -name \*.cpp -print0 | xargs grep -0 'MySentryClass('

您可以做的另一件事是使用sedperl预处理您的源文件,替换MySentryClass(\n#error MySentryClass used incorrectly\n,这有望为您提供一个接近错误位置的行号。如何做到这一点取决于您的构建系统。

于 2011-01-03T22:50:11.723 回答
1

我认为#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;
};
于 2011-01-03T23:25:16.503 回答
0

你试图做的在 C++ 中是完全合法的,我认为没有办法禁止它。

于 2011-01-03T22:41:01.267 回答