版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
前言
这个友元接口与模板接口,说实话实际常常做成什么样我可不知道。
基本友元用法
友元一般用来进行共享数据访问的,查询别人的私有属性,调用他藏起来的方法等等骚操作。友元有三种类型,非成员函数、类、类成员函数,其中最需要注意的是类成员函数做友元的写法。
非成员函数做友元
非成员函数做友元,实现中可以操控被友元类私有属性的方法有两个,一个是传入被友元类的实例,二是在该函数内定义被友元类的实例。
//Test.h
...
class A{
//普通的非成员函数友元
friend int funcTest1(const A& p);
public:
A() :width(50), height(50) {}
A(int x, int y):width(x),height(y){}
~A() = default;
//类的内部同样可以使用友元函数
void func1() {
cout << funcTest1(*this) << endl;
}
//属性不用private就没有封装的意义了
private:
int width;
int height;
};
inline int funcTest1(const A& p) { //外部传入的实例
A a;//友元内部定义的实例
cout << a.height << "," << a.width << endl;
return p.height *p.width;
}
//main.cpp
...
Open p;
cout << funcTest1(p) << endl;
p.func1();
类做友元
//Test.h
...
class A {
friend class B;
public:
A(){}
~A(){}
private:
int m_val;
};
class B {
public:
B(){}
~B(){}
int func() {
cout << "This is in B" << endl;
A a; //在B内部能实例化A后使用各种属性
return a.m_val;
}
};
//main.cpp
...
B b;
b.func();
类成员函数做友元
注意事项
- 实现放在类外面
- 几个类放在一个头文件时,实现只能放在主函数文件或者同一个头文件,并且把对应的cpp文件和编译过该实现的cpp文件都删除掉。都放在源文件中,对应的文件就不用管,可以留着。
//Test.h / Test.cpp
...
class A;
class B {
string name;
int age;
public:
B() :name("22"), age(552) {}
void display(A&);
};
class A {
string name;
int age;
public:
A(string n, int a) :name(n), age(a) {}
friend void B::display(A&);
};
//如果在类内实现就不能拥有A类的私有属性了
void B::display(A& x) {
cout << "B>" << name.data() << "," << age << endl;
cout << "A>" << x.name.data() << "," << x.age << endl;
}
//main.cpp
...
B b;
A a("232", 12);
b.display(a);
- 在两个不同的头文件中定义类时,实现要放在被友元的类的头文件中或者主函数文件中。——先编译友元的文件,再编译被友元的——(一样不能有相应的源文件,也不能有编译过相关类定义的源文件存在。比如a.cpp里写过类A的定义,但是出错了,然后就算你删除了,只有这个a文件存在,错误就一直存在。)
//test.h
class A;
class B {
string name;
int age;
public:
B() :name("22"), age(552) {}
void display(A&);
};
//testFri.h
#include"test.h"
class A {
string name;
int age;
public:
A(string n, int a) :name(n), age(a) {}
friend void B::display(A&);
};
//实现
void B::display(A& x) {
cout << "B>" << name.data() << "," << age << endl;
cout << "A>" << x.name.data() << "," << x.age << endl;
}
//main.cpp
...
B b;
A a("232", 12);
b.display(a);
- 在两个不同的模块(2个头文件加2个源文件)中定义时,被友元的类的头文件要引用友元的头文件,友元的源文件要引用被友元的头文件。比起第三条,就是把实现又放回了友元模块内。
//test.h
//和上面第三条中一样
//testFri.h
//和上面第三条中一样
//testFri.cpp
//do nothing
//test.cpp
#include"test.h"
#include"testFri.h" //编译友元的头文件->编译被友元的头文件->编译这个文件
void B::display(A& x) {
cout << "B>" << name.data() << "," << age << endl;
cout << "A>" << x.name.data() << "," << x.age << endl;
}
//main.cpp
...
B b;
A a("232", 12);
b.display(a);
普通的模板用法
函数模板
//普通用法
template<typename T>void fun(T a,T b){ /*do something*/ }
//使用了默认类型 T = int
template<typename T = int >bool equivalent(const T& a,const T& b){ return !(a<b )&&!(b>a ) ; }//不能直接用 a==b
//定义了变量
template<typename T,int N>void sayNumber(){
for(int i=0;i<N;i++)
//do somthing
}
//定义了变量,给变量一个默认数值
template<typename T,int i = 1>void func(){ }
//使用函数模板
fun<double>(1.2,2.3);
类模板
//使用了默认类型的类模板
template<typename T=float>class A{
T m_v;
public:
A(T a):m_v(a){}
bool operator<(const A& b)const; //类内不用在类型名函数名后加 <T>
};
//类外实现成员函数
template<typename T>bool A<T>::operator<(const A& b)const{ return m_v < b.m_v ; }
//使用类模板
A<> a(1);//使用了默认参数
A<double> b(2);
模板特例化
模板拥有自动推断类型的能力,所以同一个模板能符合许多条件。
利用这一性质,自然可以想到,模板即拥有条件判断的能力!!!
- 先写一个类型完整的模板类,做通例
- 然后写其他的类型不完整的模板类,类名相同,做特例
- 当调用的模板满足自动推断的条件就会执行那个模板,而这几个模板名写成一样的话,当类型、参数传递进来时,会先拿特例去匹配,匹配不到才匹配通例。如果都匹配不到就出异常。
//Vec模板类特例化
template<typename T,int N>class Vec{ /*do something*/ }; //通例
template<>class Vec<float,4>{ /*do something*/ }; //完全特例
template<typename T>class Vec<T,4>{ /*do something*/ }; //部分特例
template<typename T,int N>class Vec<const T,N>{ /*do something*/ }; //有一个参数有const修饰
template<int N>class Vec<float,N>{ /*do something*/ }; //部分特例
模板特例化实例1:传入类型分析
template<typename T, int i>class TMD {};//用于模板型参数
template<typename T1, typename T2>class theSameType {public: enum { ret = false };};//通例 返回false
template<typename T>class theSameType<T, T> {public: enum{ret=true};};//特例1 两个类型相同时返回true
void test01() {
typedef unsigned int uint;//重定义类型并非引入新的类型
typedef uint uint2;
cout << "theSameType<unsigned, uint2>::ret>"<<theSameType<unsigned, uint2>::ret << endl;//true
cout << "theSameType<TMD<unsigned, 2>, TMD<uint2, 2>>::ret>" << theSameType<TMD<unsigned, 2>, TMD<uint2, 2>>::ret << endl;//true
cout << "theSameType<TMD<int, 7>, TMD<int, 3>>::ret>" << theSameType<TMD<int, 7>, TMD<int, 3>>::ret << endl;//false
}
模板特例化实例2:递归
//通过模板特例化解决递归问题
//先递归到底,ret=1,然后返回一个个结果,最后得到ret=10!的结果
template<int N>class aTMP {
public:
enum { ret = N * aTMP<N - 1>::ret };//一直递归,直到模板参数为0,此时会实例化模板特例
};
template<>class aTMP<0> { //特例,作为递归终止条件
public:
enum { ret = 1 };
};
void test02() {
cout << aTMP<10>::ret << endl;
}
模板特例化实例3:类型、数值、代码
/*使用递归是常态*/
template<typename T, int i = 1>class someComputing {
public:
typedef volatile T* retType; //类型计算
enum { retValume = i + someComputing<T, i - 1>::retValume }; //数值计算——递归
static void f() { cout << "someComputing: i = " << i << endl; }
};
template<typename T>class someComputing<T, 0> { //特例——作为模板递归的终止条件
public:
enum { retValume = 0 };
};
template<typename T>class codeComputing {
public:
static void f() { T::f(); }//根据类型调用函数——代码计算
};
void test03() {
someComputing<int>::retType a = 0;//类型计算
cout << sizeof(a) << endl;//64位程序指针8 32位程序指针4
cout << someComputing<int, 500>::retValume << endl;//数值计算
codeComputing<someComputing<int, 99>>::f();//代码计算
}
模板IF_与WHILE_
//通例为空,所以如果不匹配特例一定报错,这是很好的调试手段(虽然我不怎么写项目,也就不怎么调试)
template<bool c,typename Then,typename Else>class IF_{};
//通过模板实现条件判断 IF_<>
template<typename Then,typename Else>class IF_<true,Then,Else>{
public:
typedef Then reType;///类型计算
};
template<typename Then, typename Else>class IF_<false, Then, Else> {
public:
typedef Else reType;///类型计算
};
//通过模板实现循环 WHILE_<>
template<template<typename>class Condition, typename Statement>class WHILE_ {
//嵌套类模板 private是为了隐藏细节
template<typename Statement>class STOP_ {
public:
typedef Statement reType;///类型计算
};
public:
typedef typename IF_<
Condition<Statement>::ret, //bool
WHILE_<Condition, typename Statement::Next>, //Then
STOP_<Statement> //Else
>::reType::reType reType;///类型计算 //Statement
};
使用IF_<>,WHILE_<>能做的事都挺复杂,基于很熟练地理解了模板特例化的递归才行。
类型计算就是一个递归的过程,会不断去匹配知道匹配到终止条件。