假设有一个基类和一个派生类像下面这样:
class Base
{};
class Derived : public Base
{};
由于类间的上行转换时安全的,我们可以得到如下的正确结果:
Derived* d;
Base* b = static<Base*>d;
下面我们自己写一个简单的SharedPtr智能指针类:
template<typename T>
class SharedPtr
{
private:
T* ptr;
static size_t count;
public:
SharedPtr(T* _ptr) : ptr(_ptr), count(1) {}
~SharedPtr()
{
count--;
if(count == 0)
{
delete ptr;
ptr = NULL;
}
}
SharedPtr(const SharedPtr<T>& other)
{
ptr = other.ptr;
count++;
}
};
编译器在编译时不会通过SharedPtr< Base >pd(pb),因为在编译器替换T时,拷贝构造函数明确了接受类型必须是SharedPtr< Base >,而由SharedPtr< Derived >对象至SharedPtr< Base >对象的转换并不存在,所以编译器会报错。所以这里为了让所有类型都能够进行隐式转换,包括父类与子类间,我们需要将构造函数写成模板的形式,下面重新对上述代码进行了改进:
template<typename T>
class SharedPtr
{
private:
T* _ptr;
static int count;
public:
template<typename U>
SharedPtr(U* ptr)
{
_ptr = static_cast<T*>ptr;
count = 1;
}
~SharedPtr()
{
count--;
if(count == 0)
{
delete _ptr;
_ptr = NULL;
}
}
T* GetPointer() const
{
return _ptr;
}
template<typename U>
SharedPtr(const SharedPtr<U>& other)
{
_ptr = static_cast<T*>(other.GetPointer());
count++;
}
};
上述的构造函数声明为模板函数,参数为U,它可以与T相同,也可以不同,也就意味着它可以接受任何可以转化成T的类型了,比如父子类。这里还定义了GetPointer的方法,因为拷贝构建中传入的对象不一定是属于同一个类的,所以不能保证可以访问到类的私有成员。_ptr = static_cast < T * >(other.GetPointer())这句话其实就是转换的实质了,只要任何可以转成T* 的other* ,都是可以通过编译的,但如果是风马牛不相及的两个类,就不会通过编译。这里有一点要强调一下,我们可以把double转成int(窄化),也可以把int转成double(宽化),但注意double* 与int* 之间是不能相互转的,如果这样写int* a = (int*) (new double(2)),是不能通过编译的,可以用static_cast转换的类要有继承关系。
总结:
1. 请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数;
2. 如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符。