RAII和工厂设计模式
RAII and factory design pattern?
假设我有Foo
类:
struct Resource {
void block();
void unblock();
};
struct Foo {
static Foo create() {
Resource resource;
resource.block();
return Foo{resource};
}
~Foo() { resource.unblock(); }
void f() {}
private:
Resource resource;
Foo(Resource resource): resource(resource) {}
};
我是对的,不能保证~Foo
在这样的块中只能调用一次?
{
Foo foo = Foo::create();
foo.f();
}
如果没有保证,如果使用C 11并移动语义,是否可以以某种方式修复?例如,在移动foo中不致电 unblock_resource
,但我不确定是否保证使用移动构造函数/操作员=从 Foo::create
返回?
复制elision不会帮助您,因为这是一种优化,并且可能不会应用或可能不会应用。
移动语义确实会有所帮助,并且您可以保证在局部变量的功能返回方面获得移动。但这意味着 you 必须编写移动构造函数,并且 you 必须修改击曲线,以免它解锁已移动的对象的资源。<<<<<</p>
不确定这与工厂模式有何关系,但是要回答您的问题"在这样的块中只有一次?":
避免使用用作返回值的复制/移动对象(即返回值优化,尤其是命名的返回值优化),但不能保证:
在以下情况下,允许编译器,但 不需要省略副本和移动 - (由于C 11)构造 of 类对象即使复制/移动(自C 11)构造函数和 破坏者具有可观察到的副作用。这是一个优化:甚至 当它发生并且未调用复制/移动构造者时, 仍然必须存在并且可以访问(好像没有发生优化 否则该程序的形式不佳:
如果函数返回按值返回类类型,然后返回 语句的表达是一个非易失性对象的名称 自动存储持续时间,这不是函数参数,或 捕获子句参数,并且具有相同类型(忽略 最高级CV合并)作为函数的返回类型,然后 省略复制/移动(由于C 11)。当该本地对象是 构造,它直接在存储中构造 否则将移动或复制函数的返回值。这 复制省略的变体称为nrvo,"命名返回值 优化"。
正如您在问题中提到的那样
另一种方法可能是使用shared_ptr
,以便由RAII风格的shared_ptr包装器管理Foo
-Object的创建和删除。如果您想保留 Foo
-constructor私有,只有一件事,因为make_shared
无法处理私人构造函数。为了克服这一点,您可以将公共构造函数声明为私人数据类型的参数为参数。有点丑陋,也许由于shared_ptr
-Wrapper而有点笨拙。但也许至少是一些灵感:
struct Foo {
private:
struct private_dummy {};
public:
static shared_ptr<Foo> create() {
shared_ptr<Foo> foo = make_shared<Foo>(private_dummy{});
return foo;
}
~Foo() { cout << "deleted."; }
Foo(struct private_dummy x) { cout << "created."; }
};
void test() {
shared_ptr<Foo> foo = Foo::create();
}
int main() {
test();
//Foo notOK();
}
规则为3/5/0。您定义了攻击器,但不复制/移动构造函数/分配操作员,这是您类型不安全的危险信号。的确,在C 17之前,破坏者可能会被拨打两次,并且在使用C 17时很容易弄乱。
。我建议使用std::unique_ptr
,以免定义任何副本/移动操作或破坏者。即使您要管理的资源不是指针,您也可以使用std::unique_ptr
。看起来像这样:
class Resource {
int handle;
public:
Resource(std::nullptr_t = nullptr)
: handle{}
{}
Resource(int handle)
: handle{ handle }
{}
explicit operator bool() const { return handle != 0; }
friend bool operator==(Resource lhs, Resource rhs) { return lhs.handle == rhs.handle; }
friend bool operator!=(Resource lhs, Resource rhs) { return !(lhs == rhs); }
void block() { std::cout << "blockn"; }
void unblock() { std::cout << "unblockn"; }
struct Deleter {
using pointer = Resource;
void operator()(Resource resource) const {
resource.unblock();
}
};
};
struct Foo {
static Foo create() {
Resource resource{42};
resource.block();
return Foo{resource};
}
void f() {}
private:
std::unique_ptr<Resource, Resource::Deleter> resource;
Foo(Resource resource): resource(resource) {}
};
您正在寻找复制。
简而言之,您的代码保证在C 17中工作,如http://www.open-std.org/jtc1/sc22/wg21/wg21/docs/papers/2015/p0135r0.html0/P>
在C 14中,在没有这样的保证之前。
复制省略是一种优化,在C 11之前存在,并允许编译器在某些情况下省略复制构造函数。
c 11添加了移动语义和副本elision的扩展,以允许编译器避免移动或复制(如果没有移动)。
不管编译器实际上是什么,您的班级仍然必须提供副本或移动构造函数,即使编译器不使用一个。
c 17引入了"保证的复制率",它使您能够像在情况下返回不可移动类的对象。请注意,建议明确提到"工厂功能"。Quote:
编写工厂功能是不可能或非常困难的
提案的示例具有此示例:
struct NonMoveable {
NonMoveable(int);
NonMoveable(NonMoveable&) = delete;
void NonMoveable(NonMoveable&) = delete;
std::array<int, 1024> arr;
};
NonMoveable make() {
return NonMoveable(42); // ok, directly constructs returned object
}
截至今天,Clang和GCC都能够用-std=c++17
标志编译该代码,但不能使用-std=c++14
。
我看到了解决此问题的两种方法:
- 使用C 17:我建议删除副本和移动构造函数(和
operator=
)以确保您的代码不会在早期标准下编译具有错误效果的标准。 - 仅依靠C 14中可用的内容来使您的代码在任何地方工作。您可能需要在对象中添加其他状态,删除复制构造函数并实现MOVE MOVE构造函数。
这是在C 14中如何完成的示例。
class Foo {
public:
Foo() = default;
Foo(const Foo &) = delete;
Foo(Foo &&rvalue) noexcept { std::swap(blocked, rvalue.blocked); }
~Foo() { if (blocked) unblock();
void block() { blocked = true; }
void unblock() { blocked = false; }
private:
bool blocked{false};
};
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 资源管理设计模式
- 工厂设计模式优化
- 下面抽象工厂设计模式的实现是正确的吗
- 实现通用工厂设计模式
- 设计模式的工厂替代方法:具有不同构造函数的类
- 在任何编译语言中实现以下语法(用于工厂设计模式)? 最好是 Kotlin,C++
- 共享对象工厂的设计模式
- RAII和工厂设计模式
- 如何使工厂设计模式在C 中
- 工厂设计模式问题
- 比工厂更好的设计模式
- 通过工厂设计模式向客户隐藏新内容的优势是什么
- 如何在工厂设计模式中设置派生产品类的数据成员
- 工厂设计模式中的纯虚拟功能错误
- 资源和工厂设计模式薄弱
- 抽象工厂设计模式在c++中的应用
- 这是哪种设计模式:工厂方法还是抽象工厂
- 设计模式-使用工厂方法合并不同的类(c++)
- 具有默认实现的抽象工厂设计模式