第十四章 C++中的代码重用

包含对象成员的类

将类的对象作为新类的成员。称为has-a关系。使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口)。使用包含时,可以获得实现,但是不能获得接口。

explicit关键字的用法:

防止单参数构造函数的隐式转换,例如定义了如下的构造函数:

Student::Student(const string &s, int n);

Student::Student(int n);

使用如下的定义:

Student st("Tom", 3);

st=5;     //这样调用会调用Student::Student(int n)

//生成一个Student对象,将原来的对象覆盖

使用explicit关键字后,上面的调用会报错,使用方法如下:

explicit Student::Student(int n);

构造函数在构造继承对象之前,先构建继承对象与所有成员对象。

类构造函数中的成员初始化列表的初始化顺序按照类声明中变量定义的顺序,而不是初始化列表的顺序。

私有继承

私有继承是C++另一种实现has-a关系的途径。使用私有继承,基类的公共成员和保护成员都将成为派生类的私有成员。

对于继承类的构造函数,成员初始化列表中使用类名而不是成员名来标识构造函数,如:

Student::Student(const char * str, const double *pd, int n)

:std::string(str),valarray<double>(pd,n)    { }

使用作用域解析符来调用基类的方法,如:

double Student::Average() const

{

    if(valarray<double>::size()>0)

        return valarray<double>::sum()/valarray<double>::size();

    else

        return 0;

}

访问基类对象的方法:使用强制类型转换

const string &Student::Name() const

{

    return (const string &) *this;

}

访问基类的友元函数:

os<<(const string &)stu<<std::endl;

多重继承

class SingingWaiter: public Waiter, public Singer {…};

如果不指定继承方式为public,默认为private。

如果多重继承的类含有一个共同基类,则会产生问题。即加入Waiter和Singer均继承自Worker类,则SingingWaiter将会产生两个Worker的副本,在使用多态性时将会产生二义性问题。可以使用强制类型转换解决一部分问题,如:

SingingWaiter ed;

Worker * pw1=(Waiter *)&ed;

Worker * pw2=(Singer *)&ed;

我们可以使用虚基类技术解决这个问题

虚基类

虚基类使得从多个类(基类相同)派生出来的对象只继承一个基类对象。在类声明中使用关键字virtual 即可。

如:

class Singer: virtual public Worker{};

class Waiter: public virtual Worker{};

定义SingingWaiter如下:

class SingingWaiter: public Singer, public Waiter {…};

SingingWaiter对象中只含有Worker对象的一个副本。

C++在基类是虚的时候,禁止信息通过中间类自动传递给基类。即不能通过调用Waiter构造函数来调用Worker类的构造函数构造Worker,故SingingWaiter类的构造函数如下:

SingingWaiter(const Worker & wk, int p=0, int v=Singer::other)

                :Worker(wk), Waiter(wk,p), Singer(wk,v) {}

通过直接调用Worker类的构造函数来构造Worker对象。

若想调用基类中相同的函数,使用作用域解析符

void SingingWaiter::Show()

{

        Singer::Show();

}

类模板

C++的类模板为生成通用的类声明提供了一种更好的办法,模板提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或者函数。

C++提供了很多模板类,如valarray,vector,array和STL(标准模板库)。

由于模板不是函数,他们不能单独编译,所以模板必须与特定的模板实例化请求一起使用。为此,最简单的方法就是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。

定义模板类

表 0-1 模板类stacktp.h

//stacktp.h – a stack template

#ifndef STACKTP_H_

#define STACKTP_H_

template <class Type>

class Stack

{

private:

enum{MAX=10}; //

Type items[MAX];

int top; //栈顶索引

public:

Stack();

bool isempty();

bool push(const Type & item); //压栈

bool pop(Type & item); //出栈

}

 

template<class Type>

Stack<Type>::Stack()

{

top=0;

}

 

template<class Type>

bool Stack<Type>::isempty()

{

return top==0;

}

 

template<class Type>

bool Stack<Type>::isfull()

{

return top==MAX;

}

 

template<class Type>

bool Stack<Type>::push(const Type & item)

{

if(top<MAX)

{

items[top++]=item;

return true;

}

else

return false;

}

 

template<class Type>

bool Stack<Type>::pop(const Type & item)

{

if(top>0)

{

item=items[--top];

return true;

}

else

return false;

}

#endif

非类型参数

template<class T, int n>

int n指定特殊的类型而不是用作泛型名称为非类型或表达式参数。

表达式参数有一些限制,可以为整型、枚举、引用或者指针。double m不合法,但是double * pm和double& rm合法。模板代码不能修改表达式的值,也不能使用使用参数的地址。实例化模板时,用作表达式参数的值必须是常量表达式。

模板的具体化

隐式实例化:

stacktp<int> stp;    //声明一个类并实例化一个对象

或者

stacktp<int> *pst;//仅声明一个指针

pst=new stacktp<int>;    //创建一个对象

显式实例化

template class stacktp<int>;

显式具体化

template <> class Classname<specialized-type-name> {…};

如:template <> class stacktp<const char *>{};//重定义stacktp

部分具体化:

//通用模板

template <class T1, class T2> class Pair{…};

template <class T1> class Pair<T1,int> {…};

template<> class Pair<int, int> {…};

编译器将根据具体化程度最高的模板

Pair<double,double> p1;//通用模板

Pair<double,int> p2;//Pair<T1,int>模板

Pair<int,int> p3;//Pair<int,int>模板

template<class T> class Feeb{…};

template<class T*> class Feeb{…};

Feeb<char> fb1;//调用第一个

Feeb<char *> fb2;//调用第二个

成员模板

模板可以作为结构、类或者模板类的成员

表 0-2 tempmemb.h

//tempmemb.h – template as member of template class

#include<iostream.h>

using std::cout;

using std::endl;

template <typename T>

class beta

{

private:

template <typename V>

class hold

{

private:

V val;

public:

hold(V v=0):val(v){}

void show() const {cout<<val<<endl;}

V Value() const {return val;}

};

hold<T> q;

hold<int> n;

public:

beta(T t,int i):q(t),n(i){}

template<typename U> //函数模板

U blab(U u,T t){return (n.Value()+q.Value)*u/t;}

void show() const{q.show();n.show();}

} ;

将模板用作参数

template <template <typename T> class Thing>

class Crab

Thing必须是一个模板类

Crab<Stack>

模板类和友元

模板类的非模板友元函数

template<class T>

class HasFriend

{

friend void report(HasFriend<T> &);

};

模板类的约束模板友元函数

模板类的约束模板友元函数

先声明模板函数

template <typename T> void counts();

声明模板类

template <typename TT>

class HasFriendT

{

friend void counts<TT>();

friend void report<>(HasFriend<TT> &);

};

模板类的非约束模板友元函数

template <typename T>

class ManyFriend

{

    template <typename C, typename D> friend void show2(C &,D &);

};

04-17 14:33