c++代码重用

包含对象成员的类

valarray

valarray就是存储数组的数组,可自动调节大小
int gpa[5]={3,1,1,3,2,3};
valarray<int> a1;//空数组
valarray<int> a2(8);//指定长度
valarray<int> a2(10,8);//指定长度,指定数组
valarray<int> a2(apg,4);//常规数组的指定元素

初始化被包含的对象

Son::Son ():Father(){}//初始化派生类中的基类
Son::Son ():String(”丁文“){}初始化派生类中的成员对象string

因为初始化的是派生类的成员对象,所以不用类名来初始化,而是用对象名初始化。这样将会调用成员对象的构造函数。

注意

初始化的顺序是定义时的顺序,成员列表的顺序,但是如果代码使用一个成员的值作为另一个成员的初始化表达式的一部分,初始化顺序就非常重要

使用被包含对象的结构

我们可以使用被包含对象的共有接口

私有继承

has-a关系的实现是通过私有继承的

基类的共有成员和保护成员都将成为派生类的私有成员

访问基类的方法

1由于私有继承方法继承过来的方法是私有的,如果你想让他变成共有,就写一个共有函数里面再调用私有函数

2私有继承可以使用类名和作用域解释符运算符来调用基类方法

访问基类对象

使用类名加上作用域解释符就可以访问基类可以使用基类的方法,那么私有对象本省,答案是对this指针强制转换为基类型

访问基类的友元函数

前面说了共有继承,说可以在不显示转换的情况下基类的引用和指针类型,可以指向派生类。而在私有继承中,如果派生类型对象 想使用基类的友元函数,必须把自己强制转换为基类型对象,

保护继承

保护继承是私有继承的变种,使用关键字protected,基类的共有成员和私有成员都会变成派生类的保护类型

使用using中定义访问权限

通常我们在类中定义一个共有函数,在共有函数再调用私有函数来”改变“访问权限。但是先在我们可以使用using来改变访问权限
class Son:public Father{

public :

using Father:f;//只需要写函数名就可以了

}
Son s;
s.f();//就像使用够用函数一样

多重继承

class A{

}

class B:public A{

}

class C:public A{

}

class D:public B,public C{

}

想上面这样的继承关系会出现一个很好玩的情况,D中有两个A对象(一个B中的A,一个C中的A),那么怎么办呢?

1使用强制转换符


D d;

A a=d;//派生类隐式转为基类

A a1=(C)a;//同理

A a2=(D)a;//同理


虚基类

class A{

}

class B:virtual public A{

}

class C: public virtual A{//先后顺序无所谓

}

class D:public B,public C{

}

如果像上上面定义的话,D中只能有一个A的副本,知道定义之后我们来解决下面三个问题

1为什么要用“虚”

2为什么不抛弃多重继承

3麻烦吗

解答

1写c++规则的人很烂(起始是让你们学习容易记),所以用已有的关键字

2因为有时候只有多继承能解决问题

3这个就单独在下面说了

新的构造函数

class A{
int a;
public :
A(int=0):a(n){};
}

class B:virtual public A{
int b;
public :
B(int m=0,int n=0):A(m),b(n){};
}

class C: public virtual A{
int c;public :
B(int m=0,int n=0):A(m),c(n){};

}

class D:public B,public C{
int d;public :
B(int m=0,int n=0,int q=0):B(m,n),C(m,n),c(q){};
}

上面的继承树种D将间接的A进行初始化,一条路是通过B,另一条是通过C,所以这样会出现冲突,但是编译器还是会通过的 为什么呢?因为系统会在初始化B和C之前先调用A的默认构造函数初始化A ,但是不想调用默认的我们可以通过显示调用构造函数的方法实现下。

class D:public B,public C{
int d;public :
B(int m=0,int n=0,int q=0):A(m),B(m,n),C(m,n),c(q){};
}

上面的只能在虚基类中是合法的,在正常的类中成员初始化列表不能初始化基类的基类的成员。

函数使用问题

class A{
int a;
public :
A(int=0):a(n){};
}

class B:virtual public A{
int b;
public :
B(int m=0,int n=0):A(m),b(n){};
f();
}

class C: public virtual A{
int c;public :
B(int m=0,int n=0):A(m),c(n){};
f();
}

class D:public B,public C{
int d;public :
B(int m=0,int n=0,int q=0):B(m,n),C(m,n),c(q){};//调用默认的构造函数A();
}

上面D如果调用f()会出现二义性,因为编译器不知道调用的是B还是C中的f(),解决办法是使用作用域解释符。像这样二义性的问题还有很多,不一一例举了。注意一点就是,在调用函数的时候,调用的都是他上级基类的函数,注意不要让同一层级的基类函数重复,如果重复就使用作用域解释符就没错了

混合使用虚基类和非虚基类

B作为C,D的虚基类,同时也是X,Y的基类,M继承C,D,X,Y,这样的话M中就有三个B类,一个是C,D的共享的,两个个是X,Y的。

类模板

