首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++11案例研究:如何用智能指针实现工厂设计?[示例和测试]

C++11案例研究:如何用智能指针实现工厂设计?[示例和测试]
EN

Stack Overflow用户
提问于 2015-11-07 20:39:03
回答 2查看 129关注 0票数 1

这是一个在C++11中使用工厂设计模式的案例。如果有人能解释一下修复调度中的实际情况,我会很高兴的:

基本设计

假设我们希望从工厂中获取不想显式保存的元素,但我们希望确保它们被正确删除。我搜索了一些例子,但似乎没有找到任何例子,所以我们需要做一些测试:

我认为实现目标的方法是使用std::unique_ptr对象,处理所有删除任务。在这里,我只喜欢unique_ptr而不是shared_ptr,因为每当它离开作用域时,它都会调用保留元素的析构函数,因此使用它可以使任何隐藏的复制可见。首先,让我们创建一个能够创建lambda函数的简单工厂:

代码语言:javascript
复制
class To_do_base
{
    public:
        std :: function<void()> what_to_do;
        To_do_base() {};
        virtual ~To_do_base() { std :: cout << "~To_do_base()..." << std :: endl; };
        std :: function<void()>& get_function() { return this -> what_to_do; };
};


class Special_to_do : public To_do_base
{
    public:
        Special_to_do()
        { 
            this -> what_to_do = []()
            {
                std :: cout << "I have a special thing for you to do!" << std :: endl;
            };
        };
        ~Special_to_do() { std :: cout << "~Special_to_do()..." << std :: endl; };
};

因此,每当我们调用Special_to_do()构造函数时,我们都会生成一个函数来输出“我有一件特别的事情要做!”现在让我们创建一个工厂,它将为我们实例化这些类:

代码语言:javascript
复制
class To_do_factory
{
    public:
        static std :: unique_ptr<To_do_base> get_to_do( const std :: string& keyword_p )
        {
            if( keyword_p == "special" )
            {
                return std :: unique_ptr<To_do_base>( new Special_to_do() );
            }
            std :: cerr << "Error: Argument_factory::get_argument()..." << std :: endl;
            return nullptr;
        };
};

让我们现在做一个主()并祈祷我们的手指!

代码语言:javascript
复制
int main()
{
    To_do_factory :: get_to_do( "special" ) -> get_function()();
    return 0;
}

程序输出:

代码语言:javascript
复制
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...

问题所在

到目前一切尚好。但是,让我们先保存函数,然后调用它,再次检查结果:

代码语言:javascript
复制
int main()
{
    std :: function<void()> to_do = To_do_factory :: get_to_do( "special" ) -> get_function();
    to_do();
    return 0;
}

程序输出:

代码语言:javascript
复制
~Special_to_do()...
~To_do_base()...
I have a special thing for you to do!

因此,现在看来返回值实际上是最初创建的函数的副本。为什么这很糟糕?通常,当您传递lambda函数时,它们会捕获其作用域中的一些变量(例如这个)。然而,当复制这些变量时,这些变量可能会超出作用域,从而导致不太大的分段错误。

我有一个答案,其中我写下了到目前为止我推导的所有东西,还有一个“破解”这个设计的方法。但我不知道我在那里做什么,这只是一个案例研究。请帮我弄清楚那里出了什么问题。谢谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-11-07 20:39:03

试图解决这个问题

测试get_function()返回值

可能不是关于智能指针,而是get_function().的返回值。我们应该测试一下:

代码语言:javascript
复制
int main()
{
    To_do_base* a = new Special_to_do();
    std :: function<void()> to_do = a -> get_function();
    to_do();
    delete a;
    return 0;
}

该程序输出:

代码语言:javascript
复制
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...

这是正确的行为。

试图通过移动来强制传递unique_ptr

也许我们应该设法迫使工厂归还原来的物品。可能通过对返回值执行std::move()来返回rvalue可能会有所帮助:

