【C++】友元接口与模板接口

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_41374099/article/details/100154230

前言

这个友元接口与模板接口,说实话实际常常做成什么样我可不知道。


基本友元用法

友元一般用来进行共享数据访问的,查询别人的私有属性,调用他藏起来的方法等等骚操作。友元有三种类型,非成员函数、类、类成员函数,其中最需要注意的是类成员函数做友元的写法。

非成员函数做友元

非成员函数做友元,实现中可以操控被友元类私有属性的方法有两个,一个是传入被友元类的实例,二是在该函数内定义被友元类的实例。

//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();

类成员函数做友元

注意事项

  1. 实现放在类外面
  2. 几个类放在一个头文件时,实现只能放在主函数文件或者同一个头文件,并且把对应的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);
  1. 在两个不同的头文件中定义类时,实现要放在被友元的类的头文件中或者主函数文件中。——先编译友元的文件,再编译被友元的——(一样不能有相应的源文件,也不能有编译过相关类定义的源文件存在。比如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);

  1. 在两个不同的模块(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_<>能做的事都挺复杂,基于很熟练地理解了模板特例化的递归才行。

类型计算就是一个递归的过程,会不断去匹配知道匹配到终止条件。

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/100154230