第14章 C++中的代码重用
2020-02-24 10:31:14 8 举报
AI智能生成
C++ Plus 笔记
作者其他创作
大纲/内容
C++中的代码重用
其他
初始化列表的初始化顺序
一般次序
调用基类构造函数,调用顺序按照他们被继承时声明的顺序(右到左)
对派生类新增的成员对象初始化,调用顺序按照他们在类中声明的顺序
执行派生类的构造函数体的顺序
例子
代码
#include<iostream>using namespace std;class Base1{public:\tBase1(int i){cout<<\"constructing Base1 \"<<i<<endl;}};class Base2{public:\tBase2(int j){cout<<\"constructing Base2 \"<<j<<endl;}};class Base3{public:\tBase3(){cout<<\"constructing Base3* \
结果
constructing Base2 2constructing Base1 1constructing Base3*constructing Base1 3constructing Base2 4constructing Base3*
说明
与初始化顺序没有关系
指向指针的引用
http://blog.csdn.net/acs713/article/details/12508049
(1)指向指针的引用,不仅改变了指针所指的对象,也改变了指针本身。
(2)指向指针的引用,可以实现对传递给形参的实参数值的交换。即指向指针的引用可以修改指针的值,在参数传递时是传址。
包含对象成员的类
valarray类的简介
简介
是一个模板类
头文件
#include<valarray>
声明
1
valarray<double> q_values;valarray<int> weights; // 长度为0的 int 数组
2
valarray<int> v2(8); // 长度为8的 int 数组
3
4
C++11
常用方法v. xx()
operator[]()
能访问各个元素
size()
返回包含的元素数
sum()
返回所用元素的总和
max()
返回最大的元素
子主题
返回最小的元素
student 类设计
is-a 关系——继承,获得继承接口和实现
has-a 关系——组合(包含),获得实现,不能获得接口
类设计
class student{private: string name; valarray<double> score;\t....}
能使用 string 和 valarray<double> 类的公有接口来访问和修改 name和scores对象
Student类没有继承string 和 valarray<double> 的公有接口
如果类的接口部分对新类有意义,可以..
例如:希望使用string接口中的operator<( ) 方法将Student对象按姓名进行排序,为此可定义 Student::operator<( ) 成员函数,在内部使用函数string::operator<( )
student示例
总代码
类声明
// studentc.h -- defining a Student class using containment#ifndef STUDENTC_H_#define STUDENTC_H_#include <iostream>#include <string> #include <valarray>class Student{ private: typedef std::valarray<double> ArrayDb; std::string name; // contained object ArrayDb scores; // contained object // private method for scores output std::ostream & arr_out(std::ostream & os) const;public: Student() : name(\"Null Student\
类定义
//public methodsdouble Student::Average() const{ if (scores.size() > 0) return scores.sum()/scores.size(); else return 0;}const string & Student::Name() const{ return name;}double & Student::operator[](int i){ return scores[i]; // use valarray<double>::operator[]()}double Student::operator[](int i) const{ return scores[i];}// private methodostream & Student::arr_out(ostream & os) const{ int i; int lim = scores.size(); if (lim > 0) { for (i = 0; i < lim; i++) { os << scores[i] << \" \"; if (i % 5 == 4) os << endl; } if (i % 5 != 0) os << endl; } else os << \" empty array \
使用
#include <iostream>#include \"studentc.h\
类声明/类定义
重命名
typedef std::valarray<double> ArrayDb;
可以用ArrayDb来替代valarray<double>
构造函数
使用explict关闭隐式转换,使编译阶段出现错误优于在运行阶段出现错误
说明:Student stu1=\"sally\"; xStudent stu2;stu2=9; x //不可以。Student stu1= Student(\"sally\");Stu2 = Student(9); 才可以。
scores(n)表示数组的大小为n
scores(a)表示初始化数组为a
成员函数
// private method for scores output std::ostream & arr_out(std::ostream & os) const;
输出成员数据的valarray中的数据。由友元函数operator<<( ) 引用
double Average() const;
计算并返回平均值
const std::string & Name() const;
返回名字
double & operator[](int i); double operator[](int i) const;
不理解为什么两个都可以存在??
第一个可以修改valarray<>数组中的值第二个不可以修改
Student stu1;stu1[0]、stu1[1]、表示各科的成绩。
友元函数
Student stu2;cin>>stu2;表示输入stu2中的name成员。//只能是一个单词至于valarray<>数组中的元素,可以直接cin>>stu2[n];来直接表示输入
Student stu2;...cout<<stu2;//输出对象的成员数据
建立对象数组,注意,ada[][] 表示的是成绩。
其他说明
初始化顺序
C++紫皮书,整理
使用被包含都对象的接口
例如,需要用到valarray中的sum()函数,可以在声明一个公有函数,用scores中的接口
私有继承
描述关系
也是has-a关系,获得实现,不获得接口
特点
基类的公有成员和保护成员将成为派生类的私有成员,基类的私有成员派生类不能直接访问
Student实例(私有继承版本)
// studenti.cpp -- Student class using private inheritance#include \"studenti.h\"using std::ostream;using std::endl;using std::istream;using std::string;// public methodsdouble Student::Average() const{ if (ArrayDb::size() > 0) return ArrayDb::sum()/ArrayDb::size(); else return 0;}const string & Student::Name() const{ return (const string &) *this;}double & Student::operator[](int i){ return ArrayDb::operator[](i); // use ArrayDb::operator[]()}double Student::operator[](int i) const{ return ArrayDb::operator[](i);}// private method。的金额ostream & Student::arr_out(ostream & os) const{ int i; int lim = ArrayDb::size(); if (lim > 0) { for (i = 0; i < lim; i++) { os << ArrayDb::operator[](i) << \" \"; if (i % 5 == 4) os << endl; } if (i % 5 != 0) os << endl; } else os << \" empty array \
#include <iostream>#include \"studenti.h\
因为私有继承没有具体名,所以直接调用基类的函数。ArrayDb是std::valarray<double>的别名
1(调用基类的方法)
组合关系
double Student::Average() const{ if (scores.size() > 0) return scores.sum()/scores.size(); else return 0;}
return ArrayDb::sum()/ArrayDb::size(); \u00A0
另外如果派生类中有同名函数(不管参数或者其他是否不同),需要有作用限定域符号。如果没有同名函数,可以不用作用限定域,上其实可以不使用。//??还是因为是私有继承所以要限定域??
2(返回基类的私有数据)
string
const string & Student::Name() const{ return name;}
arrayDb
double & Student::operator[](int i){ return scores[i]; // use valarray<double>::operator[]()}
const string & Student::Name() const{ return (const string &) *this;}
double & Student::operator[](int i){ return ArrayDb::operator[](i); // use ArrayDb::operator[]()}
return (const string &) *this;表示返回派生类的基类string部分
return ArrayDb::operator[](i); //调用ArrayDb的operator[]函数
有对象名,所以可以使用通过对象名使用函数。继承只能通过调用基类的函数,但是友元类不属于类,不能用类名显式地限定函数名。友元函数可以访问派生类的私有数据成员,当然也可以访问基类的公有、保护成员
os<<( string &)stu;//输出string的name
和组合差不多
(选择)has-a关系使用包含还是私有继承
一般使用包含来建立has-a关系如果新类的需要访问原有类的保护成员,或需要重新定义虚函数,则应使用继承。
保护继承
基类的公有成员和保护成员将成为派生类的保护成员,基类的私有成员派生类不能直接访问。
与私有继承区别
区别体现在,从派生类中派生另一个类时。
各种继承方式区别
表格
table style=\
私有继承不能隐式向上转换原因:(隐式向上转换说明基类的指针或引用指向派生类对象)
可以的话,就相当于调用了基类接口。
为什么不能在派生类中?
在派生类中使用隐式向上转换:
Student::Student(Student & stu):basestudent(stu)???相当于这样吗?觉得私有继承应该也可以这样做啊
(私有继承、保护继承)使用using重新定义访问权限
问题
使Student能够使用valarray类的sum()方法
方法一
在Student类中声明一个sum()方法
double Student::sum() const{ return std::valarray<double>::sum();}
方法二
using声明来指出派生类可以使用特定的基类成员,即使采用的是私有派生。在派生类的公有部分声明
多重继承(MI)
公有MI
is-a关系
私有、保护MI
has-a关系
类声明格式
Mi问题(主要)
从两个不同基类继承同名方法
从两个或更多相关基类那里继承同一个类的多个实例
实例//worker0(没有派生singerwaiter)
// worker0.h -- working classes#ifndef WORKER0_H_#define WORKER0_H_#include <string>class Worker // an abstract base class{private: std::string fullname; long id;public: Worker() : fullname(\"no one\
// worker0.cpp -- working class methods#include \"worker0.h\
// worktest.cpp -- test worker class hierarchy#include <iostream>#include \"worker0.h\"const int LIM = 4;int main(){ Waiter bob(\"Bob Apple\
i%4==3 表示每四个数,加一endl;后if( i%4 !=0 ) cout<<endl;表示如果总输出不是4的倍数,也要输出空行
1 2 3 45 6 7 89 10....
如果从waiter、singer 上派生出singerwaiter
有多少个worker?
保留两个Worker副本
Worker *pw1=(Waiter *) ed;Worker *pw2=(Waiter *)ed;
哪个方法?
(有多少个worker?)虚基类
解决问题
使SingerWaiter对象只包含Worker对象的一个副本。
虚基类定义
疑问(不要看了)
为什么使用术语虚?
与虚函数没有什么联系,引进新关键字会很麻烦,所以“重载”虚关键字
为什么不抛弃将基类声明为虚的这种方式,使虚行为成为多MI的准则(更像问为什么不抛弃不声明为虚的方式)
可能需要基类的多个拷贝
将基类作为虚的要求程序完成额外的工作
有其缺点(。。)
是否存在麻烦?
新的构造函数规则
单继承
class A{\tint a;public:\tA(int n = 0) :a(n){}\t....};class B :public A{\tint b;public:span class=\"Apple-tab-span\" style=\"white-space:pre\
基类是虚时候
构造方式
对虚基类,除非只需使用该虚基类的默认构造函数,这种用法(直接调用虚基类的构造函数,即使不是直接基类)是合法的也是必须的。对非虚基类,这种用法是不正确的。
(哪个方法?)
Singer 中有show()方法Waiter中有show()方法SingerWaiter newhire( \"Elise Hawks\
解决
作用域解析运算
newhire.Singer::Show( ) ;newhire.Waiter::Show( ) ;
在SingerWaiter中重新定义Show( )//指明使用哪个版本
void SingingWaiter::Show(){ Singer::Show();}
void Singer::Show() const{ cout << \"Category: singer\\"; Worker::Show(); cout << \"Vocal range: \" << pv[voice] << endl;}
void Waiter::Show() const{ cout << \"Category: waiter\\"; Worker::Show(); cout << \"Panache rating: \" << panache << \"\\";}
void Worker::Show() const{ cout << \"Name: \" << fullname << \"\\"; cout << \"Employee ID: \" << id << \"\\";}
加一个属于保护,只显示自身(新增)数据的data()方法。//派生类例外,显示两个直接基类数据的data()方法(其实可以不定义吧)
void Worker::Data() const{ cout << \"Name: \" << fullname << endl; cout << \"Employee ID: \" << id << endl;}
void Waiter::Data() const{ cout << \"Panache rating: \" << panache << endl;}
//publicvoid Waiter::Show() const{ cout << \"Category: waiter\\"; Worker::Data(); Data();}
void Singer::Data() const{ cout << \"Vocal range: \" << pv[voice] << endl;}
void SingingWaiter::Data() const{ Singer::Data(); Waiter::Data();}
//publicvoid Waiter::Show() const{ cout << \"Category: waiter\\"; Worker::Data(); // 可以直接用!!不要忘记了 Data();}
Set()函数也一样,处理方法相同
实例(解决了问题的实例)
// workermi.h -- working classes with MI#ifndef WORKERMI_H_#define WORKERMI_H_#include <string>class Worker // an abstract base class{private: std::string fullname; long id;protected: virtual void Data() const; virtual void Get();public: Worker() : fullname(\"no one\
// workmi.cpp -- multiple inheritance// compile with workermi.cpp#include <iostream>#include <cstring>#include \"workermi.h\"const int SIZE = 5;int main(){ using std::cin; using std::cout; using std::endl; using std::strchr; Worker * lolas[SIZE]; int ct; for (ct = 0; ct < SIZE; ct++) { char choice; cout << \"Enter the employee category:\\" << \"w: waiter s: singer \" << \"t: singing waiter q: quit\\"; cin >> choice; while (strchr(\"wstq\
while (strchr(\"wstq\
case 'w': 记住要单引号
Mi的其它问题
混合使用虚基类和非虚基类
假设B被用作C和D的虚基类,B被用作X和Y的非虚基类,类M,从C,D,X,Y中派生而来,问:洋浦多少个B类子对象?
3个,类M从虚派生祖先(即类C和D)那里共继承一个B类子对象并从每一个非虚派生祖先(即类X和Y)分别继承了一个B类子对象
虚基类和支配(二义性问题)
不是虚基类
class B{public:\tshort q();\t... };class C:public B{public:\tlong q();\t....};class F:public B{\t...};
F a;a.q();//ambiguous
虚基类
class B{public:\tshort q();\t... };class C:virtual public B{public:\tlong q();\t....};class F:public B{\t...};
F a;a.q();//validC中的q()优先于B中的q();
使用虚基类时,如果某个名称优先于其他所有名称,名称不会产生二义性。
虚二义性规则与访问规则无关
class B{public:\tshort q();\t... };class C:virtual public B{private://变为私有\tlong q();\t....};class F:public B{\t...};
不带作用域解析运算符的q() 在F类中或类外都表示为B::q()F a;a.q();//相当于访问了不可访问的q(),所以是不行的。
类模板
作用
提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或函数。
定义、使用
原来
typedef unsigned long Item;class Stack{private: enum {MAX = 10}; // constant specific to class Item items[MAX]; // holds stack items int top; // index for top stack itempublic: Stack(); bool isempty() const; bool isfull() const; bool push(const Item & item); // add item to stack bool pop(Item & item); // pop top into item};
现在
template <class Type>class Stack{private: enum {MAX = 10}; // constant specific to class Type items[MAX]; // holds stack items int top; // index for top stack itempublic: Stack(); bool isempty(); bool isfull(); bool push(const Type & item); // add item to stack bool pop(Type & item); // pop top into item};
template<class Type>class Stack{.....};
要以template<..>开头,可以把class 看做是变量的类型名,该变量接收类型作为其值.
class可用typename 代替
Item items[MAX](原来)——(变成)Type items[Max]其他函数
对于Stack来说,意味着应将声明中所用typedef标识符Item替换成Type
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(Type & item){ if (top > 0) { item = items[--top]; return true; } else return false; }
bool Stack::push( const Item &item){....}
//改为template<class Type>bool Stack<Type>::push(const Type & item){.....}
template<class Type>前缀Stack<Type>限定符
如果在类声明中定义了方法(内联定义),可以省略模板前缀和类限定符
模板的具体实现——如用来处理string对象的栈类称为实例化或具体化
模板不是函数,不能单独编译。一般将所用模板信息放在一个头文件中
使用模板类
// stacktem.cpp -- testing the template stack class#include <iostream>#include <string>#include <cctype>#include \"stacktp.h\"using std::cin;using std::cout;int main(){ Stack<std::string> st; // create an empty stack char ch; std::string po; cout << \
原理
仅在程序包含模板不能生成模板类,必须请求实例化。
Stack<int> kernels;Stack<string> colonels;看到上述声明,编译器按Stack<Type>模板生成两个独立的类声明和两组独立的类方法。Stack<int>使用Int替换模板中所用的Type
与函数模板不同,类模板必须显式地提供所需的类型。
指针栈(小难点)
应用
功能
Type items[MAX]改为Type * items 是想让栈的大小可变化,因此也新增了一个数据成员stacksize
Type *items....items = new Type[stacksize];//items 是指向数组的指针,items[]也可以照常使用
items = new char *[stacksize]表示数组是指向char*的指针数组,
函数的参数里面。指向指针的引用char *&
特殊性
把字符串压入栈实际上是把字符串的地址压入到栈中。从栈中弹出字符串是把地址值复制到out数组中。
数组模板示例和非类型的参数
允许指定数组大小的简单数组模板
方法1
在类中使用动态数组和构造函数参数来提供元素数目,如上的stack
方法2
使用模板参数来提供常规数组大小
方法2代码
T ——类型参数
n——非类型参数/表达式参数//int n, \u00A0n指明了类型
表达式参数限制
与方法一对比
好处
构造函数方法(方法一)使用的是通过new 和delete管理的堆内存,表达式参数方法使用的是为自动变量维护的内存栈,执行速度快
坏处
每种数组大小都声明自己的模板
//下面的声明只生成一个类声明Stack<int>eggs(12);Stack<int>dunkers(13);
构造函数方法(方法2)更通用,它可以将一种尺寸的数组赋给另一种尺寸的数组,也允许改变数组的大小。
virtual T & operator[](int i);——*1 virtual T operator[](int i) const;——*2 //区别?
第一个返回的ar[i]可以作为左值,可以修改ar[i](虽然是私有成员)的内容。(因为返回的是a[i]的引用)
模板的多功能性
模板的用法
用作基类
template<typename T>class Array{private: T entry; .....};
template<typename Type>clsee GrowArray:public Array<Type>{...};
!!!注意写法
组件类
template<typename TP>class Stack{ Array<Tp>ar; ......}
用作其他模板的类型参数
Array< Stack<int> >asi;//下详细
递归使用模板
含义
twodee是包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组。
等价于int twodee[10][5]
可直接用twodee[i][j]表示...
// twod.cpp -- making a 2-d array#include <iostream>#include \"arraytp.h\
使用多个类型参数
默认类型模板参数
可以为类模板类型提供默认值,不能为函数模板参数提供默认值。但都可以为非类型参数提供默认值。
模板的具体化
隐式实例化
根据所需类型,编译器使用通用模板所提供的处方生成具体的类定义
编译器在需要对象之前,不会生成类的隐式实例化
显式实例化
声明必须位于模板定义所在的名称空间中。(一般需要加名称空间,在哪里加?)
虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)
显式具体化(整个模板类)
特定类型(替换模板泛型)的定义
tempalate<typename T>class SortedArray{....};
如果T是const char * 类型时,成员函数很多需要改动,需要重新定义一个关于接受const char*类型的类。
格式
template<>class SortedArray<const char *>{...};
SortedArray<const char *> dates;//使用具体模板当具体模板和通用模板都与实例化请求匹配,编译器使用具体化的版本
早期的编译器格式为class SortedArray<const char *>//没有template<>
(模板类中的成员函数具体化)
好像没有?那需要怎么办?只是某个函数不一样,其他不需要改怎么办?
部分具体化
类型参数之1指定具体类型
例如,模板
为指针提供特殊版本来部分具体化现有模板
template<class T>class Feeb{ .... };template<class T*>class Feeb{ ... };
Feeb<char> fb1;Feeb<char *> fb2; // use Feeb T*;
成员模板
内容
1.模板类将另一个模板类作为其成员
2.模板类将模板函数作为其成员
1.
在类内声明hold类方法
上代码
deta类中,可以直接用hold类中的公有接口,私有接口不可用。
在类外声明hold类方法
deta中
template<typename T>class deta{...private: template<typename V> class hold;.....}
类外声明
template<typename T> template<typename V> class beta<T>::hold {.....};
那hold的成员函数也在外定义的话怎么办?
template<typename T> template<typename V> void beta<T>::hold<V>::show() const {.....}//???
有些编译器不支持类外面定义
hold是类的成员,所以要通过使用作用域解析运算符来完成
2.
在类外定义Ublab方法
类外定义
模板用作参数
// tempparm.cpp ?templates as parameters#include <iostream>#include \"stacktp.h\
作用(应用)一般用于实现STL
在Crab中Thing<int>s1;Thing<double>s2;为其私有数据成员。
template<template<typename T> class Thing>class Crab{private: Thing<int>s1; Thing<double>s2; .....};
template<typename T> 是类型,Thing是参数//template<typename T>——typename/classThing ——Type/T
main中,若有Crab<king> legs;则king一定要是类模板;与Thing匹配,即King要有以下定义template<typename T>class King{ ... };
模板类和友元
非模板友元
函数本身不是模板,有时会使用一个模板参数(或者不使用)
情形
//类声明template<typename T>class HasFirend{public: friend void counts() ......}
//类定义void counts(){....};
count对象不通过对象调用,没有对象参数,如何访问Hasfirend对象?
可以访问全局对象
??体现
2. 可以使用全局指针访问非全局对象
3. 可以创建自己的对象
4. 可以访问独立于对象的模板类的静态数据成员(应用用的是这种)
第4点的应用
//类声明中...class HasFriend{......static int ct;.......friend void counts();.....};
//类定义中void couts(){ cout<<“Int count”<<HasFriend<int>::ct<<\";\"; cout<<\"double count\"<<HasFriend<double>::ct<<\";\";
ct为静态数据成员,意味类的每一个特定的具体化都有自己的静态成员。
cout()是所用HasFriend具体化得友元,每个HasFriend<>对象都可以调用count,且内容一样。
//类声明template<typename T>class HasFriend{.....public: firend void reports(HasFriend<T>&);//template parameter模板参数 ........}
//类定义(与类声明在同一个头文件中)....void reports( HasFriend<double> & hf ){ .......}void reports(HasFriend<int> & hf){...}
这里的友元函数虽然用到了模板参数,但是本身不是模板类声明中/类定义中,没有template<>打头
在类定义中根据不同情形要具体化。(虽然只有一个函数原型)void reports(HasFriend<T> & hf ) 类定义中,直接这样是不可以的
void reports( HasFriend & hf ) 是HasFriend<double>的友元void reports(HasFriend<int> & hf)是HasFriend<int>的友元
应用代码
// frnd2tmp.cpp -- template class with non-template friends#include <iostream>using std::cout;using std::endl;template <typename T>class HasFriend{private: T item; static int ct;public: HasFriend(const T & i) : item(i) {ct++;} ~HasFriend() {ct--; } friend void counts(); friend void reports(HasFriend<T> &); // template parameter};// each specialization has its own static data membertemplate <typename T>int HasFriend<T>::ct = 0;// non-template friend to all HasFriend<T> classesvoid counts(){ cout << \"int count: \" << HasFriend<int>::ct << \"; \"; cout << \"double count: \" << HasFriend<double>::ct << endl;}// non-template friend to the HasFriend<int> classvoid reports(HasFriend<int> & hf){ cout <<\"HasFriend<int>: \" << hf.item << endl;}// non-template friend to the HasFriend<double> classvoid reports(HasFriend<double> & hf){ cout <<\"HasFriend<double>: \" << hf.item << endl;}int main(){ cout << \"No objects declared: \"; counts(); HasFriend<int> hfi1(10); cout << \"After hfi1 declared: \"; counts(); HasFriend<int> hfi2(20); cout << \"After hfi2 declared: \"; counts(); HasFriend<double> hfdb(10.5); cout << \"After hfdb declared: \"; counts(); reports(hfi1); reports(hfi2); reports(hfdb); // std::cin.get(); return 0; }
模板类的约束友元
使友元函数本身成为模板,使类的每一个具体化都获得与友元匹配的具体化
步骤
在类定义的前面声明每个模板函数
template<typename T> void counts();template<typename T> void report( T & hf );
具体位置见下代码
在函数中再次将模板声明声明友元
template<typename TT>class HasFriendT{.... friend void counts<TT>( ); friend void report<>(HasFriendT<TT> & );
count没有参数,必须使用模板参数语法(<TT>)来指明其具体化。report的<>为空,因为可以从函数参数推断出模板类型参数。当然,也可以使用:report<HasFriendT<TT>>(Hasfriend<TT> & )
为友元提供模板定义
template<typename T> void counts(){....}
template<typename T>void report( T & hf ){ ......}
// tmp2tmp.cpp -- template friends to a template class#include <iostream>using std::cout;using std::endl;// template prototypestemplate <typename T> void counts();template <typename T> void report(T &);// template classtemplate <typename TT>class HasFriendT{private: TT item; static int ct;public: HasFriendT(const TT & i) : item(i) {ct++;} ~HasFriendT() { ct--; } friend void counts<TT>(); friend void report<>(HasFriendT<TT> &);};template <typename T>int HasFriendT<T>::ct = 0;// template friend functions definitionstemplate <typename T>void counts(){ cout << \"template size: \" << sizeof(HasFriendT<T>) << \"; \"; cout << \"template counts(): \" << HasFriendT<T>::ct << endl;}template <typename T>void report(T & hf){ cout << hf.item << endl;}int main(){ counts<int>(); HasFriendT<int> hfi1(10); HasFriendT<int> hfi2(20); HasFriendT<double> hfdb(10.5); report(hfi1); // generate report(HasFriendT<int> &) report(hfi2); // generate report(HasFriendT<int> &) report(hfdb); // generate report(HasFriendT<double> &) cout << \"counts<int>() output:\\"; counts<int>(); cout << \"counts<double>() output:\\"; counts<double>(); // std::cin.get(); return 0; }
每种T类型都有自己的友元函数counts( );
模板类的非约束友元
每个(友元)函数具体化都是每个类具体化的友元。
约束模板友元函数——在类外声明的模板具体化。非约束模板友元函数——在类内部声明模板。
非约束友元模板类型参数与模板类类型参数不同
模板别名
2.(C++11)
0 条评论
回复 删除
下一页