4

我正在使用的代码有自己的智能指针实现,它执行简单的引用计数。是的,我们不应该有自己的实现。是的,我们应该使用 boost 中的一个或类似的。忍受我。

我发现我想写这样的代码:

...
CountedPointer<Base> base;
...
CountedPointer<Derived> derived;
...
base = derived;

但是,CountedPointer 的复制构造函数有一个这样的原型:

CountedPointer(const CountedPointer<T> &other);

所以上面的代码无法编译,因为它找不到合适的构造函数(或赋值运算符 - 那里是同样的故事)。我尝试用这样的原型重写复制构造函数:

template<U>
CountedPointer(const CountedPointer<U> &other);

但是,我遇到了一个问题,即复制构造函数必须访问它正在复制的对象的私有成员(即原始指针),如果它位于不同的 CountedPointer 特化中,它们是不可见的。

Alexandrescu 在他的Loki库中通过封装指针的访问器函数避免了这个问题,但如果可能的话,我不希望直接访问原始指针。

有什么办法可以写这个来允许派生到基本副本,但不允许一般访问原始指针?

更新: 我已经实现了下面接受的答案,并且效果很好。当我只提供复制构造函数的模板版本,替换原始的未模板版本时,我花了一段时间弄清楚为什么我的程序会出现可怕的段错误。最终,我意识到编译器并不将模板版本视为复制构造函数,而是提供了一个默认版本。默认的只是愚蠢地复制内容而不更新计数器,所以我最终得到了悬空指针和双重释放。同样的事情也适用于赋值运算符。

4

3 回答 3

4

你不能让他们成为朋友吗?像:

template<typename T>
class CountedPointer {

    // ...

    template<U>
    CountedPointer(const CountedPointer<U> &other);

    template<typename U> friend class CountedPointer;
};
于 2009-02-18T14:08:58.190 回答
3

“Alexandrescu 通过封装指针的访问器函数在他的库 Loki 中避免了这个问题,但如果可能的话,我不希望直接访问原始指针”

我认为添加原始指针获取器的成本将远低于试图绕过没有原始访问的复杂性成本。只是没有一种语言机制可以在两个不相关的模板类之间转换实例。对于编译器来说,它们是两个完全不同的东西,在运行时没有任何关系。这就是为什么您的一个模板类实例无法访问其他私人的原因。

您可以考虑使用所有 CountedPointers 的基类创建这种关系。您可能必须在此基类中放置一个 void*。然后你必须自己做所有检查(T是从U派生的然后强制强制转换......我可以隐式地将T转换为U吗?如果是强制转换......等等)但这可能会变得非常复杂. 这是这种方法的一个粗略的开始:

class CountedPointerBase
{
    void* rawPtr;
};

template <class T>
class CountedPointer : public ConutedPointerBase
{
      T* myRawT = reinterpret_cast<T*>(rawPtr);

      template<class U>
      CountedPointer( CountedPointer<U> u)
      {
           // Copying a CountedPointer<T> -> CountedPointer<U>
           if (dynamic_cast<U*>(myRawT) != NULL)
           {
               // Safe to copy one rawPtr to another
           }
           // check for all other conversions
      }
}

查看两种类型是否兼容可能还有很多其他复杂性。也许有一些 Loki/Boost 模板的灵巧性可以确定两个类型参数是否可以转换为另一个。

无论如何,正如您所看到的,这可能是一个比添加吸气剂更复杂的解决方案。能够获得原始指针还有其他好处。例如,您可以将原始指针传递给只接受原始指针的库函数,并将它们用作临时指针。我想,如果您团队中的某个人决定保留原始指针的副本而不是智能指针,这可能很危险。那个人可能应该立即被打鳟鱼。

于 2009-02-18T14:02:14.180 回答
0

如果你有一个 CountedPointer(T* other); 你会解决这个问题。构造函数和类似的赋值运算符。

于 2009-02-18T14:13:58.317 回答