簡體   English   中英

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 ,但我不確定是否保證使用移動構造函數/ operator =從Foo::create返回?

復制elision對您沒有幫助,因為它是一種優化,可能會也可能不會應用。

移動語義確實有幫助,並且您可以在函數返回局部變量時得到保證。 但是,這意味着必須編寫移動構造函數, 必須修改析構函數,以便它不解鎖是從移動對象的資源。

不確定這與工廠模式有什么關系,但要回答你的問題“我是否正確,並且不能保證在這樣的塊中只能調用一次~Foo?”:

允許復制/移動用作返回值的對象(即返回值優化,尤其是返回值優化),但不能保證:

在下列情況下, 允許編譯器,但不要求省略復制和移動(自C ++ 11)類對象的構造 ,即使復制/移動(自C ++ 11)構造函數和析構函數具有可觀察到的副作用。 這是一個優化:即使它發生並且沒有調用copy- / move-構造函數,它仍然必須存在並且可訪問(好像根本沒有發生優化),否則程序是不正確的:

如果函數按值返回類類型,並且return語句的表達式是具有自動存儲持續時間的非易失性對象的名稱,該對象不是函數參數或catch子句參數,並且具有相同的類型(忽略頂級cv-qualification)作為函數的返回類型,然后省略復制/移動(因為C ++ 11)。 構造該本地對象時,它直接在存儲器中構造,否則將移動或復制函數的返回值。 復制省略的這種變體被稱為NRVO,“命名返回值優化”。

一種方法是控制移動構造函數和析構函數中的鎖定/解鎖資源,就像您在問題中提到的那樣。

另一種方法是使用shared_ptr ,這樣你的Foo -object的創建和刪除由RAII風格的shared_ptr包裝器管理。 如果你想讓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();
}

您正在尋找copy-elision

簡而言之,您的代碼保證可以在C ++ 17中使用,如http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html所述

在C ++ 14和之前沒有這樣的保證。

復制省略是在C ++ 11之前存在的優化,並允許編譯器在某些情況下省略復制構造函數調用。

C ++ 11添加了移動語義,並且擴展了復制省略以允許編譯器避免移動或復制(如果移動不可用)對象。

無論編譯器實際執行什么操作,您的類仍必須提供復制或移動構造函數,即使編譯器不會使用它。

C ++ 17引入了“保證copy-elision”,它允許您返回不可移動類的對象,就像您的情況一樣。 請注意,提案明確提到“工廠功能”。 引用:

編寫工廠功能是不可能或非常困難的

提案示例有此示例:

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中可用的東西來使您的代碼無處不在。 您可能希望向對象添加其他狀態,刪除復制構造函數並實現移動構造函數。

這是如何在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};
};

規則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 << "block\n"; }
    void unblock() { std::cout << "unblock\n"; }

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM