一 类模板

(1)概念

类模板中定义的函数参数类型或者属性等数据类型可以参数化。
template <typename 类型参数1,typename 类型参数2,...>
class 类型{
	//...
}
#include <iostream>
using namespace std;

template <typename T,typename U>
class Test
{
private:
	T a;
	U b;
public:
	Test(T a, U b)
	{
		this->a = a;
		this->b = b;
	}
	void show()
	{
		cout << a << " " << b << endl;
	}
};
int main(int argc,char*argv[])
{
	//Test t(1, 'a');  
	Test<int, char> t(1, 'a');  //类模板创建一定要显示调用
	t.show();
	return 0;
}

(2)模板类的继承

#include <iostream>
using namespace std;
template <typename T>
class  Parent
{
protected:
	T a;
public:
	Parent(T a)
	{
		this->a = a;
	}
	void show()
	{
		cout << a << endl;
	}
};
class Child:public Parent<int>  //模板类派生普通类,继承的同时对基类实例化
{
public:
	Child(int a) :Parent(a)
	{

	}
	void show()
	{
		cout << a << endl;
	}
};

template <typename T,typename U>
class Child2 :public Parent<T>   //模板类派生模板类,继承的同时不需要对parent实例化
{
private:
	U b;
public:
	Child2(T a, U b) :Parent<T>(a)
	{
		this->b = b;
	}
	void show()
	{
		cout << this->a << " " << this->b << endl;
	}
};
int main(int argc,char*argv[])
{
	Child c1(1);
	c1.show();

	Child2<int, double> c2(1, 1.11);
	c2.show();
	return 0;
}

(3)模板类的声明

#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
	Test(T a);
	void show();
	~Test();
protected:
private:
	T a;
};

template <typename T>
Test<T>::Test(T a)  //Test<T>表示Test是模板类,不是普通类
{
	this->a = a;
}

template <typename T>
void Test<T>::show()
{
	cout << a << endl;
}
template <typename T>
Test<T>::~Test()
{

}
int main(int argc,char*argv[])
{

	return 0;
}

(4) 模板类中的static

#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
	Test(T a);
	void show();
	~Test();
protected:
private:
	T a;
public:
	static int count;
};
template <typename T>
int Test<T>::count = 0;

template <typename T>
Test<T>::Test(T a)  //Test<T>表示Test是模板类,不是普通类
{
	this->a = a;
	count++;
}

template <typename T>
void Test<T>::show()
{
	cout << a << endl;
}
template <typename T>
Test<T>::~Test()
{

}
int main(int argc,char*argv[])
{
	Test<int> t1(1);
	Test<int> t2(1);
	Test<int> t3(1);
	Test<int> t4(1);
	Test<int> t5(1);

	Test<char> t6('a');
	Test<char> t7('a');
	Test<char> t8('a');

	cout << Test<int>::count << endl;
	cout << Test<char>::count << endl;
	return 0;
}
二 成员模板

2.1 为什么要使用成员模板

#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
	T m_a;
public:
	void print(const Test<T> &a)
	{
		cout << a.m_a << endl;
	}

};
int main(int argc,char *argv[])
{
	Test<int> a;
	a.m_a = 1;
	Test<double> b;
	b.m_a = 1.123;
	a.print(a);
	a.print(b);
	return 0;
}

2.2 成员模板的实现

#include <iostream>
using namespace std;
//template <typename T,typename U>
template <typename T>
class Test
{
public:
	T m_a;
public:
	template <typename X>
	void print(const Test<X> &a)
	{
		cout << a.m_a << endl;
	}

};
int main(int argc,char *argv[])
{
	Test<int> a;
	a.m_a = 1;
	Test<double> b;
	b.m_a = 1.123;
	a.print(a);
	a.print(b);
	return 0;
}
三 关键词-typename

3.1 内嵌依赖类型名

内嵌:是指在类的内部定义的
依赖:依赖于摸一个模板参数
类型名:最终要指出的这个类型名

3.2 实例

