当前位置: 首页 > 工具软件 > V2EX PLUS > 使用案例 >

C++ Primer Plus 14章

冯开诚
2023-12-01
valarray类 // #inlucde<valarray>
使用时
double gpa[5] = {3.1, 3.5, 3.8, 2.9, 3,3};
valarray<double> v1; //size 0
valarray<double> v2(8); //size 8
valarray<double> v2(10,8); //size 8 value is 10
valarray<double> v4(gpa, 4); //size 4, init by first 4 elements of gpa


//初始化顺序
当初始化列表包含多个项目是,这些项目被初始化的顺序为它们被声明的顺序,而不是它们在初始化列表中的顺序
ex:
class Student
{
string name;
valarray<double> scores;
};
如果其构造函数如下:
Student(const char * str, const double *pt, int n):scores(pd, n), name(str){}
则name成员仍将先初始化,因为其在类中首先被声明,如果代码使用一个成员的值,初始化另一个成员的值或一部分时,初始化顺序将非常重要


//多重继承(multiple inheritance MI)
class Student:private std::string, private std::valarray<double>
{
public:
}
新的构造函数
Student(const char * str, const double *pt, int n):std::string(pd, n), std::valarray<double>(str){}
1 访问父类的方法
 使用类名::方法 --> // return scores.sum()/scores.size();(成员)
                    // return valarray<double>::sum()/valarray<double>::size();
2 访问父类对象
 强制转换 -->
 const string & Student::Name () const
 {
return (const string &) (*this); //因为student继承于string,所以student就是一个string
 }
3 访问父类的友元
 强制转换为父类的引用或指针
 
 
//不同类型的继承
    特征  : public : protected          : private
公有成员变成 子类的公有成员 子类的保护成员 子类的私有成员
保护成员变成 子类的保护成员 子类的保护成员 子类的私有成员
私有成员变成 只能通过父类接口访问 只能通过父类接口访问 只能通过父类接口访问
能够隐式向上转换 可以 在子类中可以 不可以
(子类隐式转为父类)


//C++的访问权限
public: 任意访问
protected: 本类以及子类
private: 本类


//使用using重新定义访问权限
class Student : private std::string, private std::valarray<double>
{
//...
public:
using std::valarray<double>::min; //没有() 以及返回值,参数列表
using std::valarray<double>::max;
//...
}
valarray<double>::min()与valarray<double>::max()在类Student中使用,就像它们是Student的公有方法一样,
同时,其也可以被Student的子类使用,如果在Student中不是 private 的


//多重继承
class SingingWaiter:public Waiter, public Singer{...};
如果不特别注明,那么默认为 private 继承
class SingingWaiter:public Waiter. Singer{...};
Singer为一个私继承父类


多重继承
Worker
Singer Waiter
 SingingWaiter
 
class Worker //虚基类
{
//...
virtual void show() const = 0; //纯虚函数
};
class Waiter : public Worker
{
//...
};
class Singer : public Worker
{
//...
};
class SiningWaiter : public Waiter, public Worker
{
//...
};
//这样的做法会带来一些问题:
1 有多少Worker
2 使用哪个方法


SingingWaiter ed;
Worker *pw = &ed; //这会有二义性,因为一个SingingWaiter有两个Worker,会直接编译出错 所以
Worker *pw1 = (Waiter *) &ed;
Worker *pw2 = (Singer *) &ed;


这里还有一个问题,即包含两个Worker的拷贝,如果Worker基类有一个唯一Id,那么SiningWaiter就有了两个Id,这是常识所违背,并无法使用多态
依次为:Worker Singer Worker Waiter //第一个Worker是Singer的 第二个Worker是Waiter的


//虚基类
virtual base class 用来解决这个问题, 虚基类使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象
class Singer: virtual public Worker{...};
class Waiter: public virtual Worker{...};
class SingingWaiter : public Singer, public Waiter{...};
现在 SingingWaiter 将只包含 Worker 对象的一个拷贝,即Singer和Waiter对象共享一个Worker对象,现在也可以使用多态了
则 依次为  Worker Singer Waiter
//NOTE:如果
class Singer: virtual public Worker{...}; //有virtual
class Waiter: public virtual Worker{...}; //没有virtual
class SingingWaiter : public Singer, public Waiter{...};
则 依次为  Worker Singer Worker Waiter //第一个Worker是SingingWaiter的 第二个Worker是Waiter的 由于Singer为虚继承所以在继承体系中没有Worker
//不过直接new Singer的话,还是会有 Worker 的
class SingingWaiter : public Waiter, public Singer{...};
则 依次为  Worker Worker Waiter Singer //第一个Worker是SingingWaiter的 第二个Worker是Waiter的 
//见下方有关MI的问题 1