template <class Type>//class可以用typename代替
//定义类
class A{

private :

int a;

public :

A();

void f(const Type &);

void f2(Type & )

};
//类的构造函数
template <class Tyoe >

A<Type>::A(){

}
//类的普通函数
template<class Type>

void A<type>::f(const Type& a){

}

template<class>A<type>::f2( Type& a){
}
上面每一个函数都已相同的函数头声明template<class Type>

如果在类声明中定义方法(内联定义),则可以省略模板前缀和类限定符。模板类和模板函数一样,它们只是告诉编译器如何生成函数,所以不能单独编译,一般情况下我们把模板单独放在一个头文件中。

使用模板类

使用模板类一定记住,在显示的提供参数来初始化模板类,而模板函数可以根据提供的参数来初始化模板函数

数组模板示例和非类型参数

template<class T,int n>
关键字clas指出T为类型参数,int 指出类型为int。这种参数(指定特殊的类型而不是用作泛型名)称为非类型或表达式参数。假定声明如下
Array<double,12> a;
编译器将定义名为Array<double,12>的类,并且创建一个类型为Array<double ,12>的a对象。报答时参数有一些限制,表达式参数可以是整型,枚举,引用和指针,
不能修改参数的值,也不能使用参数地址(n++和&n)。另外实例化,用作表达式参数的值必须是常量表达式

于构造函数方法相比,这种改变数组的方法有一种有一种优点,那就是为自动变量维护的内存栈,速度更快。

缺点是每个数组大小将会生成自己的模板//生成两个模板
Array<double 12>a1;

Array<double 13>a2;
//生成一个模板
Array<int>a4(12);

Array<int>a4(13);

另一种区别是,构造函数方法更通用,只是因为数组大小作为类成员(而不是硬编码)存储在定义中的,这样可以将一种尺寸的数组赋值给另一个尺寸的数组,也可以创建允许数组大小可变的类。

模板多功能性


可以将用于常规类的技术用于模板类。模板类可以用作基类,也可以用作组件类,还可以用作其他模板类的参数。
template<typename T>
class Array{
private :
T a;
}
template<type Type>
class B:public A<type>{}
template<typename T>
class C
{
A<T> a;
}
A< <B<int> > a;

递归使用模板

另一个模板多功能性的例子,可以递归使用模板,例如
A<A<int,5> ,5> a;
上面a是一个包含10个元素的数组,每一个元素包含一个包含5个元素的数组,变成常规数组就是
int a[10][5];
使用多个类型的参数template<class T1,class T2>
class A{
T1 a;
t2 B;
}
默认类型模板参数
template<class T1,class T2=int>class A{...};
这样的话,你如果省略T2的话编译器将使用int;

模板的具体化


1隐式实例化
A<int,100> a;//没有隐式实例化,因为还没有需要对象,所以只是生成类具体类的定义
pt=new A<int ,30>;//进行类隐式初始化,生成了对象
2显示实例化
template class A<string ,100>;
上面虽然没有提及对象,也将生成类的声明,和隐式实例化一样,也将根据通用模板来生成具体化

3显示具体化
template<> class clssname<specialized_tyoe-name>{};
例如
template<>class A(const char*){
.....
}

4部分具体化

如果有多个模板可以选择,编译器将使用具体化成都最高的模板A
template<class T1>class A{};//T2为int
template<class T1>class A<T1,int>{};//T2为int
<double,double>p1;//使用第一个模板
A<double,int>p1;//使用第二个模板
A<int,int>p2;//使用第二个模板
具体化特性是的能够设置各种限制
teemplate<class T1,classT2,class T3>class A{};
template<class T1,class T2>class A<T1,T2,T3>{};
template<class T1>class A(T1,T1*,T1*){};
A<int,short,char*>t1;//1
A<int ,short>t2;//2
A<char,char*,char*>t3;//3

成员模板


模板可用作结构,类或模板类的成员。

将模板用作参数,模板本身就是模板的参数


template<template <typename T>class A>;
上面的意思是,如果A<B> a;B必须是个模板

模板类和友元


模板类声明也可以友元,模板的友元分为三类

1非模板友元template<typename T>
class A{
public :
friend void f(A<T> &);
}
void f(A<short>&){};//定义的模板类的友元
void f(A<int> &){};
2约束模板友元,即友元的类型取决类被实例化时的类型template<typename T> void f<>(A<T> &);//第一步先定义模板函数
template<typename T> void f2<>();
template<typename T>//第二部定义模板类
class A{
public :
friend void f<>(A<T> &);//显示具体化
friend void f2<T>();//f2没有参数所以,所以必须使用指定的参数来知名显示具体化
}
A<int> a;//第三步定义变量
在定义模板的友元函,就可以不用一个一个写了
3非约束模板友元,即友元的所有集体化都是类的每一具体化的友元

template <class T>
class A{
template <typename C,typename D>friend void f(C&,D&);
}
当我们调用函数时会进行实例化 根据参数生成相应的函数 如果调用函数时传递的是A<int & >就会和下面匹配
void f<A<int &>,A<int &>>(A<int &>,A<int &>){};

c++11模板别名


typedef A<double,12> a;



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值