代码语言:javascript
复制
class To_do_factory
{
    public:
        static std :: unique_ptr<To_do_base>&& get_to_do( const std :: string& keyword_p )
        {
            if( keyword_p == "special" )
            {
                return std :: move( std :: unique_ptr<To_do_base>( new Special_to_do() ) );
            }
            std :: cerr << "Error: Argument_factory::get_argument()..." << std :: endl;
            exit( -1 );
            return std :: move( std :: unique_ptr<To_do_base>( nullptr ) );
        };
};

代码编译并输出如下:

代码语言:javascript
复制
~Special_to_do()...
~To_do_base()...
[1]    329 segmentation fault  ./main

嗯,这有点让人难堪.如果我们先保存rvalue然后调用它呢?检查之后:没有传递rvalue引用就可以了,但是它仍然用rvalue创建分段错误。

在main()中保存unique_ptr

那么,让我们恢复到lvalue。使用工厂的第一种方法的代码:

代码语言:javascript
复制
int main()
{
    std :: unique_ptr<To_do_base> saved_return_value( To_do_factory :: get_to_do( "special" ) );
    std :: cout << "Return value saved." << std :: endl;
    //std :: function<void()> to_do = To_do_factory :: get_to_do( "special" ) -> get_function(); // WRONG: assigns by copying OR frees dinamically allocated memory after assigning
    std :: function<void()> to_do = saved_return_value -> get_function();
    to_do();
    return 0;
}

生成一个很好的输出:

代码语言:javascript
复制
Return value saved.
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...

嗯,效果很好.但这有点失去了功能工厂的意义。在这里,我们可以生成工厂元素,但只有当我们保存它们时.

保存工厂中的指针

也许有一种方法可以保存工厂的指针。我们应该尝试在工厂中创建一个静态成员变量来处理保存。传播媒介应该是好的。

代码语言:javascript
复制
class To_do_factory
{
    public:
        static std :: vector<std :: unique_ptr<To_do_base>> to_do_list;
        static std :: unique_ptr<To_do_base>& get_to_do( const std :: string& keyword_p )
        {
            if( keyword_p == "special" )
            {
                to_do_list.push_back( std :: unique_ptr<To_do_base>( new Special_to_do ) );
                return to_do_list.back();
                //return std :: unique_ptr<To_do_base>( new Special_to_do() );
            }
            std :: cerr << "Error: Argument_factory::get_argument()..." << std :: endl;
            exit( -1 );
            return to_do_list.back();
        };
};

std :: vector<std :: unique_ptr<To_do_base>> To_do_factory :: to_do_list; 
// global scope I have no idea, where else could I define this...

输入如下:

代码语言:javascript
复制
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...

最后,一个好的输出!:)

测试

让我们不要走在前面,我们应该检验一下,兰博达是否真的是我们想要的。我们可以尝试给它一个值来捕捉:

代码语言:javascript
复制
class Special_to_do : public To_do_base
{
    private:
        int x = 2;
    public:
        Special_to_do()
        { 
            this -> what_to_do = [&]()
            {
                std :: cout << "I have " << this -> x << " special thing for you to do!" << std :: endl;
            };
        };
        ~Special_to_do() { std :: cout << "~Special_to_do()..." << std :: endl; };
};

产出:

代码语言:javascript
复制
I have 2 special thing for you to do!
~Special_to_do()...
~To_do_base()...

这是预期的输出。不过,我还是对这种行为感到困惑.有人能给我一个“更正确”的方法来实现这一点吗?谢谢您:)

票数 1
EN

Stack Overflow用户

发布于 2015-11-08 05:07:15

如果我理解,问题是如果lambda被存储、复制等,如何防止lambda捕获的变量超出作用域(已经销毁)。

这里有两个建议。

Lambdas经常是回调。在超出范围之前,对象从调用它的服务中退出注册。

或者,由lambda捕获的所有指针(其生存期不太安全)都应该是shared_ptrs。如果在调用lambda时对象确实总是可用的,这是很好的。您可能会发现this是一个有用的讨论。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33587510

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档