繁体   English   中英

为智能指针编写副本构造函数时遇到问题

[英]Problems writing a copy constructor for a smart pointer

我正在使用的代码具有自己的智能指针实现,该指针实现了简单的引用计数。 是的,我们不应该有自己的实现。 是的,我们应该使用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中解决此问题,但我希望尽可能不要直接访问原始指针。

有什么方法可以编写此代码,以允许派生到基本副本,但不允许对原始指针的常规访问?

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

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

template<typename T>
class CountedPointer {

    // ...

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

    template<typename U> friend class CountedPointer;
};

“ 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模板的技巧可以确定两个类型参数是否可以转换为另一个。

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

如果您有CountedPointer(T * other),则将解决问题。 构造函数和类似的赋值运算符。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM