我正在使用的代码有自己的智能指针实现,可以进行简单的引用计数。是的,我们不应该有自己的实施。是的,我们应该使用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中出现此问题,但如果可能的话,我宁愿不直接访问原始指针。
有什么方法可以写这个来允许派生到基本副本,但是不允许对原始指针进行一般访问?
更新 我已经实现了下面接受的答案,并且效果很好。当我只提供复制构造函数的模板化版本时,我花了一段时间弄清楚为什么我的程序可怕地发生故障,取代了原始的非模板化版本。最后,我意识到编译器不会将模板化版本视为复制构造函数,而是提供默认版本。默认的只是愚蠢地复制内容而不更新计数器,所以我最终得到悬空指针和双重释放。同样的事情也适用于赋值运算符。
答案 0 :(得分:4)
template<typename T>
class CountedPointer {
// ...
template<U>
CountedPointer(const CountedPointer<U> &other);
template<typename U> friend class CountedPointer;
};
答案 1 :(得分:3)
“Alexandrescu通过为封装指针设置访问器函数来避免他的库Loki中的这个问题,但是如果可能的话,我宁愿不直接访问原始指针”
我认为添加原始指针getter的成本远远低于尝试绕过没有原始访问权限的复杂性成本。没有一种语言机制可以在两个不相关的模板类之间转换实例。对于编译器,它们是两个完全不同的东西,在运行时没有任何关系。这就是为什么你一个模板类实例无法访问其他私有。
您可以考虑为所有CountedPointers创建与基类的这种关系。您可能必须在此基类中放置void *。然后你必须自己做所有的检查(是从T开始然后强迫演员......我可以隐含地将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模板的光滑度可以确定两个类型的参数,如果你可以将一个参数投射到另一个。
无论如何,正如您所看到的,这可能是一个更复杂的解决方案,然后只需添加一个getter。能够获得原始指针还有其他好处。例如,您可以将原始指针传递给只接受原始指针的库函数,并将它们用作临时指针。我想,如果你团队中的某个人决定持有原始指针的副本而不是智能ptr,那么这可能是危险的。那个人应该被简单地鞭打。
答案 2 :(得分:0)
如果您有问题,您将解决问题 CountedPointer(T *其他);构造函数和类似的赋值运算符。