您的位置:首页 > 编程语言 > C语言/C++

EffectiveC++学习笔记-条款45

2017-08-10 07:08 537 查看
条款45 运用成员函数模板接受所有兼容类型

运用成员函数模板接受所有兼容类型

有一个基类Widget和其子类Button:

//实现内容不重要
class Widget{};
class Button : public Widget{};


假如我们实现自己的智能指针(和C++的shared_ptr做对比)

template<typename T>
class SmartPtr
{
public:
SmartPtr(T* ptr) :m_ptr(ptr)
{
cout << "construct" << endl;
}
SmartPtr(const SmartPtr<T>& other) :m_ptr(other.get())
{
cout << "copy" << endl;
}
//返回原始指针
T* get() const { return m_ptr; }

//其他成员

private:
T* m_ptr;
};
编译器不允许`SmartPtr<Widget> pt1 = SmartPtr<Button>(new Button);`因为在编译期替换T时,拷贝构造函数明确了接受类型必须是`SmartPtr<Widget>`,而由`SmartPtr<Button>`对象至`SmartPtr<Widget>`的转换并不存在,所以编译器报错。

为了转换合理我们必须让copy构造函数可以识别到父类的SmartPtr<Widget>,或者更多其他类。


template<typename Other>
SmartPtr(Other* ptr) :m_ptr(ptr)
{
cout << "construct SmartPtr" << endl;
}

template<typename Other>
SmartPtr(const SmartPtr<Other>& other) : m_ptr(other.get())
{
cout << "copy SmartPtr" << endl;
}


现在我们可以测试一下:

SmartPtr<Widget> pt1 = SmartPtr<Button>(new Button);
cout << "-------------" << endl;
SmartPtr<Widget> pt2(new Button);
cout << "-------------" << endl;
SmartPtr<Button> pt3 = SmartPtr<Button>(new Button);
cout << "-------------" << endl;
SmartPtr<Widget> pt4 = pt3;
cout << "-------------" << endl;

//打印结果
construct
copy SmartPtr
-------------
construct SmartPtr
-------------
construct
-------------
copy SmartPtr
-------------


这里另外声明了一个模板参数Other,它可以与T相同,也可以不同,也就意味着它可以接受任何可以转化成T的类型了,比如父子类

还有对于get函数,因为拷贝构建中传入的对象不一定是属于同一个类的,所以不能保证可以访问到类的私有成员,需要提供一个get函数。

这是和内置的shared_ptr的行为是一致的。

还有一个重要的特征地方:在class类声明泛化copy构造函数(member template),并不会阻止编译器生成它们自己的copy构造函数(non-template),换言之,如果程序中只写了泛化的copy构造函数,那么编译器还是会自动生成一个非泛化的版本出来,如果不想要这个缺省版本,那一定不能偷懒,要两个版本的copy构造函数都要写。

总结

请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数;

如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符。

标准shared_ptr的实现

// TEMPLATE CLASS shared_ptr
template<class _Ty>
class shared_ptr
: public _Ptr_base<_Ty>
{   // class for reference counted resource management
public:
typedef shared_ptr<_Ty> _Myt;
typedef _Ptr_base<_Ty> _Mybase;

constexpr shared_ptr() _NOEXCEPT
{   // construct empty shared_ptr
}

**template<class _Ux>
explicit shared_ptr(_Ux *_Px)
{   // construct shared_ptr object that owns _Px
_Resetp(_Px);
}**

template<class _Ux,
class _Dx>
shared_ptr(_Ux *_Px, _Dx _Dt)
{   // construct with _Px, deleter
_Resetp(_Px, _Dt);
}

constexpr shared_ptr(nullptr_t) _NOEXCEPT
{   // construct empty shared_ptr
}

template<class _Dx>
shared_ptr(nullptr_t, _Dx _Dt)
{   // construct with nullptr, deleter
_Resetp((_Ty *)0, _Dt);
}

template<class _Dx,
class _Alloc>
shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax)
{   // construct with nullptr, deleter, allocator
_Resetp((_Ty *)0, _Dt, _Ax);
}

template<class _Ux,
class _Dx,
class _Alloc>
shared_ptr(_Ux *_Px, _Dx _Dt, _Alloc _Ax)
{   // construct with _Px, deleter, allocator
_Resetp(_Px, _Dt, _Ax);
}

template<class _Ty2>
shared_ptr(const shared_ptr<_Ty2>& _Right, _Ty *_Px) _NOEXCEPT
{   // construct shared_ptr object that aliases _Right
this->_Reset(_Px, _Right);
}

shared_ptr(const _Myt& _Other) _NOEXCEPT
{   // construct shared_ptr object that owns same resource as _Other
this->_Reset(_Other);
}

**template<class _Ty2,
class = typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
void>::type>
shared_ptr(const shared_ptr<_Ty2>& _Other) _NOEXCEPT
{   // construct shared_ptr object that owns same resource as _Other
this->_Reset(_Other);
}**

template<class _Ty2>
explicit shared_ptr(const weak_ptr<_Ty2>& _Other,
bool _Throw = true)
{   // construct shared_ptr object that owns resource *_Other
this->_Reset(_Other, _Throw);
}

#if _HAS_AUTO_PTR_ETC
template<class _Ty2>
shared_ptr(auto_ptr<_Ty2>&& _Other)
{   // construct shared_ptr object that owns *_Other.get()
this->_Reset(_STD move(_Other));
}
#endif /* _HAS_AUTO_PTR_ETC */

shared_ptr(_Myt&& _Right) _NOEXCEPT
: _Mybase(_STD move(_Right))
{   // construct shared_ptr object that takes resource from _Right
}

template<class _Ty2,
class = typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
void>::type>
shared_ptr(shared_ptr<_Ty2>&& _Right) _NOEXCEPT
: _Mybase(_STD move(_Right))
{   // construct shared_ptr object that takes resource from _Right
}

template<class _Ux,
class _Dx,
class = typename enable_if<is_convertible<
typename unique_ptr<_Ux, _Dx>::pointer, _Ty *>::value,
void>::type>
shared_ptr(unique_ptr<_Ux, _Dx>&& _Right)
{   // construct from unique_ptr
_Resetp(_Right.release(), _Right.get_deleter());
}

template<class _Ux,
class _Dx>
_Myt& operator=(unique_ptr<_Ux, _Dx>&& _Right)
{   // move from unique_ptr
shared_ptr(_STD move(_Right)).swap(*this);
return (*this);
}

_Myt& operator=(_Myt&& _Right) _NOEXCEPT
{   // take resource from _Right
shared_ptr(_STD move(_Right)).swap(*this);
return (*this);
}

template<class _Ty2>
_Myt& operator=(shared_ptr<_Ty2>&& _Right) _NOEXCEPT
{   // take resource from _Right
shared_ptr(_STD move(_Right)).swap(*this);
return (*this);
}

~shared_ptr() _NOEXCEPT
{   // release resource
this->_Decref();
}

_Myt& operator=(const _Myt& _Right) _NOEXCEPT
{   // assign shared ownership of resource owned by _Right
shared_ptr(_Right).swap(*this);
return (*this);
}

template<class _Ty2>
_Myt& operator=(const shared_ptr<_Ty2>& _Right) _NOEXCEPT
{   // assign shared ownership of resource owned by _Right
shared_ptr(_Right).swap(*this);
return (*this);
}

#if _HAS_AUTO_PTR_ETC
template<class _Ty2>
_Myt& operator=(auto_ptr<_Ty2>&& _Right)
{   // assign ownership of resource pointed to by _Right
shared_ptr(_STD move(_Right)).swap(*this);
return (*this);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息