我有以下代码:
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()的新实现工作和方便?
答案 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