#include <iostream>
using namespace std;
//template <typename T,typename U>
template <typename T>
class Test
{
public:
	T m_a;
	typedef T* PT;   //实际上就是一个内嵌依赖类型名
	//static int PT;
public:
	template <typename X>
	void print(const Test<X> &a)
	{
		cout << a.m_a << endl;
	}
};
int main(int argc,char *argv[])
{
	int num = 100;
	//typename Test<int>::PT p = &num;
	Test<int>::PT p = &num;
	return 0;
}
四 using 给模板起别名
#include <iostream>
using namespace std;
//template <typename T,typename U>
template <typename T,typename X>
class Test
{
public:
	T m_a;
	typedef T* PT;   //实际上就是一个内嵌依赖类型名
	//static int PT;
public:
};
template <typename S>
using MyA = Test<string, S>;  //将两个参数降为一个参数
using s32 = int;
using P_func = void(*)(int, int);
int main(int argc,char *argv[])
{

	Test<string, int> a;
	MyA<int> a2;
	return 0;
}
五 实例化

5.1 #pragma once

1、ifndef方法是c/c++的标准支持,比较常用的方式
#pragma once一般由编译器提供保证,功能是保证同一个文件不会被包含多次
visual studio 2017之后的版本新建头文件都会自带#pragma once
    
2、兼容性:#pragma once产生于ifndef之后,ifndef不受任何编译器的限制,而#pragma once方法有些编译器不支持(较老的编译器(gcc3.4之前的不支持)),兼容性不够好,
3、ifndef可以针对文件中的一部分代码,而#pragma once只针对整个文件
4、ifndef 更加灵活,兼容性好,#pragma once操作简单,效率更高。

5.2 隐式实例化

隐式实例化是指在函数调用的时候,如果没有发现与之相匹配的函数存在,编译器会寻找同名的函数模板,如果可以成功进行参数的推演,就对函数模板实例化。
类模板同上。

5.3 显示实例化

显示实例化:外部实例化,在不发生函数调用的时候将函数模板实例化
类模板同上。

5.3.1 函数模板的显示实例化

template [返回值类型] [函数名]<实际类型列表>(函数参数列表)
template void func<int>(const int&);

5.3.2 类模板的显示实例化

template class Test<int>;
作用:减少隐式实例化实例多个函数和类的开销,只实例化一次(不同编译器处理机制可能不同)
#pragma once
#include <iostream>
#include <string>
#include <vector>
using namespace std;
template <typename T>
void print(const T &t)
{
	cout << t << endl;
}
template <typename T,typename CONT = vector<T>>
class Stack
{
public:
	Stack()
	{
		top = -1;
	}
	void push(T num)
	{
		data.push_back(num);
	}
	void pop()
	{
		//return data.pop_back();
	}
	int top;
	CONT data;    //vector<T> data;
};

#include "template.h"
template void print<int>(const int &t); //显示实例化,只实例化一次
template class Stack<int, vector<int>>;
void Func()
{
	print(5);
	print("hello");
	print(10);
	Stack<int> s;
	Stack<string> s1;
}
#include "template.h"
extern void Func();
template void print<int>(const int &t); //显示实例化,只实例化一次
int main(void)
{
	print(5);
	print("hello");
	print(10);
	Stack<int> s;
	Stack<string> s1;
	Func();

}

5.4 模板全特化和偏特化

5.4.1 为什么要模板特化?

因为编译器认为,对于特定的类型,如果你能对某一个功能更好的实现,那么就该听你的
模板实例化时会优先匹配“模板参数”最符合那个特化的版本
模板特化的前提已要求模板的泛化。

(1) 类模板的全特化

#include <iostream>
#include <string>
using namespace std;
class A
{
public:
	A()
	{

	}
	int m_a;
};

template <typename T,typename U>
class Test
{
public:
	T m_a;
	U m_b;
public:
	void print()
	{
		cout << m_a << endl;
		cout << m_b << endl;
	}
};
//模板特化的作用:处理一些模板处理不了或者处理不好的数据和方法,需要将这些类型特例化,按照特例化的方法处理
//类的全特化:全部类型全部指定
template<>
class Test<A, string>
{
public:
	A  m_a;
	string m_b;
public:
	void print()
	{
		cout << "class Test<A, string>" << endl;
		cout << m_a.m_a << endl;
		cout << m_b << endl;
	}
};
int main(void)
{
	Test<int, string> a;
	a.m_a = 1;
	a.m_b = "helloworld";
	a.print();
	A tb;
	Test<A, string> s1;
	s1.m_a = tb;
	s1.m_b = "world";
	s1.print();
	return 0;
}

(2) 类模板的偏特化

模板参数数量偏特化

#include <iostream>
#include <string>
using namespace std;
class A
{
public:
	A()
	{

	}
	int m_a;
};

