【问题标题】:passing reference of boost::shared_ptr of inherited type to a function [closed]将继承类型的 boost::shared_ptr 的引用传递给函数[关闭]
【发布时间】:2014-05-18 14:18:11
【问题描述】:

我有以下代码:

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

错误:“APtr &”类型的引用(非 const 限定)不能用“BPtr”类型的值初始化

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

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

但这不是很优雅

我不想共享来自 C::_a 的指针,只是复制数据 如何使 C::get () 的新实现工作方便?

【问题讨论】:

  • 如果您的示例代码至少在语法上是正确的,这将很有帮助。在这种情况下,您应该能够为我们提供一个编译和演示问题的最小示例,不是吗?
  • @Rook 代码我修好了,不过不用编译你也能理解问题
  • 代码不固定...AFactory 的定义完全被破坏,s_get 是私有的,你使用了Class 而不是class,等等。跨度>
  • 您也不能将 B* 传递给期望 A*&amp; 的函数,并且出于同样的原因:您最终会得到一个指向 B 的指针,其中包含实际上是指针的内容到A
  • 你可以只返回一个共享指针,返回一个引用是个坏主意。你可以从B* 构造一个shared_ptr&lt;A&gt;;这是安全和合法的。反过来说,你似乎在尝试,两者都不是。

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


【解决方案1】:

让我们看看有问题的函数。

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

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

问题是APtr&amp; 不能引用BPtr 的实例。这不应该真的那么令人惊讶......如果你可以传入一个伪装成APtrBPtr,然后为其分配一个新的A,那么你就违反了类型系统。考虑到从c.get(b) 返回时,您可以调用b-&gt;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

【讨论】:

  • 对象切片???我看不出对象切片的相关性,我们不处理多态继承中的构造或赋值。
  • 第一个选项不起作用,因为“a”可能会替换其引用的对象。第二个选项不适用,因为我不想返回“c::_a”的共享指针,(这将共享引用计数)例如如果 a::px = 0(boost 中的指针: :shared_ptr) 当 a 被传递时,我们想在 get 中分配它
  • @hellfire769 所以...getParams 到底打算做什么?是打算通过智能指针传递 _a(因此对b 的修改会影响_a),还是只是打算将一些值从_a 复制到b,但让两个对象完全分开?
  • @Rook 正好是第二个。
  • @hellfire769 re: dynamic_pointer_cast:它创建一个新的shared_ptr 实例并修改托管资源的引用计数。这似乎是您要避免的那种事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
  • 2014-04-28
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多