禁止实例化作为临时对象(C ++)

时间:2011-01-03 22:26:32

标签: c++ temporary temporary-objects

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

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

毋庸置疑,这是失败的,因为哨兵在创作后立即死亡,而不是按照预期在范围的最后。有没有办法阻止MySentryClass被实例化为临时代码,以便上面的代码无法编译,或者至少在运行时中止错误消息?

6 个答案:

答案 0 :(得分:8)

我无法想到一种自动检测你是否犯这个错误的方法。您可以随时创建一个扩展为正确的宏,并使用它来声明哨兵,如果您继续使用错误。

#define MY_SENTRY_CLASS(_X) MySentryClass _sentry(_X)

然后使用

MY_SENTRY_CLASS(arg);

或在显示器上贴一张贴子以提醒您。

答案 1 :(得分:4)

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

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

MySentryClass x;
x = arg;

你可以为它做一些方法链。

MySentryClass x;
x.SetArg1(arg).SetArg2(arg2).construct();

答案 2 :(得分:3)

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

答案 3 :(得分:2)

不确定您是否会喜欢此解决方案,但解决方案可能是grep

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

您可以做的另一件事是使用sedperl预处理您的源文件,将MySentryClass(替换为\n#error MySentryClass used incorrectly\n,这有望为您提供一个关闭的行号到错误的地方。如何执行此操作取决于您的构建系统。

答案 4 :(得分: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;
};

答案 5 :(得分:0)

您尝试做的事情在C ++中是完全合法的,我认为没有办法禁止它。