запрещение создания экземпляров в качестве временного объекта (C ++) - PullRequest
10 голосов
/ 04 января 2011

Мне нравится использовать сторожевые классы в c ++, но у меня, похоже, есть психическое недомогание, которое приводит к многократной записи ошибок вроде следующего:

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

Нет необходимости говорить, что это не удалось, потому что часовой умирает сразу послесоздание, а не в конце области, как предполагалось.Есть ли какой-нибудь способ предотвратить создание экземпляра MySentryClass как временного, чтобы вышеприведенный код либо не компилировался, либо, по крайней мере, прерывался с сообщением об ошибке во время выполнения?

Ответы [ 6 ]

8 голосов
/ 04 января 2011

Я не могу придумать автоматический способ обнаружения, если вы делаете эту ошибку или нет.Вы всегда можете создать макрос, который расширяется до нужной вещи, и использовать его для объявления сторожа, если вы продолжаете использовать его неправильно.пост-это на вашем мониторе, чтобы напомнить вам.

4 голосов
/ 04 января 2011

Единственное, что вы можете сделать, это сделать конструкторы приватными и принудительно получить доступ через вспомогательную функцию. Это намного менее похоже на первоначальный синтаксис конструкции и менее вероятно, что оно будет ошибочным. Вы также можете выделить в куче (все еще пустая трата времени), но это гораздо легче обнаружить. Однако, если вы хотите, чтобы ваш класс был конструируемым, вы не можете помешать людям создавать значения этого типа.

Редактировать: Если вы знаете, что MySentryClass всегда принимает аргумент, вы можете запретить конструкцию AND и разрешить только оператор = (аргументы). Это заставит вас сделать

MySentryClass x;
x = arg;

Вы могли бы сделать какую-то цепочку методов для этого.

MySentryClass x;
x.SetArg1(arg).SetArg2(arg2).construct();
3 голосов
/ 04 января 2011

Нет, выхода из этой проблемы нет. Чтобы создавать объекты в стеке, у вас должны быть открытые конструкторы, и если у вас есть открытые конструкторы, вы можете совершить ошибку, о которой сообщаете.

2 голосов
/ 04 января 2011

Не уверен, что вам понравится это решение, но решение вполне может быть grep:

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

Еще одна вещь, которую вы можете сделать, это использовать sed или perl для предварительной обработки вашего исходного файла, заменив MySentryClass( на \n#error MySentryClass used incorrectly\n, что, как мы надеемся, даст вам номер строки, близкий к месту ошибки. Как это сделать, зависит от вашей системы сборки.

1 голос
/ 04 января 2011

Я думаю, что #define - лучший метод.
Но просто как вариант не использовать #define:

Main

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";
    }
}

EditedТеперь работает лучше.

#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;
};
0 голосов
/ 04 января 2011

То, что вы пытаетесь сделать, совершенно законно в C ++, и я не думаю, что есть способ запретить это.

...