这是一个在C++11中使用工厂设计模式的案例。如果有人能解释一下修复调度中的实际情况,我会很高兴的:
基本设计
假设我们希望从工厂中获取不想显式保存的元素,但我们希望确保它们被正确删除。我搜索了一些例子,但似乎没有找到任何例子,所以我们需要做一些测试:
我认为实现目标的方法是使用std::unique_ptr对象,处理所有删除任务。在这里,我只喜欢unique_ptr而不是shared_ptr,因为每当它离开作用域时,它都会调用保留元素的析构函数,因此使用它可以使任何隐藏的复制可见。首先,让我们创建一个能够创建lambda函数的简单工厂:
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()构造函数时,我们都会生成一个函数来输出“我有一件特别的事情要做!”现在让我们创建一个工厂,它将为我们实例化这些类:
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;
};
};让我们现在做一个主()并祈祷我们的手指!
int main()
{
To_do_factory :: get_to_do( "special" ) -> get_function()();
return 0;
}程序输出:
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...问题所在
到目前一切尚好。但是,让我们先保存函数,然后调用它,再次检查结果:
int main()
{
std :: function<void()> to_do = To_do_factory :: get_to_do( "special" ) -> get_function();
to_do();
return 0;
}程序输出:
~Special_to_do()...
~To_do_base()...
I have a special thing for you to do!因此,现在看来返回值实际上是最初创建的函数的副本。为什么这很糟糕?通常,当您传递lambda函数时,它们会捕获其作用域中的一些变量(例如这个)。然而,当复制这些变量时,这些变量可能会超出作用域,从而导致不太大的分段错误。
我有一个答案,其中我写下了到目前为止我推导的所有东西,还有一个“破解”这个设计的方法。但我不知道我在那里做什么,这只是一个案例研究。请帮我弄清楚那里出了什么问题。谢谢!
发布于 2015-11-07 20:39:03
试图解决这个问题
测试get_function()返回值
可能不是关于智能指针,而是get_function().的返回值。我们应该测试一下:
int main()
{
To_do_base* a = new Special_to_do();
std :: function<void()> to_do = a -> get_function();
to_do();
delete a;
return 0;
}该程序输出:
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...这是正确的行为。
试图通过移动来强制传递unique_ptr
也许我们应该设法迫使工厂归还原来的物品。可能通过对返回值执行std::move()来返回rvalue可能会有所帮助:
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 ) );
};
};代码编译并输出如下:
~Special_to_do()...
~To_do_base()...
[1] 329 segmentation fault ./main嗯,这有点让人难堪.如果我们先保存rvalue然后调用它呢?检查之后:没有传递rvalue引用就可以了,但是它仍然用rvalue创建分段错误。
在main()中保存unique_ptr
那么,让我们恢复到lvalue。使用工厂的第一种方法的代码:
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;
}生成一个很好的输出:
Return value saved.
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...嗯,效果很好.但这有点失去了功能工厂的意义。在这里,我们可以生成工厂元素,但只有当我们保存它们时.
保存工厂中的指针
也许有一种方法可以保存工厂的指针。我们应该尝试在工厂中创建一个静态成员变量来处理保存。传播媒介应该是好的。
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...输入如下:
I have a special thing for you to do!
~Special_to_do()...
~To_do_base()...最后,一个好的输出!:)
测试
让我们不要走在前面,我们应该检验一下,兰博达是否真的是我们想要的。我们可以尝试给它一个值来捕捉:
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; };
};产出:
I have 2 special thing for you to do!
~Special_to_do()...
~To_do_base()...这是预期的输出。不过,我还是对这种行为感到困惑.有人能给我一个“更正确”的方法来实现这一点吗?谢谢您:)
发布于 2015-11-08 05:07:15
如果我理解,问题是如果lambda被存储、复制等,如何防止lambda捕获的变量超出作用域(已经销毁)。
这里有两个建议。
Lambdas经常是回调。在超出范围之前,对象从调用它的服务中退出注册。
或者,由lambda捕获的所有指针(其生存期不太安全)都应该是shared_ptrs。如果在调用lambda时对象确实总是可用的,这是很好的。您可能会发现this是一个有用的讨论。
https://stackoverflow.com/questions/33587510
复制相似问题