【问题标题】:Factory pattern using unique_ptr in c++在 C++ 中使用 unique_ptr 的工厂模式
【发布时间】:2015-02-01 15:35:18
【问题描述】:

我在 C++ 中有一个旧的工厂实现,我想在其中使用唯一指针而不是原始指针。我的代码的一个最小示例如下。我有一个基类A 和一个派生类B。在main()中,我将1传递给A中的create方法,现在b1的类型变成了B

#include <iostream>
#include <map>

class A {
 public:
  A() {}
  virtual void Foo() {}
  std::map<int, A *> &registerType() {
    static std::map<int, A *> map_instance;
    return map_instance;
  }
  A *create(int n) { return registerType()[n]; }
};

class B : A {
 public:
  B() { registerType()[1] = this; }
  void Foo() { std::cout << "I am B!\n"; }
};

static B b0;

int main() {
  A *b1 = new A();
  b1 = b1->create(1);
  b1->Foo();

  return 0;
}

现在如果我想将原始指针更改为唯一指针,我自然会得到一个错误集合(以下代码导致错误):

#include <iostream>
#include <map>
#include <memory>

class A {
 public:
  A() {}
  virtual void Foo() {}
  std::map<int, std::unique_ptr<A>> &registerType() {
    static std::map<int, std::unique_ptr<A>> map_instance;
    return map_instance;
  }
  std::unique_ptr<A> create(int n) { return registerType()[n]; }
};

class B : A {
 public:
  B() { registerType()[1](this); }
  void Foo() { std::cout << "I am B too!\n"; }
};

static B b0;

int main() {
  std::unique_ptr<A> b1(new A());
  b1 = b1->create(1);
  b1->Foo();

  return 0;
}

错误是:

In member function 'std::unique_ptr<A> A::create(int)':
use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = A; _Dp = std::default_delete<A>]'
   std::unique_ptr<A> create(int n) { return registerType()[n]; }

In constructor 'B::B()':
no match for call to '(std::map<int, std::unique_ptr<A> >::mapped_type {aka std::unique_ptr<A>}) (B* const)'
   B() { registerType()[1](this); }
                               ^

所以我想知道:

  1. 是否打算在像我这样的情况下使用唯一指针? (我认为答案应该是肯定的!)
  2. 我需要将this 作为unique_ptr 类型传递给registerType 方法。如何将指向当前实例的指针(this 关键字)的所有权传递给unique_ptr? (如果可能或打算可能的话。)
  3. 如果在这里使用唯一指针是一个好习惯,我应该如何实现它?

【问题讨论】:

  • “我自然会收到一堆错误” - 请在您的帖子中包含错误的全文。随意省略出现在多行中的重复项。
  • 我不明白这将如何与std::unique_ptr 一起工作。您当然可以将它们存储在std::map 中,并且可以引用它们。但它们充其量只能被感动;没有复制。将它们从您的地图中移出是可行的,但是为什么要首先麻烦地拥有一张地图。看起来std::shared_ptr 会为您的尝试带来更好的回报。(如果配置正确,您还可以设置共享this。)
  • @a.sam 它以一种非常令人困惑的方式编写,您有一个“创建”函数,它不会创建任何东西,并且通过执行 b1 = b1-&gt;create(1); 会泄漏内存
  • @PeterT:+1 表示您的精彩观点。因此,即使我在AB 中添加析构函数,也无法避免内存泄漏。我对吗?那么,我怎样才能首先避免内存泄漏呢?
  • @a.sam 如果你 new 某事 delete 它。对于您的具体情况,首先不要创建A。将registerTypecreate 设为静态,因为它们所做的只是访问静态数据成员并使用A *b1 = A::create(1); 调用它

标签: c++ this factory smart-pointers unique-ptr


【解决方案1】:

首先,如果有人想要实现工厂模式,可以使用原始指针的可接受方式如下:

#include <iostream>
#include <map>

class A;

class A_Factory {
 public:
  A_Factory() {}
  virtual A *create() = 0;
};

class A {
 public:
  A() {}
  static void registerType(int n, A_Factory *factory) {
    get_factory_instance()[n] = factory;
  }
  static A *create(int n) {
    A *A_instance = get_factory_instance()[n]->create();
    return A_instance;
  }
  virtual void setMyID(int n) {}
  virtual void I_am() { std::cout << "I am A\n"; }
  virtual ~A() {}

 protected:
  int MyID;
  static std::map<int, A_Factory *> &get_factory_instance() {
    static std::map<int, A_Factory *> map_instance;
    return map_instance;
  }
};

class B : public A {
 public:
  B() {}
  void Foo() {}
  void I_am() { std::cout << "I am B " << MyID << "\n"; }
  void setMyID(int n) { MyID = n; }
  ~B() {}

 private:
};

class B_Factory : public A_Factory {
 public:
  B_Factory() { A::registerType(1, this); }
  A *create() { return new B(); }
};

static B_Factory b0_factory;

void caller() {}

int main() {
  A *b1 = A::create(1);
  A *b2 = A::create(1);
  b1->setMyID(10);
  b2->setMyID(20);
  b1->I_am();
  b2->I_am();
  delete b1;
  delete b2;

  return 0;
}

A 是基类,B 是派生类。如果我们将1 传递给A::create(int n),则会生成B 类型的对象。内存是手动管理的,不会有内存泄漏。

关于帖子中的问题:

  1. 是的。 unique_ptr 很棒;尽可能使用它们!
  2. 根据问题中提出的设计,传递this 的所有权在某种程度上是必要的。我想不出一种方法来传递this 的所有权。使用答案中提供的设计,无需传递this 的所有权。
  3. 在上述工厂模式中实现 unique_ptr 如下:
#include <iostream>
#include <map>
#include <memory>

 class A;

 class A_Factory {
 public:
  A_Factory() {}
  virtual std::unique_ptr<A> create_unique() = 0;
};

 class A {
 public:
  A() {}
  static void registerType(int n, A_Factory *factory) {
    get_factory_instance()[n] = factory;
  }
  static std::unique_ptr<A> create_unique(int n) {
    std::unique_ptr<A> A_instance =
        std::move(get_factory_instance()[n]->create_unique());
    return A_instance;
  }

  virtual void setMyID(int n) {}
  virtual void I_am() { std::cout << "I am A\n"; }
  virtual ~A() {}

 protected:
  int MyID;
  static std::map<int, A_Factory *> &get_factory_instance() {
    static std::map<int, A_Factory *> map_instance;
    return map_instance;
  }
};

 class B : public A {
 public:
  B() {}
  void Foo() {}
  void I_am() { std::cout << "I am B " << MyID << "\n"; }
  void setMyID(int n) { MyID = n; }
  ~B() {}

 private:
};

 class B_Factory : public A_Factory {
 public:
  B_Factory() { A::registerType(1, this); }
  std::unique_ptr<A> create_unique() {
    std::unique_ptr<A> ptr_to_B(new B);
    return ptr_to_B;
  }
};

 static B_Factory b0_factory;

 void caller() {}

 int main() {
  std::unique_ptr<A> b1 = std::move(A::create_unique(1));
  std::unique_ptr<A> b2 = std::move(A::create_unique(1));
  b1->setMyID(10);
  b2->setMyID(20);
  b1->I_am();
  b2->I_am();

  return 0;
}

如您所见,无需手动管理内存,内存管理由unique_ptr 处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 1970-01-01
    • 2011-03-13
    相关资源
    最近更新 更多