Проблема с Фабрикой и динамическим размещением в C ++ - PullRequest
3 голосов
/ 29 декабря 2008

У меня есть фабрика, которая строит объекты с самым длинным временем жизни в моем приложении. Они имеют типы, скажем, ClientA и ClientB, которые зависят от Provider (абстрактный класс с множеством возможных реализаций), поэтому оба клиента имеют ссылку на Provider в качестве члена.

Согласно аргументам командной строки, фабрика выбирает одну реализацию Provider, создает ее (с "new") и передает ее конструкторам обоих клиентов.

Фабрика возвращает объект, который представляет все мое приложение. Моя основная функция в основном такова:

int main(int argc, char** argv)
{
    AppFactory factory(argc, argv);
    App app = factory.buildApp();
    return app.run();
}

И метод buildApp в основном такой:

App AppFactory::buildApp()
{
    Provider* provider = NULL;

    if (some condition)
    {
        provider = new ProviderX(); 
    }
    else
    {
        provider = new ProviderY();
    }

    ClientA clientA(*provider);
    ClientB clientB(*provider);

    App app(clientA, clientB);
    return app;
}

Итак, когда выполнение заканчивается, вызываются деструкторы всех объектов, кроме объекта-провайдера (потому что он был создан с помощью "new").

Как я могу улучшить этот дизайн, чтобы убедиться, что деструктор провайдера называется?

РЕДАКТИРОВАТЬ: Чтобы уточнить, я намерен, чтобы и клиенты, провайдер и объект приложения совместно использовать один и тот же срок службы. После всех ответов я думаю, что и клиенты, и поставщик должны быть распределены в куче, ссылки на которую переданы объекту App, который будет отвечать за их удаление, когда он умирает. Что ты скажешь?

Ответы [ 8 ]

3 голосов
/ 30 декабря 2008

С умным указателем общего владения все очень просто:

App AppFactory::buildApp()
{
    boost::shared_ptr<Provider> provider;

    if (some condition)
    {
        provider.reset(new ProviderX()); 
    }
    else
    {
        provider.reset(new ProviderY());
    }

    ClientA clientA(provider);
    ClientB clientB(provider);

    App app(clientA, clientB);
    return app;
}

Предполагается, что объект приложения владеет клиентами, и все клиенты совместно используют одного поставщика. Заставьте клиентов взять shared_ptr<Provider> тогда вместо Provider&. Пока существует копия shared_ptr, владеющая объектом провайдера, объект не будет освобожден.

Лучше всего не копировать clientA и clientB и не копировать приложение, возвращая его по значению, а перемещать клиентов в приложение и перемещать само приложение в возвращаемый объект. Это станет возможным в следующей версии C ++. Но в настоящее время вы либо делаете их указателями (используя shared_ptr), либо продолжаете копировать их. Другой вариант - использовать auto_ptr, который имеет семантику псевдо-передачи права собственности. Но у этого шаблона есть некоторые присущие ему проблемы. Поэтому вы должны избегать его использования.

2 голосов
/ 30 декабря 2008

Если конструктор приложения не скопирует клиентов, то их также нужно будет редактировать новым () - текущие из них будут размещены в стеке и будут удалены при возврате приложения.

Я думаю, что вы можете быть осторожны с тем, какие объекты создаются - например, положить отладочные операторы в конструкторы Клиента.

То, что вы, вероятно, хотите, это чтобы подсчитывать ссылки на Провайдера, и чтобы каждый Клиент просто уменьшал счетчик ссылок, но это большая работа.

Либо сделайте AppFactory владельцем провайдера.

2 голосов
/ 30 декабря 2008

делает провайдера переменной экземпляра AppFactory. затем сделайте провайдера умным указателем или удалите его в dtor AppFactory.

1 голос
/ 30 декабря 2008

Вы говорите «поставщик и объект приложения имеют одно и то же время жизни», но учтите, что в C ++ приведен следующий фрагмент кода ...

App app(clientA, clientB);
return app;

... возвращает копию объекта App: и поэтому вы можете (в зависимости от компилятора, см., Например, http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx) фактически иметь два экземпляра объекта App (один внутри метод AppFactory :: buildApp () и другой внутри основной функции).

Чтобы ответить на ваш вопрос, я думаю, что я согласен с вашей правкой: передайте указатель на поставщика в конструктор приложения, сохраните его как данные члена экземпляра приложения и удалите его при уничтожении экземпляра приложения. Кроме того, однако, вы также можете изменить свой код, чтобы не копировать экземпляр приложения: например, выделить экземпляр приложения в куче, изменить метод AppFactory :: buildApp (), чтобы он возвращал указатель на App и удалите экземпляр App в конце основной функции.

1 голос
/ 30 декабря 2008

Сделать провайдер переменной-членом AppFactory и удалить ее в деструкторе:

class AppFactory
{
    public:
    AppFactory(int argc, char** argv) : provider(NULL)
    {
       //...
    }
    ~AppFactory()
    {
        if (provider != NULL)
            delete provider;
    }
    App buildApp()
    {

        if (some condition)
        {
            provider = new ProviderX(); 
        }
        else
        {
            provider = new ProviderY();
        }

        ClientA clientA(*provider);
        ClientB clientB(*provider);

        App app(clientA, clientB);
        return app;

    } 
    private:
    Provider* provider;

};

int main(int argc, char** argv)
{
    AppFactory factory(argc, argv);
    App app = factory.buildApp();
    return app.run();
}
1 голос
/ 30 декабря 2008

Можно было бы заставить фабрику возвращать объект AppComponents, содержащий все компоненты, которые фабрика создает. То есть как то так:

int main(int argc, char** argv)
{
    AppFactory factory(argc, argv);
    AppComponents components = factory.buildApp();
    return components.getApp().run();
}

Тогда класс AppComponents будет отвечать за удаление вашего провайдера и других объектов.

1 голос
/ 30 декабря 2008

здесь на самом деле недостаточно, чтобы сильно помочь, но, не меняя слишком много, вы можете добавить подсчет ссылок к объекту Provider, и когда клиенты будут уничтожены, они отбрасывают ссылку. Когда ссылка становится равной 0 в объекте Provider, вызывается delete this.

Ваше время жизни и объем немного отрывочны. Почему вы создаете некоторые объекты в стеке, а некоторые - в куче, особенно ваши клиенты?

0 голосов
/ 30 декабря 2008

Просто позвоните

delete provider;
provider = NULL;

в деструкторе ClientA и ClientB. Это также вызовет деструктор провайдера.

...