//虚基类带来的新的构造函数规则
Work是虚基类,那么
SiningWaiter(const Worker & wk. int p = 0. int v = Singer::other)
:Waiter(wk, p), Singer(wk, p); //错误!
因为Work只有一个 而从Waiter,Singer却有两个入口,所以会出错
SiningWaiter(const Worker & wk. int p = 0. int v = Singer::other)
:Worker(wk), Waiter(wk, p), Singer(wk, p); //对于虚基类,必须这么做;对于非虚基类,这是非法的
//一点想法:如果有一基类是虚拟继承的其基类,那么这个基类的基类相当于直接是这个子类的父类


//函数调用的二义性
Waiter有 show()
Singer有 show()
SingingWaiter 没有 show()
那么,在但继承时,SingingWaiter将自动使用基类的方法,
但在,多重继承中,就会出现二义性,会出错 //如果所有基类中只有一个基类有此方法,则不会有二义性
1 使用作用域操作符 ::
SingingWaiter sw;
sw.Singer::show();
sw.Waiter:show();
2 SingingWaiter 也定义show 方法,并在方法内部定义使用哪个版本


//有关MI的问题
1 混合使用虚基类和非虚基类
 当类通过多条虚拟途径和非虚拟途径继承某个特定的基类时,该类将包含 1 个所有的 virtual 途径的基类子对象和 n个 非 virtual 途径获得的的基类
 
2 虚基类和支配
 成员名的优先性
 1 子类中的名称优先于直接或间接祖先类中的相同名称
 2 virtual 二义性和访问规则无关,即时有的方法是 private 不可访问的 也会导致二义性
 
//类模板
原版Stack类
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10};
Item items[MAX];
int top;
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item &item);
bool pop(Time &item);
}
模板类头
template <typename T>
对于相应的类型,用 T 来代替:
Item items[MAX]; -> T items[MAX];
函数定义也要如此:
bool Stack::push(const Item &item)
{
//...
}
->
template <typename T>
bool Stack<T>::push(const T &item)
{
//....
}
模板类的声明与实现都要放在一个头文件中(.h) <- 除非编译器实现了新的 export 关键字
这是因为模板不是函数,它们不能单独编译,模板必须与特定的模板实例化请求一起使用


//stack.h
#ifndef STACK_H_
#define STACK_H_
//模板类
template <typename T>
class Stack
{
private:
enum{MAX =10};
Type items[MAX];
int top;
public:
Stack();
bool isempty();
bool isfull();
bool push(const T& t);
bool pop(T& t);
};
//定义-<放在与声明的同一个头文件.h中
template <typename T> //这里的<typename T>其实可以换成其他的字符 但是下面Stack<T>等的也要做相应的变化,但不要这么做
bool Stack<T>::Stack()
{
top = 0;
}


template <typename T>
bool Stack<T>::isempty()
{
return top == 0;
}


template <typename T>
bool Stack<T>::isfull()
{
return top == MAX;
}


template <typename T>
bool Stack<T>::push(const T &t)
{
if(top < MAX)
{
items[top++] = item; //注意这里如果是类类型对象,则会有 赋值操作符调用  .operator=()
return true;
}else
{
return false;
}
}


template <typename T>
bool Stack<T>::pop(T &t)
{
if(top > 0)
{
t = items[--top]; //注意这里如果是类类型对象,则会有 赋值操作符调用  .operator=()
return true;
}else
{
return false;
}
}
#endif
如果编译器有 export 关键字,则可以
//stack.h
#ifndef STACK_H_
#define STACK_H_
//在头文件中每个类模板以 export 开始,然后类模板的实现就可以如普通类一样在 .h 中声明,在.cpp中实现了
export template<typename T>
class Stack
{
//...
};
#endif


//使用模板类
Stack<int> kernels; //一个存储int的Stack
Stack<string> colones; //一个存储string的Stack


//深入探讨类模板
刚才的Stack<T>可以用于任何类型吗,如指针?答案是否定的,例如,我们可以
Stack<string> st;
string po;
cin >> po;
st.push(po);
ex:
Stack<char *> st;
并将 string po 替换为
1 char * po;
  在 cin >> po 时出错,因为没有相应存储字符串的内存
2 char po[40];
  在 item = items[--tops]; 时出错,因为相当于item是一个数组名,对数组名赋值是不可行的
3 char * po = new char[40];
  在 items[top++] = item; 时出问题,因为这相当于只有一个 po 对象,即 char * x = char * po, 完全是指针指向内容赋值,最后items中的所有项都指向一片内存
  
