C++学习笔记3--类
- 转换函数
- pointer-like class & function-like class
- 泛型编程
- 模板特化
转换函数
转换函数有两个方向。
##将类转换成别的类##
class Fraction
{
operator double() const
{return (double)(m_numerator/m_denominator);}
}
////////
Fraction f(3,5);
double d=4+f;
编译器行为:首先查找有没有+的重载,没有。那么尝试把f转换成double,找到有operator double(),于是把f转成double。
non-eplicit-one-argument ctor
class Fraction
{
Fraction(int num, int den=1)
:m_numerator(num),m_denominator(den)
{}
Fraction operator+(const Fraction& f)
{return Fraction(...);}
}
////////
Fraction f(3,5);
double d2=f+4;
编译器行为:找fraction有没有+的重载,找到了,但是这个函数的参数是个fraction,不是double,于是编译器看能否把4转换成fraction,于是调用上面的non-explicit-one-argument ctor,把4转换成4/1。
explicit
class Fraction
{
explicit Fraction(int num, int den=1)
:m_numerator(num),m_denominator(den)
{}
operator double() const
{return (double)(m_numerator/m_denominator);}
Fraction operator+(const Fraction& f)
{return Fraction(...);}
}
如果ctor和转换函数并存,对于d2=f+4来说,既可以把f再转换成double,又可以把4转成fraction,产生ambiguous歧义。但是对于d=4+f来说可以并存的。
解决办法:在Fraction ctor前面加上explicit。这样4就不会自动转换成fraction。此时[error] conversion from double to fraction requested.
问题:f+4不调用double转换吗?
标准库中的应用:
operator[]重载这个函数应该返回一个bool值,但是却返回了reference,这是一个__bit_reference类,代码中包括了operator bool() const这样的一个转换函数。
pointer-like classes#
智能指针#
有时候会将类构造成一个指针,这个类具有和指针同样的操作,但是比指针要更智能一些。
struct Foo
{
void method(void){...}
};
template <class T>
class shared_ptr
{
public:
T& operator*() const
{ return *px;}
T* operator->() const
{ return px;}
private:
T* px;
}
////////////////////////////////
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method();
首先创建一个智能指针sp,初始化用new Foo的普通指针。
然后来看 * 这个操作,是取一个对象,那么函数应该返回 *px。
对于->这个操作,通过指针调用函数method,函数应该返回px, 等同于px->method(); 但是当->作用上去,这个->符号被消耗掉了,那么px怎么调用method呢?这里的解释是->这个符号是连续作用的。
迭代器
迭代器要指向容器中的一个元素,所以也是一个point-like class。但是迭代器由于有遍历操作,所以还要实现++,–等操作。
reference operator*() const
{return (*node).data;}
pointer operator->() const
{return &(operator*() );}
///////////////////////////////
list<Foo>::iteration it;
*it;
it->method();
对于*,取得的是一个object。
对于->,意思是调用Foo::method();相当于(*it).method();
相当于(&(*it))->method();
function-like classes
主要是对于()的重载。因为函数主要通过()对函数调用。
C++里面有个类叫pair,第一个元素和第二个元素可以是任意类。现在我们要设计function-like classes,来取它的第一个和第二个元素值。
类里面没有成员函数吗?为什么要用一个类来模拟呢?
对于select1st这个类来说,接收的是Pair类型的class,对()重载返回第一个元素。灰色的地方还有继承关系,这里没有写出来。
调用形式:select1st<pair>()();
第一个小括号创建临时对象,第二个调用operator()重载。
namespace
可以利用不同的namespace进行测试。
template
class template
template<typename T>
对数据类型进行操作,每创造不同类型的对象,就会创造出一份代码。所以也有人说模板会引起代码的膨胀。
function template
templete<class T>
inline
const T& min(const T&a, const T&b)
{
return b < a ? b : a;
}
当对一个类进行比大小的时候,发现对所有的类都是一样的操作,所以把类型变成一个类。
但是在使用的时候不用像类模板一样指明类型,编译器会进行自动推导。
当调用r3=min (r1,r2)的时候,b < a, 但是编译器并不知道怎么比较大小,所以去找b有没有对<的运算符重载。所以还应该在类里对<进行运算符重载。
member template
template <class T1, class T2>
struct pair{
T1 first;
T2 second;
pair(): first(T1()),second(T2()){}
pair(const T1& a, const T2& b):first(a),second(b) {}
template <class U1,class U2>
pair(const pair<U1, U2>&p):
first(p.first),second(p.second){}
};
//////////////////////////////////////
pair<Derived1,Derived2> p;
pair<Base1,Base2> p2(p);
成员模板允许上述对p2初始化的动作。假设有四个类,父类分别是鱼类和鸟类,各自的派生类是鲫鱼和麻雀。那么我们可以把鲫鱼和麻雀组成的pair放入一个由鱼类和鸟类构成的pair中。因为此时赋值操作first(p.first)能够成功,因为鲫鱼是一种鱼类,反之不可。
标准库应用:
对于普通指针有如下用法:
Base1* p=new Derived1;//up-cast
对于上面出现过的智能指针也应该有这个功能
template<typename _Tp>
class shared_ptr:public__shared_ptr<_Tp>
{
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p)
:__shared_ptr<_Tp>(__p){}
}
//////////////////////////////////
shared_ptr<Base1> sptr(new Derived1);
specialization 模板特化
针对模板的特殊类型进行的设计。比如原来template能够传很多类型,但是你现在针对一个bool类进行设计。被绑定之后不用写尖括号里面的东西。
偏特化-个数的偏
原来有两个参数,现在绑定一个。
偏特化-范围
原来的类型是T,现在只是指定指针。
class <typename T>
class C {};
class <typename T>
class C<T*>{};
模板模板参数
模板的第二个参数也是个模板,Container以第一个模板参数作为参数。用户在使用的时候XCLs<string, list> mylist;
指定任意类型和任意容器。但是容器的模板还有第二第三参数,所以直接写<string,list>
编译器无法通过,需要用using...
这句话。
对于容器之外的参数,比如SmartPtr接收一个参数的就可以直接通过。
对于下面这种情况,第二个参数是被制定好的,这不是模板模板参数。