21

我觉得以前有人问过这个问题,但是我在 SO 上找不到它,在 Google 上也找不到任何有用的东西。也许“协变”不是我要找的词,但这个概念与函数的协变返回类型非常相似,所以我认为它可能是正确的。这是我想做的,它给了我一个编译器错误:

class Base;
class Derived : public Base;

SmartPtr<Derived> d = new Derived;
SmartPtr<Base> b = d; // compiler error

假设这些课程完全充实......我想你明白了。由于某些不清楚的原因,它无法将 a 转换SmartPtr<Derived>为 a 。SmartPtr<Base>我记得这在 C++ 和许多其他语言中很正常,但目前我不记得为什么。

我的根本问题是:执行此分配操作的最佳方法是什么?目前,我将指针从 中拉出SmartPtr,显式地将其向上转换为基本类型,然后将其包装在SmartPtr适当类型的新类型中(请注意,这不会泄漏资源,因为我们的本土SmartPtr类使用侵入式引用计数)。那很长而且很乱,尤其是当我需要将其包装SmartPtr在另一个对象中时……有什么快捷方式吗?

4

6 回答 6

15

SmartPtr<Base>并且SmartPtr<Derived>SmartPtr模板的两个不同实例。Base这些新类不共享继承Derived。因此,你的问题。

执行此分配操作的最佳方法是什么?

 SmartPtr<Base> b = d; 

不调用赋值运算符。这会调用 copy-ctor(副本在大多数情况下会被省略),就像您写的一样:

 SmartPtr<Base> b(d); 

提供一个接受SmartPtr<OtherType>并实现它的复制ctor。赋值运算符也是如此。你必须写出 copy-ctor 和 op= 记住 SmartPtr 的语义。

于 2009-03-12T15:57:01.730 回答
13

复制构造函数和赋值运算符都应该能够采用不同类型的 SmartPtr 并尝试将指针从一个复制到另一个。如果类型不兼容,编译器会抱怨,如果它们兼容,你的问题就解决了。像这样的东西:

template<class Type> class SmartPtr
{
    ....
    template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor

    template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator
};
于 2009-03-12T15:56:22.620 回答
4

模板不是协变的,这很好;想象一下在以下情况下会发生什么:

vector<Apple*> va;
va.push_back(new Apple);

// Now, if templates were covariants, a vector<Apple*> could be
// cast to a vector<Fruit*>
vector<Fruit*> & vf = va;
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples!

为了实现你想要做的事情,SmartPointer 类必须有一个模板化的构造函数,它接受另一个 SmartPointer 或另一个类型的指针。你可以看看 boost::shared_ptr,它就是这样做的。

template <typename T>
class SmartPointer {

    T * ptr;

  public:
    SmartPointer(T * p) : ptr(p) {}
    SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {}

    template <typename U>
    SmartPointer(U * p) : ptr(p) {}

    template <typename U>
    SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {}

    // Do the same for operator= (even though it's not used in your example)
};
于 2009-03-12T16:02:58.847 回答
3

取决于SmartPtr班级。如果它有一个复制构造函数(或者在你的情况下,赋值运算符)SmartPtr<T>,其中 T 是它构造的类型,那么它不会工作,因为即使 T1 和 T2 通过继承相关也SmartPtr<T1>无关SmartPtr<T2>.

但是,如果 SmartPtr 有一个模板化的复制构造函数/赋值运算符,带有模板参数TOther,接受SmartPtr<TOther>,那么它应该可以工作。

于 2009-03-12T15:56:05.883 回答
2

假设您可以控制 SmartPtr 类,解决方案是提供一个模板化构造函数:

template <class T>
class SmartPtr
{
    T *ptr;
public:

    // Note that this IS NOT a copy constructor, just another constructor that takes 
    // a similar looking class.
    template <class O>
    SmartPtr(const SmartPtr<O> &src)
    {
        ptr = src.GetPtr();
    }
    // And likewise with assignment operator.
};

如果 T 和 O 类型兼容,它将起作用,如果不兼容,则会出现编译错误。

于 2009-03-12T15:56:28.253 回答
0

我认为最简单的方法是根据以下内容自动转换为另一个 SmartPtr:

template <class T>
class SmartPtr
{
public:
    SmartPtr(T *ptr) { t = ptr; }
    operator T * () const { return t; }
    template <class Q> operator SmartPtr<Q> () const
    { return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); }
private:
    T *t;
};

请注意,这个实现是健壮的,因为转换运算符模板不需要了解智能指针的语义,因此不需要复制引用计数等。

于 2009-03-12T17:26:22.903 回答