01_导读
本课程目标:
1、泛型编程(Generic Programming)和面向对象(Object-Oriented Programming)虽然分属不同的思维,但它们正是C++技术主流。因此,本课程也探讨template(模板)。
2、深入探讨面向对象之继承关系所形成的对象模型(Object Model),包括隐藏域底层的this指针、vptr(虚指针)、vtbl(虚表),virtual mechanism(虚机制)以及虚函数(vitual function)造成的polymorphism(多态)效果。
02_转换函数(conversion function)
//demo2_1
class Fraction
{
public:
Fraction(int num,int den=1)
:m_numberator(num),m_denominator(den){}
oprator double() const
{
return (double)(m_numberator / m_denominator )
}
private:
int m_numberator;
int m_denominator;
}
//...
Fraction f(3,5);
double d=4+f;//调用operator double() 将f转换为0.4
转换函数分为两类,一类是转出去(把 class对象转为其它),另一类是转进来(把其它转为class对象)。编译器在编译程序最后一行时,首先寻找有没有operator+ 的符号重定义,如果有,并且允许一个整型加一个类对象返回一个double型,则采取此方案。如果上述方案不成立,那么编译器将在类对象f中寻找转换函数,看时候能将f转换为double类型。本次便是采取了这个方案。
注意:
(1)转换函数一定不带参数。
(2)转换函数没有转换类型,而其返回类型就是double型。
(3)转换函数通常要加上const。该加最好加上,否则后续编程可能会带来隐患。
(4)转换函数的意义就是当使用。
(5)任何一个class,只要程序员认为合理都可以定义转换函数,并且可以书写多个转换函数。
03_non-explicit one argument constructor
//demo3_1
class Fraction
{
public:
Fraction(int num,int den=1)
:m_numberator(num),m_denominator(den){}
Fraction operator+ (const Fraction &f)
{
return Fraction(......);
}
private:
int m_numberator;
int m_denominator;
}
//...
Fraction f(3,5);
double d2=f+4;//调用non-explicit ctor 将4转为Fraction
//然后调用operator+
one argument是一个术语,说的是只要一个实参就可以了。这种类的设计师合理的,因为例如数字5,它其实是5/1,因此传递参数只传递一个5过去就可以了。但是如果需要传递两个参数的话,它也能够接收。
这个例子中是将数字4(字面值为int类型)编译器通过调用构造函数将其转换为了Fraction类型。具体分析:当编译到最后一行时,编译器使用+号重载,首先f是Fraction对象,然后调用Fraction类的+号重载函数进行加右边的数据,但是右边是一个整型,按照加号重载的定义,右边应该加一个Fraction类型的数据,因此编译器试图将4通过调用构造函数转换为Fraction类型,这是合理的,因为4可以理解为4/1。因此实现了二者的相加。
在上个例子是把class对象转换成了double类型的数据(转出去),而这个例子是把int型的数据转换成了class对象(转进来)。但是我们口语中所说的转换函数其实指的并不是这一种,而是上个例子。即转出去。
//demo3_2
class Fraction
{
public:
Fraction(int num,int den=1)
:m_numberator(num),m_denominator(den){}
operator double() const
{
return (double)(m_numberator / m_denominator )
}
Fraction operator+ (const Fraction &f)
{
return Fraction(......);
}
private:
int m_numberator;
int m_denominator;
}
//...
Fraction f(3,5);
double d2=f+4;//ERROR ambiguous
转出去和转进来都行得通,而编译器就会引起歧义。因此报错。
//demo3_3
class Fraction
{
public:
explicit Fraction(int num,int den=1)
:m_numberator(num),m_denominator(den){}
operator double() const
{
return (double)(m_numberator / m_denominator )
}
Fraction operator+ (const Fraction &f)
{
return Fraction(......);
}
private:
int m_numberator;
int m_denominator;
}
//...
Fraction f(3,5);
double d2=f+4;//[ERROR]conversion from 'double' to 'Fraction ' requested
explicit关键字用90%会用在构造函数前面。它的意思是不允许将4这样的int类型转换为Fraction,也就是不允许编译器那么的自动。如果没有添加这个关键字,则编译器默认为non-explicit。
04_pointer-like classes
这个class实例化出来的对象像指针,但是它比普通的指针更聪明,我们称之为智能指针。指针允许的用到的地方,这个对象都可以应用。指针允许的操作它都允许。c++2.0有好几个智能指针,但是他们都拥有如图示的指针结构。即在封装的内部有一个真正的指针(天然的指针),然后被封装成一个类,代表一个智能指针。
1、下述代码是pointer-like的第一大分类(智能指针):
//demo4_1
template<class T>
class shared_ptr
{
public:
T& operator*() const
{ return *px; }
T& operator->() const
{ return px; }
shared_ptr(T* p) : px(p) { }
private:
T* px;
long* pn;
......
}
//应用
struct Foo
{
...
void method(void){...}
}
shared_ptr<Foo> sp(new Foo)
Foo f(*sp)
sp->method();//等价于px->method();
在这里,一定会有5678行的四句代码。这是指针最基本的功能。
2、pointer-like的第二种用法:迭代器
//demo4_2
template<class T,class Ref ,class Ptr>
struct __list_iterator
{
typedef __list_iterator<T,Ref,Ptr>self;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
link_type node;
bool operator ==(const self& x)const{ return node == x.node; }
bool operator !=(const self& x)const{ return node != x.node; }
reference operator*()const { return (*node).data; }
pointer operator->() const { return &(operator*()); }
self& operator++(){ node = (link_type)((*node).next);return *this;}
self operator++(int) {self tmp = *this; ++*this; return tmp;}
self& operator--(){node = (link_type)((*node).prev);return *this;}
self operator--(int) {self tmp = *this; --*this;return tmp;}
};
template <class T>
struct __list_node
{
void *prev;
void *next;
T data;
}
迭代器也可以算作一个智能指针,但是它与普通智能指针有所不同。
//demo4_3
//应用示例
reference operator*()const { return (*node).data; }
pointer operator->() const { return &(operator*()); }
list<Foo>::itrator ite;
...
*ite;//获得一个Foo Object
ite->method();
//意思是调用Foo::method()
//相当于(&(*ite))->method();
要理解->和*为什么设计成这样。
05_function-like classes
设计一个类,使之像一个指针,或一个函数。函数必然有个函数名称,然后有个小括号作用上去,而其实上小括号的名称叫做function call operator (函数调用操作符)。下述代码便是库中function-like的一个例子。它的类属性对小括号进行重载,使它实例化的对象能够接受小括号传递的参数,这就类似于函数。一个class中如果试图重载(),我们就称其为function-like。标准库中有许多仿函数。其中的仿函数都继承一些奇特的基类(base class)。
template<class T>
struct identity
{
const T & operator()(const T &x)const{return x;}
};
template<class Pair>
struct select1st
{
const typename Pair::first_type&
operator()(const Pair &x) const
{ return x.first }
}
template<class Pair>
struct select2nd
{
const typename Pair::second_type&
operator()(const Pair &x) const
{ return x.second }
}