将继承类型的boost :: shared_ptr的引用传递给函数

时间:2014-05-18 14:18:12

标签: c++ inheritance boost shared-ptr factory

我有以下代码:

struct A : boost::noncopyable {
  virtual A& copy (const A&);
};
struct B : A {
  virtual B& copy (const B&);
};

typedef boost::shared_ptr <A> APtr; 
typedef boost::shared_ptr <B> BPtr; 
typedef std::string Key;

struct AFactory {
static APtr s_get (const Key& key) {
APtr a;
//... //some factory code
return a;     
}
};

这是C类的旧实现,以及用法

class COld {
public:
  void get (APtr a) {
    a->copy (*_a) ;
  }
private:
  Key _key;  
  APtr _a;
};

int main (void) {
 APtr a = AFactory::s_get ("b"));
 BPtr b = boost::dynamic_pointer_cast <B> (AFactory::s_get ("b"));
 COld c;

 c.get (a); //works good
 c.get (b); //works good

 return 1;
}

我试图对用户更友好,并且如果它是NULL则分配传递的对象

class CNew {
public:
  void get (APtr& a) {
    if (!a) {
      a = AFactory::s_get (_key);
    }
    a->copy (*_a) ;
  }
private:
  Key _key;  
  APtr _a;
};

int main (void) {
 //current usage
 APtr a;
 BPtr b;
 APtr ap = boost::dynamic_pointer_cast <A> (b);

 CNew c;

 c.get (a);
 c.get (b); // here I get the error: 
 c.get (ap); // ok, but i don't like it

 return 1;
}

错误:&#34; APtr&amp;&#34;类型的引用(不是const限定的)不能使用#34; BPtr&#34;

类型的值进行初始化

确保用户可以将BPtr转换为APtr以传递给c.get():

APtr ap = boost::dynamic_pointer_cast <A> (b);
c.get (ap);

但那不是很优雅

我不想分享来自C :: _ a的指针,只需复制数据即可 如何使C :: get()的新实现工作和方便?

1 个答案:

答案 0 :(得分:3)

让我们看看有问题的功能。

void get (APtr& a) {
  if (!a) {
    a = AFactory::s_get (_key);
  }
  a->copy (*_a) ;
}

它需要一个智能指针引用,可能将其指向智能指针资源的一个实例,然后将一些值复制到其中。

问题是APtr&无法引用BPtr的实例。这不应该是那么令人惊讶...如果您可以将BPtr伪装成APtr,然后为其分配一个新的A,那么您违反了类型系统。考虑从c.get(b)返回时,您可以调用b->some_method_A_does_not_have() ...如果b以某种方式持有A的实例,您最终会触发某种运行时错误。毕竟,你不希望B*保持指向A的指针。

最简单的解决方案是按值传递a,并要求对其进行初始化,以便您不必替换其托管资源。

void get (APtr a) {
  if (!a)
    throw std::exception();

  a->copy (*_a) ;
}

对于您的库的使用者来说,这可能不是那么方便,但是所需的样板是最小的,并且行为很容易描述。传递shared_ptr副本的开销很小,除非大量使用get,否则不应该成为问题。如果它被大量使用,要求用户以效率的名义采取额外的步骤是合理的。

另一种可能性是模板化工厂类和get方法。

template <class T>
struct Factory
{
  static boost::shared_ptr<T> s_get()
  {
    boost::shared_ptr <T> t;
    // whatever
    return t;
  }
};

class C
{
public:
  template <class T>
  void get(boost::shared_ptr<T>& t) {
    if (!t) {
      t = Factory<T>::s_get();
    }
    dynamic_cast<A*>(t.get())->copy(*_a);
  }

  // etc