template <typename T,typename U>
class Test
{
public:
	T m_a;
	U m_b;
public:
	void print()
	{
		cout << m_a << endl;
		cout << m_b << endl;
	}
};
//通过减少类模板参数,实现类模板的偏特化
template<typename T>
class Test<T, string>
{
public:
	T  m_a;
	string m_b;
public:
	void print()
	{
		cout << "class Test<T, string>" << endl;
		cout << m_a << endl;
		cout <<"name:"<<m_b << endl;
	}
};
int main(void)
{
	Test<int, string> a;
	a.m_a = 1;
	a.m_b = "helloworld";
	a.print();
	/*a tb;
	test<a, string> s1;
	s1.m_a = tb;
	s1.m_b = "world";
	s1.print();*/
	return 0;
}

(3)类模板参数范围偏特化

int --->const int   T -->T*   T--->T&
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
	A()
	{

	}
	int m_a;
};

template <typename T,typename U>  //可通过将其改为const,指针,引用等降低其范围
class Test
{
public:
	T m_a;
	U m_b;
public:
	void print()
	{
		cout << m_a << endl;
		cout << m_b << endl;
	}
};
//通过减少类模板参数,实现类模板的偏特化
template<typename T,typename U>
class Test<const T, const U>
{
public:
	T m_a;
	U m_b;
public:
	void print()
	{
		cout << "class Test<const T, const U>" << endl;
	}
};
int main(void)
{
	Test<const int, const int> a1;
	a1.print();
	return 0;
}
六 内存管理

6.1 c++内存管理

大多数问题都是由于内存泄漏导致的。

6.2 内存分析诊断工具 valgrind

sudo apt-get update
sudo apt-get install -f
sudo apt-get intall valgrind

使用 valgrind:

嵌入式C++(六)_c++

6.3 如何避免内存泄漏

new/delete的成对使用
new[]/delete[]成对出现
人为的控制new/delete的问题,无法杜绝内存泄漏

6.4 c++为什么没有提供GC机制(垃圾回收机制)

没有共同基类:c++是从c语言发展而来,允许直接操作指针,允许将一个类型转换位另一个类型
对于一个指针无法知道它真正指向的类型,而java和c#都有一个共同的基类。
系统开销:GC机制所带来的系统开销,违反了c++的设计哲学--》不为不必要的功能支持代价,不符合c++高效的特性,不符合底层工作的要求。
消耗内存:c++产生的年代内存比较小
替代方法:c++有析构函数,智能指针,引用计数器等去管理资源的释放。

6.5 智能指针

6.5.1 作用

帮助开发者动态的开辟和释放申请的空间,能有效的防止内存泄漏。

6.5.2 分类

分类:
	auto_ptr(c++98): 被c++11智能指针替代,不再使用
    unique_ptr(c++) 独占指针
    share_ptr(c++):共享指针
    weak_ptr(c++):弱指针 
    int *p:裸指针

6.5.3 share_ptr

共享式指针:

多个指针可以同时指向一个对象(共享所有权,协同工作)
当最后一个指针被销毁或者指向其它对象时,这个对象会被释放

工作原理:引用计数增加/减少(原子操作)

引用计数增加:
    用一个智能指针初始化另一个智能指针
    函数传参:传递一个智能指针
    函数返回值:返回一个智能指针
引用计数减少:
    给智能指针赋予新值,指向一个新对象
    局部的智能指针离开其作用域。

定义:

share_ptr<指向的类型> 智能指针变量名
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class A
{
public:
	A()
	{
		cout << "A的无参构造" << endl;
	}
	A(int n)
	{
		m_a = n;
		cout << "A的有参构造" << endl;
	}
	int m_a;
	~A()
	{
		cout << "A的析构函数" << endl;
	}
};
shared_ptr<A> test()
{
	shared_ptr<A> temp(new A);
	return temp;
}
void Func(shared_ptr<A> temp)
{
	cout << temp->m_a << endl;
}
int main(void)
{
	//int *p = new int(5);  //p是裸指针
	//shared_ptr<int> p = new int(5); //初始化:调用类型转换构造函数 explicit
	shared_ptr<int> p(new int(5));
	cout << *p << endl;

	shared_ptr<string> s(new string("helloworld"));
	cout << *s << endl;
	int num = 100;
	//shared_ptr<int> p2(&num);   //智能指针常用于堆空间,指向栈空间的时候,会导致内存释放两次

	 //A *pa = new A(100);
	//delete pa;
	shared_ptr<A> pa(new A);
	shared_ptr<A> res = test();
	Func(pa);
	shared_ptr<A> pa2(pa);
	return 0;
}