//正确使用指针堆栈 <- 创建不同的指针是调用方法的任务
//指针堆栈
#ifndef STACKP_H_
#define STACKP_H_
template <typename T>
class Stack
{
private:
enum {SIZE = 10};
int stacksize;
Type *items;
int top;
public:
explicit Stack(int ss = SIZE);
Stack(const Stack & st);
~Stack(){delete [] items;}
bool isempty(){return top == 0;}
bool isfull(){return top == stacksize;}
bool push(const T& t);
bool pop(Type & t);
Stack & operator=(const Stack & st);
};


template<typename T>
Stack<T>::Stack(int ss):stacksize(ss),top(0)
{
items = new T[stacksize];
}


template<typename T>
Stack<T>::Stack(const Stack &st)
{
if(this == &st)
return;
stacksize = st.stacksize;
top = st.top;
item = new T[stacksize];
for(int i = 0; i < top; i++)
items[i] = st.items[i];
}


template <typename T>
bool Stack<T>::push(const T &t)
{
if(top < MAX)
{
items[top++] = item; //注意这里如果是类类型对象,则会有 赋值操作符调用  .operator=()
return true;
}else
{
return false;
}
}


template <typename T>
bool Stack<T>::pop(T &t)
{
if(top > 0)
{
t = items[--top]; //注意这里如果是类类型对象,则会有 赋值操作符调用  .operator=()
return true;
}else
{
return false;
}
}


template <typename T>
Stack<Type> & Stack<Type>::operator = (const Stack<Type> &st)
{
if(this == &st)
return *this;
delete [] items;
stacksize = st.stacksize;
top = st.top;
items = new Type[stacksize];
for(int i = 0; i < top; i++)
items[i] = st.items[i];
return *this;
}
#endif


//PS
template <typename T, typename S> <-- Stack<int, long> //即可以模板头可以使用多个typename
template <typename T, int i> <-- Stack<int, 12> //即可以模板头还可以指定表达式参数
//表达式参数可以用整形,枚举,引用,指针

//模板的多样性
template <typename T>
class A
{
};
template <typename T>
class B
{
};
用于基类
template <typename T>
class C : public A<T>
{
};
template <typename T1, typename T2>
class C : public A<T1>, public B<T2>
{
};
用于成员
template <typename T>
class D
{
A<T> a;
};
template <typename T1, typename T2>
class C : public A<T1>, public B<T2>
{
A<T1> a;
B<T2> b;
};
模板类继承模板类
template <typename T>
class Queue
{
};


template <typename T1,typename T2>
class Stack : public Queue<T2>
{
private:
enum{MAX =10};
T1 items[MAX];
int top;
public:
Stack();
bool isempty();
bool isfull();
bool push(const T1& t);
bool pop(T1& t);
};


template <typename T1, typename T2> //注意这里
Stack<T1, T2>::Stack()
{
top = 0;
}


PS:
Array< Stack<int> > asi; //an array of stacks of int


1 递归使用模板
ArrayTp< ArrayTp <int, 5>, 10 > twodee;
这使得 twodee 是一个包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组,≈ int twodee[10][5]


2 使用多个类型参数
template <typename T1, typename T2>
class Pair
{
//....
};


3 类模板默认参数
template <typename T1, typename T2 = int>
class Topo
{
//...
};
Topo<double, double> m1; //T1 is double , T2 is double
Topo<double> m2; //T1 is double , T2 is int for default


//模板的具体化
类模板有 隐式实例化 显式实例化 显式具体化 <- 统称为具体化 specialization
1 隐式实例化 (implicit instantiation)
ArrayTP<int , 100> stuff; //隐式实例化 
编译器在需要对象前,不会生成类的隐式实例化
ArrayTP<double, 30> *pt; //一个指针,此时不会实例化
pt = new ArrayTP<double, 30>; //生成了对象,此时会实例化


2 显式实例化
在模板定义的名称空间内,使用关键字 template 并指出所需类型来声明类时,编译器将生成类声明的显式实例化(explicit instantiation)
template class ArrayTP<string, 100>; 将  ArrayTP<string, 100> 声明为一个class,编译器将根据声明生成类的实例


3 显式具体化
显式具体化(explicit specialization) 是特定类型(用于替换模板中的通用类型)的定义。可以在需要时为特殊类型实例化时,对模板进行修改。
template <class T>
class SortedArray
{
//...
};
在比较时 operator<(); 不同的类型可能需要不同的操作,那么就可以
template <> class 
SortedArray<char *>
{
//...
};


4 部分具体化
C++允许部分具体化(partial specialization),即部分限制模板的通用性。ex:部分具体化可以给类型参数之一指定具体类型
//general template
template <typename T1, typename T2> 
class Pair {...};
//specialization with T2 set to int
template <typename T1> //这里不写具体化的部分
class Pair<T1, int>{...}; //这里指定所有部分的话,就导致了显示具体化


