c++中的代码重用
valarray头文件:valarray 模板类
valarray<int> q_values;
double gpa[5] = {...};
valarray<double> v1; //size 0
valarray<int> v2(8); // an array of 8 elements
valarray<int> v3(10,8); //an array of 8 elements, each set to 10
valarray<double> v4(gpa, 4);//an array of 4 elements initialized to the first 4 elements of gpa;
valarrray<int> v5 = { ...};
- operator[]()
- size()
- sum()
- max()
- min()
使用组合关系,获得了实现但是没有获得接口
当初始化列表包含多个项目时,项目被初始化的顺序为他们被声明的顺序而不是在初始化列表中的顺序
实现has的另一种方法:私有继承,基类的公有成员和保护成员都成为派生类的私有成员
-
公有继承:基类公有方法成为派生类共有方法,继承接口is-a
-
私有继承:基类共有方法称为派生类私有方法,不继承基类的接口 has-a
-
包含将对象作为一个命名的成员对象添加到类中;
-
私有继承将对象作为一个未被命名的继承对象添加到类中
私有继承:private关键字,默认
在私有继承中使用基类方法:强制类型转换
私有继承中,不进行显式类型转换的情况下,不能将指向派生类的引用或指针赋值给基类的引用或指针。多重继承编译器不知道要转换成哪个基类
实现has-a关系大多数情况下使用包含,但是:
-
私有继承能访问到保护类成员
-
同时能重新定义虚函数
保护继承:protect基类的公有成员和保护成员将成为派生类的保护成员
特征,变成派生类的 | 公有继承 | 保护继承 | 私有继承 |
---|---|---|---|
公有成员 | 公有成员 | 保护成员 | 私有成员 |
保护成员 | 保护成员 | 保护成员 | 私有成员 |
私有成员 | 只能通过基类接口访问 | 只能通过基类接口访问 | 只能通过基类接口访问 |
能否隐式向上转换 | 是 | 是(只能在派生类中) | 否 |
使用保护继承或私有继承时,想要让基类方法在派生类外部可用:
-
方法一:定义一个使用该基类方法的派生类方法
-
方法二:使用using声明,即使是私有继承,派生类也可以访问基类的私有方法了
//定义成员 using std::valarray
::min; //只使用成员名,没有圆括号,函数特征标和返回类型
多重继承MI
SingingWaiter = ed;
Worker * pd = &ed; //mbiguous
Worker * pw1 = (Waiter *) &ed;
Worker * pw2 = (Singer *) &ed;
虚基类:从多个类派生出的对象只继承一个基类对象。关键字virtual。关键字重载
class Singer : virtual public Worker {}
class Waiter : virtual public Worker {}
//继承的Singer和Waiter共享一个Worker对象
class SingingWaiter: public Singer, public Waiter{}
为什么不把虚函数成为准则呢?
-
有时可能需要基类的多个拷贝
-
将基类作为虚的开销多
-
缺点:基类为虚时,竞争信息通过中间类自动传给基类
SingingWaiter(,,,) : Waiter(wk,p), Singer(wk,v) {}//error //对于虚类 SingingWaiter(,,,) : Worker(wk), Waiter(wk,p), Singer(wk,v) {}
方法的歧义:
SingingWaiter sw();
sw.Singer::Show();
//更好:在SingingWaiter中重新定义Show()
void SingingWaiter::Show()
{
Singer::show();
}
void SingingWaiter::Show()
{
Singer::show();
Waiter::show();
}
//如何解决显式姓名和id两次?
//模块化,提供一个只显示Worker组件的方法和只显式Waiter和Singer组件的方法然后在SingingWaiter 中组合起来
混合使用虚基类和非虚基类:类包含一个表示所有的虚途径的基类子对象和分别表示各条非虚途径的多个基类子对象。
使用非虚基类,子类没有使用限定符时会导致二义性,而使用虚基类后如果某个名称优先于其他所有名称则使用时不加限定符也不会导致二义性。
优先性:派生类中的名称优先于直接或间接祖先类中的相同名称。
虚二义性规则与访问规则无关,即使是私有的。例子见p567
类模板
template <class Type, int n>
n是非类型参数或表达式参数,限制为只能使用整形、枚举、引用或指针 double n不合法,但是double * n合法
template <typenameType>
不能将模板实例函数放在独立的实现文件中,模板不是函数不能单独编译,必须与特定的模板实例化请求一起使用,应该将所有模板信息放在一个头文件中,使用时包含就行
表达式参数方法的优点在于不用new 和delete释放内存了,提高执行速度,缺点是每个数组大小都会生成自己的模板,但是构造函数更加灵活,通用
常规类的技术可以用于模板类,比如继承等。也可以递归使用模板
ArrayTP<ArrayTP<int,5>, 10> a
等价于 int a[10][5]
默认类型模板参数:
template <class T1, class T2 = int> class Topo{}
不能给函数模板参数提供默认值,当然,可以为所有非类型参数提供默认值
模板具体化
隐式实例化,显式实例化,显式具体化
隐式实例化
implict instantiation
编译器在需要对象之前不会生成类的隐式实例化
ArrayTP<double 30> * pt;
pt = new Array<double, 30>; //现在才生成
显式实例化
explict instantiation
使用关键字template并指出所需类型
template class ArrayTP<string, 100>; //生成实际的类
这种情况下,即使没有创建或提及类对象,编译器也将生成类声明
显式具体化
explicit specialization
对特定类型的模板进行修改
定义如下:
template <> class Classname<special-type-name> {};
部分具体化
partial specialization
//general template
template <class T1, class T2> class Pair {}
//specialization with T2 set to int
template <class T1> class Pair<T1, int> {}
//指定了所有类型,显式具体化
template <> class Pair<int, int> {}
//如果有多个模板可供选择,编译器将使用具体化程度最高的模板
Pair<double, double> p1;
Pair<double, int> p1;//部分具体化
Pair<int, int> p1;//显式具体化
指针提供特殊版本:
template<class T> class F{}
template<class T*> class F{}
如果提供的类型不是指针,将使用通用版本,否则将使用指针版本
神通广大之处:
//general template
template <class T1, class T2, class T3> class Trio {}
//specialization with T3 set to T2
template <class T1, class T2> class Trio<T1, T2, T2> {}
//specialization with T3 and T2 set to T1 *
template <class T1> class Trio<T1, T1 *, T1 *> {}
Trio<int, short, char *> t1;//use general template
Trio<int, short> t2; //use Trio<T1, T2, T2>
Trio<char, char *, char *> //use v3
成员模板:
模板可以用作结构,类或模板类的成员
可以直接在一个(模板)类里面定义模板类,或者在里面声明,在外部定义,外部定义的语法:
template<typename T>
template<typename V>
class beta<T>::hold{
}
template<typename T>
template<typename U>
U beta<T>::blab(Uu, T t){
}
注意点
- 不能写成
template<typename T, typename V>
- 域解析运算符指出hold和blab是beta\<T>成员
模板可以包含类型参数如typename T和非类型参数如int n,还可以包含本身是模板的参数
template< template<typename T> class Thing>
class Crab{
private:
Thing<int> s1; //被替换成如King<int>
}
Crab<King> legs;
//要求King的格式如下:
template<typename T>
class King {
}
模板类和友元:
-
非模板友元
template
class HasFriend { public: friend void counts();//成为所有实例化的友元 friend vod report(HasFriend &);//error,必须指明具体化 friend vod report(HasFriend &); } //必须为要使用的友元定义显式具体化 // non-template friend to the Hasfriend class void report(HasFriend &) {} void report(HasFriend &) {} -
约束模板友元,友元类型取决于类被实例化时的类型
友元函数本身成为模板
//1.在类定义前面声明每个模板函数: template
void counts(); template void report(T &); //2. 在函数中再次将模板声明为友元,根据类模板参数的类型声明具体化 template class HasFriendT { friend void counts(); //必须提供类型参数,作为模板具体化,因为counts没有参数,不能推断 friend void report<>(HasFriend &); //可以不同类型参数 } //3. 为友元函数提供模板化定义 //template friend functions definitions template void counts() { ... } -
非约束模板友元,友元的所有具体化都是类的每个具体化的友元
在类内部声明模板,每个函数具体化都是每个类具体化的友元,友元模板类型和模板类模板类型不同
template
class ManyFriend{ template friend void show2(C &, D &); }
c++11:模板别名
typedef std::array<double,12> arrd12;
11新增:使用模板提供一系列别名:
template<typename T>
using arrtype = std::array<T, 12>;
arrtype<double> g;
arrtype<int> i;
arrtype<string> m;
11允许将using=用于非模板,这是与typedef等价,可读性更强
()