工厂和C ++中的动态分配问题

时间:2008-12-29 20:55:59

标签: c++ design-patterns factory

我有一个工厂,可以在我的应用程序中构建寿命最长的对象。这些类型包括ClientAClientB,它们依赖于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对象共享相同的生命周期。在所有答案之后,我现在认为客户端和提供者都应该在堆上分配它的引用传递给App对象,它将在它死亡时负责删除它们。你怎么说?

8 个答案:

答案 0 :(得分:3)

使用共享所有权智能指针非常简单:

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

假设app对象拥有客户端,并且客户端都共享一个提供者。让客户端使用shared_ptr<Provider>,而不是Provider&。只要仍然拥有提供者对象的shared_ptr的副本,该对象将不会被释放。

最好不要复制clientA和clientB,也不要通过按值返回来复制应用程序,而是将客户端移动到应用程序中,然后将应用程序本身移动到返回的对象中。即将推出的C ++版本也是可能的。但是目前,你要么指向它们(使用shared_ptr),要么继续复制它们。另一个选择是使用auto_ptr,它具有伪转移所有权语义。但该模板存在一些固有问题。所以你应该避免使用它。

答案 1 :(得分:2)

使提供者成为AppFactory的实例变量。然后使提供者成为智能指针或在AppFactory的dtor中删除它。

答案 2 :(得分:2)

除非App的构造函数复制客户端,否则它们也需要new()ed - 当前的那些在堆栈上分配,并在返回应用程序时被删除。

我想您可能要小心创建哪些对象 - 例如将调试语句放在客户端的构造函数中。

你可能想要的是让提供者被引用计数,让每个客户端只减少引用计数,但这是很多工作。

或者让AppFactory拥有提供者。

答案 3 :(得分:1)

这里的帮助不够多,但是如果没有太多改变,你可以向Provider对象添加引用计数,当客户端被破坏时,它们会丢弃引用。当引用在Provider对象中变为0时,则调用delete this。

你的生命和范围有点粗略。为什么要在堆栈上创建一些对象,在堆上创建一些对象 - 特别是您的客户端?

答案 4 :(得分:1)

一个选项是让工厂返回一个包含工厂构造的所有组件的AppComponents对象。即像这样的东西:

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

然后,AppComponents类将负责删除您的提供者和其他对象。

答案 5 :(得分:1)

使提供者成为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();
}

答案 6 :(得分:1)

你说“提供者和App对象共享相同的生命周期”但要注意在C ++中,以下代码片段......

App app(clientA, clientB);
return app;

...正在返回App对象的副本:因此您可能(具体取决于编译器,请参阅http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx)实际上有两个App对象实例(一个在内部) AppFactory :: buildApp()方法和主函数内的另一个。)

要回答您的问题,我认为我同意您的编辑:将指针传递给您的App构造函数,将其存储为App实例的成员数据,并在销毁App实例时将其删除。但是,除此之外,您还可以更改代码以确保不复制App实例:例如,在堆上分配App实例,更改AppFactory :: buildApp()方法以返回指向应用程序,并删除主函数末尾的App实例。

答案 7 :(得分:0)

致电

delete provider;
provider = NULL;

在ClientS和ClientS的析构函数中。这也将调用提供者的析构函数。