如果有多个模板可供选择,则C++选取具体化程度更高的
Pair<double, double> p1; //use general Pair template
Pair<double, int> p2; //use Pair<T1, int> partial specialization
Pair<int, int> p3; //use Pair<int, int> explicit specialization
也可以通过为指针提供特殊版本来部分具体化现有的模板
template<class T> // general version
class Feeb{...};
template<class T*> //pointer partial specializaion
class Feeb{...}; //modified code
如果提供类型不是指针,则编译器使用通用版本;如果是指针,则编译器提供指针具体化版本
Feeb<char> fb1; //use general Feeb template, T is char
Feeb<char *> fb2; //use Feeb T* template, T is char


部分具体化可以这种各种限制
//general template
template <typename T1 typename T2, typename T3> class Trio{...};
//specialization with T3 set to T2
template <typename T1 typename T2> class<T1, T2, T2> Trio{...};
//specialization with T3 and T2 to T1*
template <typename T1> class<T1, T1*, T1*> Trio{...};

Trio<int , short, char *> t1; // use general template Trio<T1, T2, T3>
Trio<int , short> t2; // use Trio<T1, T2, T2>
Trio<char, char *, char *> t3; // use Trio<T1, T1*, T1*>


//成员模板
模板可以用作结构,类或模板类的成员
//tempmemb.h class bata <- 这个模板类将另一个模板类和模板函数作为成员
#include <iostreams>
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; //template objcet
hold<int> n; //template 显式具体化
public:
beta(T t, int i) : q(t), n(i){}
template<typename U> //template method
U blab (U u, T t){return (n.Value() + q.Value() *u / t;)}
void show() const {q.show(); n.show();}
};


//将模板用于参数
模板可以包含类型参数(typename T)和非类型参数(int n)。模板还可以包含本身就是模板的参数~!
template < template <typename T> class Thing >, 其中 template <typename T> class 是类型(等同于 typename T 中的 typename, Thing 是参数 相当于 T)
ex:
Crab类成员声明了两个对象
Thing<int> s1;
Thine<double> s1;
然后声明了一个Crab对象
Crab<Ling> legs;
其中:
king是一个模板类
template <typename T>
class King{...}
然后
Thing<int> 被实例化为 King<int>
Thing<double> 被实例化为 King<double>
ps(Crab类)
template <template <typename T> class Thing >
class Crab
{
private:
Thing<int> s1;
Thing<double> s2; //这两个模板类将根据Crab的模板实例化而实例化,不过int,double是固定的
public:
Crab(){};
bool push(int a, double x){return s1.push(a) && s2.push(x);} //假设Thing有push
bool pop(int a, double x) {return s1.pop(a)  && s2.pop(x);} //假设Thing有pop
}
<-使用
Crab<Stack> nebula; //Stack是一个模板类
还可以混合使用模板参数与常规参数
template <template <typename T> class Thing, typename U, typename V>
class Crab
{
private:
Thing<U> s1;
Thing<V> s2; //这两个模板类将根据Crab的模板实例化而实例化,U,V也是根据编译时模板指定
}
<-使用
Caab<Stack, int, double> nebula; //T = Stack, U = int, V = double


//模板与友元
模板的友元分为三类
1 非模板友元
ex:
template <typename T>
class HasFriend
{
friend void  counts(); //HasFriend的所有实例化后类的友元
};
带参数
template <typename T>
class HasFriend
{
friend void counts(HasFriend<T> &); //(HasFriend &) 则不行,因为不存咋HasFriend这种类,必须写明为HasFriend<T> 
};
//很多C++编译器不支持模板类的友元函数


2 模板类的约束模板友元函数
首先,在类定义前声明每个模板函数
template <typename T> void counts();
template <typename T> void reprot(T &);
然后在函数再次将模板函数声明为友元,则其根据模板参数的类型声明具体化
template <typename TT>
class HasFrieandT
{
//...
friend void counts<TT>();
friend void report< HasFriend<TT> >(HasFriend<TT> &); //因为带参数,也可写为report<>(HasFriend<TT> &);
}
例如:
HasFriendT<int> squack;
实例化为
class HasTriendT
{
friend void counts<int>();
friend void report< HasFriend<int> >(HasFriend<int> &);
}

3 模板类的非约束模板友元函数
template <typename T>
class ManyFriend
{
//...
template <typename C, typename D> friend void show2(C & , D &);
};
show2的具体实例化
void show2<ManyFirend<int>, ManyFirend<int>>(ManyFirend<int> & c, ManyFirend<int> & d);
 类似资料: