七、C++语言初阶:模板

2023-10-27

7:模板

7.1 基本概念

  • 什么是模板?
    模板(Template)是允许函数或者类通过泛型(generic types)的形式表现或运行的特性。
  • 模板有什么用?
    模板可以使函数或者类只写一份代码而对应不同的类型。
  • 模板编程/泛型编程
    一种独立于特定类型的编码方式
  • 模板分类
    模板分为函数模板类模板两类。
    1、函数模板(Function template):使用泛型参数的函数(function with generic parameters)
    2、类模板(Class template):使用泛型参数的类(class with generic parameters)

7.2 函数模板

  • 模板声明
template <模板形参表> 函数返回类型 函数(形参表);
  • 模板定义
template <模板形参表>
函数返回类型 函数(形参表){
      函数体;
};

例如:

template <typename T> T Max(T a,T b){
        return a>b?a:b;
}
  • 模板实例化
函数(实参表)

产生模板特定类型的函数或者类的过程称为实例化
调用函数模板与调用函数完全一致

  • 实例
    最值函数Max()
    字符串转数值StringToNumber()
#include <iostream>
#include <sstream>
using namespace std;
/*
int Max(int a,int b){
    return a>b?a:b;
}
double Max(double a,double b){
    return a>b?a:b;
}
char Max(char a,char b){
    return a>b?a:b;
}
*/
//使用模板,避免代码大量重复
template <typename T>
T Max(T a,T b){  //根据a和b的输入,自动识别类型
   return a>b?a:b;
}
/*
void Swap(int& a,int& b){
    int c = a;
    a = b;
    b = c;
}
void Swap(char& a,char& b){
    char c = a;
    a = b;
    b = c;
}
*/
template<typename T>
void Swap(T& a,T& b){
    T c = a;
    a = b;
    b = c;
}
template <typename T>
string NumToString(T num){
    ostringstream oss;
    oss << num;
    return oss.str();
}
template <typename T>
T StringToNumber(const string& s){
    T res;
    istringstream iss(s);
    iss >> res;
    return res; 
}
int main(){
    cout << Max(12,10) << endl;
    //cout << Max(1,3.4) << endl; //报错
    //解决办法
    cout << Max((double)1,2.2) << endl;  //强转  double Max(double a,double b)
    cout << Max<double>(10,2.2) << endl; //模板  double Max(double a,double b)
    cout << Max('a','b') << endl;

    int a = 1,b = 2;
    cout << a << "," << b << endl;
    Swap(a,b);
    cout << a << "," << b << endl;
    char c1 = 'c',c2 = 'd';
    cout << c1 << "," << c2 << endl;
    Swap(c1,c2);
    cout << c1 << "," << c2 << endl;

    cout << NumToString(1234) << endl;
    cout << NumToString(1.234) << endl;

    cout << StringToNumber<int>("1234") << endl; //告诉模板传入参数类型
    cout << StringToNumber<float>("1.234") << endl; //告诉模板传入参数类型
}
12
2.2
10
b
1,2
2,1
c,d
d,c
1234
1.234
1234
1.234

7.3 类模板

  • 模板声明
template <模板形参表> class 类名;
  • 模板定义
template <模板形参表>
class 类名 {
}
  • 模板实例化
类名<模板实参表> 对象;
  • 模板参数表
    多个模板参数之间,分割。模板参数,模板参数,…
  • 模板参数
  • 类型形参
    class 类型形参或者typename 类型形参

注:类模板的声明与实现通常都写在头文件中,是不能够分开的。

  • 实例1:类模板 求圆的周长和面积
#include <iostream>
#include <cmath>
using namespace std;
template <typename T>
class Circle{
T r;
public:
    Circle(T r):r(r){}
    float GetLength()const{
        return 2*M_PI*r;
    }
    float GetArea()const{
        return M_PI*r*r;
    }
};
int main(){
	//模板实例化
    Circle<int> c(3);
    cout << c.GetLength() << "," << c.GetArea() << endl;
    Circle<float> c2(3.14);
    cout << c2.GetLength() << "," << c2.GetArea() << endl;
}
18.8496,28.2743
19.7292,30.9748
  • 实例2:类模板 实现数字list和字符list
#include <iostream>
using namespace std;
template <typename T>
class SeqList{
T* list;
size_t size;
public:
    SeqList():list(NULL),size(0){}
    void Append(T num){
    	T* temp = new T[size+1];
	for(int i = 0;i < size;++i){
	    temp[i] = list[i];
	}
	temp[size] = num;
	++size;
        delete [] list;
	list = temp;
    }
    size_t GetSize(){
  	return size;		    
    }
    T& operator[](T i){
    	return list[i];
    }
    void ShowList(){
	for(int i = 0;i < size;++i){
	    cout << list[i] << " ";
	}
	cout << endl;
    }
};

int main(){
    SeqList<int> l;
    l.Append(1);
    l.Append(2);
    l.Append(3);
    l.Append(4);
    l.ShowList();

    SeqList<char> c;
    c.Append('H');
    c.Append('E');
    c.Append('L');
    c.Append('L');
    c.Append('O');
    c.ShowList();
}
1 2 3 4 
H E L L O

7.4 模板参数推导/推演(deduction)

  • 定义
    模板参数推导/推演(deduction):由模板实参类型确定模板形参的过程。

实例化有两类:
显示实例化:代码中明确指定类型的实例化
隐式初始化:根据参数类型自动匹配的实例化

注:
1、类模板参数允许自动类型转换(隐式转换);
2、函数模板参数不允许自动类型转换(隐式转换)
3、在模板参数列表中,class和typename完全一样。但是在语义上,class表示类,typename代表所有类型(类以及基本类型)。
4、请尽量使用typename

函数模板实参类型不一致问题

template <typename T> 
inline const T& Max(const T& a, const T& b){
        return a>b?a:b;
}

模板实例化时,如果输入数据为:

Max(2,2.4)

参数推导会出现模板实参类型intdouble不一致的错误。
解决方法:
1、每个模板参数独立类型

template <typename T , typename U> inline const T& Max(const T& a, const U& b){
        return a>b?a:b;
}

注意:这种解决方法还有一个问题,就是返回值只能强制设置为T或者U,不能自动推导。C++11的后置推导解决这个问题。

template <typename T, typename U> 
inline auto Max(const T& a, const U& b)->decltype(a>b?a:b)
{
        return a>b?a:b;
}

2、显示指定模板实参类型

Max<int>(2,2.4)

或者

Max<double>(2,2.4)

3、实参强制类型转换

Max(2,static_cast<int>(2.4))

或者

Max(static_cast<double>(2),2.4)

注:模板参数推导不允许类型自动转换,模板参数必须严格匹配。

函数模板实例显示指定模板实参可以显示指定模板实参,也可以不指定(类型自动推导),类模板实例化必须

7.5 特化

  • 模板特化(specialization):
    模板参数在某种特定类型下的具体实现称为模板的特化。模板特化有时也称之为模板的具体化。
  • 特化作用
    1、对于某种特殊类型,可以做特殊处理或者优化。
    2、避免实例化类的时候产生诡异行为。
  • 模板特化分类
    1、函数模板特化(Function specializations):对函数模板的全部模板类型指定具体类型。
    2、类模板特化(Class specializations):对类模板的全部或者部分模板类型指定具体类型。

7.5.1 函数模板特化

  • 特点
    函数模板,却只有全特化,不能偏特化。
  • 步骤
    与类的全特化相同
  • 语法:
template<typename T>
void Func(const T& n){}

// 特化
template<>
void Func(const int& n){}

实例:

#include <iostream>
#include <sstream>
#include <cstring>
using namespace std;
/*
int Max(int a,int b){
    return a>b?a:b;
}
double Max(double a,double b){
    return a>b?a:b;
}
char Max(char a,char b){
    return a>b?a:b;
}
*/
//命名空间
namespace My{
template <typename T>
T max(T a,T b){  //根据a和b的输入,自动识别类型
   cout << "Max(" << a << "," << b << ")=";
   return a>b?a:b;
}
//模板特化,特化的原因是有些输入参量,模板处理不了
template<>
const char* max(const char* a,const char* b){
    return strcmp(a,b) > 0? a:b;
}
};
//使用模板,避免代码大量重复
template <typename T>
T Max(T a,T b){  //根据a和b的输入,自动识别类型
   return a>b?a:b;
}
/*
void Swap(int& a,int& b){
    int c = a;
    a = b;
    b = c;
}
void Swap(char& a,char& b){
    char c = a;
    a = b;
    b = c;
}
*/
template<typename T>
void Swap(T& a,T& b){
    T c = a;
    a = b;
    b = c;
}
template <typename T>
string NumToString(T num){
    ostringstream oss;
    oss << num;
    return oss.str();
}
template <typename T>
T StringToNumber(const string& s){
    T res;
    istringstream iss(s);
    iss >> res;
    return res; 
}
int main(){
    cout << My::max(1,2) << endl;
    cout << Max(12,10) << endl;
    //cout << Max(1,3.4) << endl; //报错
    //解决办法
    cout << Max((double)1,2.2) << endl;  //强转  double Max(double a,double b)
    cout << Max<double>(10,2.2) << endl; //模板  double Max(double a,double b)
    cout << Max('a','b') << endl;
    cout << max(string("abcd"),string("efg")) << endl;
    //cout << Max<string>("ancd","efg") << endl;  // 这样也可以???
    cout << Max("abcd","efg") << endl; //输出结果错误,因为字符串不能用>或<进行比较,所以不能调用模板
    //解决办法:模板特化
    cout << My::max("abcd","efg") << endl;

    int a = 1,b = 2;
    cout << a << "," << b << endl;
    Swap(a,b);
    cout << a << "," << b << endl;
    char c1 = 'c',c2 = 'd';
    cout << c1 << "," << c2 << endl;
    Swap(c1,c2);
    cout << c1 << "," << c2 << endl;

    cout << NumToString(1234) << endl;
    cout << NumToString(1.234) << endl;

    cout << StringToNumber<int>("1234") << endl; //告诉模板传入参数类型
    cout << StringToNumber<float>("1.234") << endl; //告诉模板传入参数类型
}
Max(1,2)=2
12
2.2
10
b
efg
efg
abcd
efg
1,2
2,1
c,d
d,c
1234
1.234
1234
1.234

7.5.2 类模板特化

  • 特点
    类模板特化,每个成员函数必须重新定义。
  • 类模板特化分为两种:
    全特化(Full specializations):具体指定模板的全部模板参数的类型。
    局部特化(Partial specializations):具体指定模板的部分模板参数的类型。

7.5.2.1 全特化

  • 步骤:
    声明一个模板空参数列表template<>
    在类名称后面的<>中显示指定类型。
  • 语法:
// 模板  
template<class T>
class Test{};

// 全特化
template<>
class Test<int*>{};

实例:

#include <iostream>
#include <cmath>
using namespace std;
template <typename T>
class Circle{
T r;
public:
    Circle(T r):r(r){}
    float GetLength()const{
        return 2*M_PI*r;
    }
    float GetArea()const{
    	return M_PI*r*r;
    }
};
//类特化
template<>
class Circle<int>{
int r;
public:
    Circle(int r):r(r){}
    float GetLength()const{
	cout << "Circle<int>:";
        return 2*M_PI*r;
    }
    float GetArea()const{
	cout << "Circle<int>:";
    	return M_PI*r*r;
    }
};
int main(){
    Circle<int> c(3);
    cout << c.GetLength() << "," << c.GetArea() << endl; //调用特化模板
    Circle<float> c2(3.14);
    cout << c2.GetLength() << "," << c2.GetArea() << endl;
}
Circle<int>18.8496,28.2743
19.7292,30.9748

7.5.2.2 偏特化

  • 偏特化就是部分特化,分为两种情况
    个数特化:只为部分模板参数指定具体类型(模板参数个数变少)
    范围特化:模板参数不变,限制模板参数的匹配类型(指针、引用、const)
  • 步骤:
    在一个模板类参数列表不指定或者指定部分具体类型。
    在类名称后面的对应类型中显示指定该类型。
  • 实例:
template<typename T1,typename T2>
class Test{};

将模板参数偏特化为相同类型

template<typename T>
class Test<T,T>{};

将一个模板参数特化成具体类型

template<typename T>
class Test<T,int>{};

把两个类型偏特化成指针类型

template<typename T1,typename T2>
class Test<T1*,T2*>{};

注:类模板特化,相当于函数模板的重载
全特化和偏特化的编码区别:
全特化的模板参数列表为空template<>,偏特化的模板参数列表不为空。
实例:

#include <iostream>
#include <cstring>
using namespace std;

// 引用类型模板
template <typename T>
bool Equal(const T& a,const T& b){
    return a == b;
}

// 特化成浮点型
template<>
bool Equal(const double& a,const double& b){
    return abs(a-b) < 1e-6;
}

// -------------------------------------------------
// 指针类型模板(函数模板重载)
template<typename T>
bool Equal(const T* a,const T* b){
    return *a==*b;
}

// 特化成char*
template<>
bool Equal(const char* a,const char* b){
    return strcmp(a,b)==0;
}

int main(){
    cout << Equal(1,1) << endl;
    cout << Equal(1.2,1.2) << endl;
    cout << Equal(string("abc"),string("abc")) << endl;


    cout << Equal(1,2) << endl;
    cout << Equal(1.2,1.21) << endl;
    cout << Equal(string("abcd"),string("abc")) << endl;
    
    cout << Equal(1.2,(10.2-9)) << endl;


    int arr[] = {1,2,3,1};
    cout << Equal(arr,arr+3) << endl; // bool Equal(int*,int*)

    cout << Equal("abc","abcd") << endl;
}

综合应用:空间坐标中的点、线、三角形和直角三角形

#include <iostream>
#include <cmath>
#include <sstream>
using namespace std;
class Point {
protected:
    int x,y;
public:
    Point(int x,int y):x(x),y(y) {}
    int GetX()const {
        return x;
    }
    int GetY()const {
        return y;
    }
    friend ostream& operator<<(ostream& os,const Point& p) { //流对象不能拷贝构造,所以要加引用
        return os << "(" << p.x << "," << p.y << ")";
    }
};
// 继承的本质是A is a B
// Circle is a Point ???
// 但是圆本身不是点
// 说圆包含一个点更合适
// A has a B
// Circle has a Pointer
class Circle { /*:public Point*/
    int r;
    Point center;
public:
    Circle(int x,int y,int r):center(x,y),r(r) {}
    int GetR()const {
        return r;
    }
    Point GetCenter()const {
        return center;
    }
    friend ostream& operator<<(ostream& os,const Circle& c) {
        return os << "(" << c.center.GetX() << "," << c.center.GetY() << "," << c.r << ")";
    }
};
class Line {
    Point a;
    Point b;
public:
    Line(const Point& a,const Point& b):a(a),b(b) {}
    float GetLength()const {
        int w = a.GetX() - b.GetX();
        int h = a.GetY() - b.GetY();
        return sqrt(w*w+h*h);
    }
    bool IsParallel(const Line& line)const {
        int x1 = a.GetX() - b.GetX();
        int y1 = a.GetY() - b.GetY();
        int x2 = line.a.GetX() - line.b.GetX();
        int y2 = line.a.GetY() - line.b.GetY();
        return x1*y2 == x2*y1;
    }
    bool IsVertical(const Line& line)const {
        int x1 = a.GetX() - b.GetX();
        int y1 = a.GetY() - b.GetY();
        int x2 = line.a.GetX() - line.b.GetX();
        int y2 = line.a.GetY() - line.b.GetY();
        return x1*x2 + y2*y1 == 0;
    }
    friend ostream& operator<<(ostream& os,const Line& l) {
        return os << l.a << "~" << l.b;
    }
};
class Triangle {
protected:
    Point a;
    Point b;
    Point c;
public:
    Triangle(Point a,Point b,Point c):a(a),b(b),c(c) {
        ostringstream oss;
        oss << a << b << c;
        if(Line(a,b).IsParallel(Line(a,c))) throw invalid_argument(oss.str()+"三点不能共线");
    }
    float GetLength()const {
        return Line(a,b).GetLength() + Line(a,c).GetLength() + Line(b,c).GetLength();
    }
    virtual float GetArea()const {
        float p = GetLength()/2.0;
        return sqrt(p*Line(a,b).GetLength()+p*Line(a,c).GetLength()+p*Line(b,c).GetLength());
    }
    friend ostream& operator<<(ostream& os,const Triangle& t) {
        return os << "[" << t.a << "," << t.b << "," << t.c << "]";
    }
};
class RightAngleTriangle:public Triangle {
public:
    RightAngleTriangle(const Point& a,const Point& b,const Point& c):Triangle(a,b,c) {
        bool vertical = Line(a,b).IsVertical(Line(a,c)) || Line(a,c).IsVertical(Line(b,c)) || Line(a,b).IsVertical(Line(b,c));
        if(!vertical) throw invalid_argument("不能构成直角三角形");
    }
    float GetArea()const override {
        cout << "直角三角形面积:" << endl;
        Line l1(a,b);
        Line l2(a,c);
        Line l3(b,c);
        if(l1.IsVertical(l2)) {
            return l1.GetLength()*l2.GetLength()/2.0;
        } else if(l1.IsVertical(l3)) {
            return l1.GetLength()*l3.GetLength()/2.0;
        } else if(l2.IsVertical(l3)) {
            return l2.GetLength()*l3.GetLength()/2.0;
        } else {
            return 0;
        }
    }
};
void PrintTriangle(const Triangle& t) {
    cout << t.GetLength() << " " << t.GetArea() << endl;
}
int main() {
    Point p(3,4);
    cout << p << endl;
    Circle c(1,2,3);
    cout << c << endl;

    Point o(0,0);
    Line l(o,p);
    cout << l << ":" << l.GetLength() << endl;

    Triangle t(p,o,Point(3,0));
    cout << t << ": Length" << t.GetLength() << " Area:"  << t.GetArea() << endl;

    Line l1(o,Point(0,1));
    Line l2(o,Point(0,2));
    cout << l1.IsParallel(l2) << " " << l1.IsVertical(l2) << endl;

    //Triangle t2(o,Point(0,1),Point(0,2));

    RightAngleTriangle t3(o,p,Point(3,0));
    cout << t3.GetArea() << endl;

    //多态
    /*  //不常用写法
    Triangle& t4 = t3;
    cout << t4.GetArea() << endl;
    */
    //常用写法
    PrintTriangle(t3);
}
(3,4)
(1,2,3)
(0,0)~(3,4):5
[(3,4),(0,0),(3,0)]: Length12 Area:8.48528
1 0
直角三角形面积:
6
12 直角三角形面积:
6

改进:加入抽象类Shape统一所有类

#include <iostream>
#include <cmath>
#include <sstream>
using namespace std;
class Point; //前置声明,因为抽象类中用到Point
//包含纯虚函数的类成为抽象类,抽象类只定义不实现
class Shape {
public: 
    //纯虚函数
    virtual bool Contained(const Point& p) const = 0;
};
//定义抽象类的目的是把下面所有类进行统一
class Point:public Shape {
protected:
    int x,y;
public:
    Point(int x,int y):x(x),y(y) {}
    int GetX()const {
        return x;
    }
    int GetY()const {
        return y;
    }
    friend ostream& operator<<(ostream& os,const Point& p) { //流对象不能拷贝构造,所以要加引用
        return os << "(" << p.x << "," << p.y << ")";
    }
    bool Contained(const Point& p)const override {
        return p.x == x && p.y == y;
    }
};
// 继承的本质是A is a B
// Circle is a Point ???
// 但是圆本身不是点
// 说圆包含一个点更合适
// A has a B
// Circle has a Point
// 可以说Circle is a Shape
class Line:public Shape {
    Point a;
    Point b;
public:
    Line(const Point& a,const Point& b):a(a),b(b) {}
    float GetLength()const {
        int w = a.GetX() - b.GetX();
        int h = a.GetY() - b.GetY();
        return sqrt(w*w+h*h);
    }
    bool IsParallel(const Line& line)const {
        int x1 = a.GetX() - b.GetX();
        int y1 = a.GetY() - b.GetY();
        int x2 = line.a.GetX() - line.b.GetX();
        int y2 = line.a.GetY() - line.b.GetY();
        return x1*y2 == x2*y1;
    }
    bool IsVertical(const Line& line)const {
        int x1 = a.GetX() - b.GetX();
        int y1 = a.GetY() - b.GetY();
        int x2 = line.a.GetX() - line.b.GetX();
        int y2 = line.a.GetY() - line.b.GetY();
        return x1*x2 + y2*y1 == 0;
    }
    friend ostream& operator<<(ostream& os,const Line& l) {
        return os << l.a << "~" << l.b;
    }
    bool Contained(const Point& p) const override {
        bool parallel = Line(a,p).IsParallel(Line(b,p));
        return parallel && (min(a.GetX(),b.GetX()) <= p.GetX() && p.GetX() <= max(a.GetX(),b.GetX()))
               && (min(a.GetY(),b.GetY()) <= p.GetY() && p.GetY() <= max(a.GetY(),b.GetY()));

    }
};
class Circle:public Shape {
    int r;
    Point center;
public:
    Circle(int x,int y,int r):center(x,y),r(r) {}
    int GetR()const {
        return r;
    }
    Point GetCenter()const {
        return center;
    }
    friend ostream& operator<<(ostream& os,const Circle& c) {
        return os << "(" << c.center.GetX() << "," << c.center.GetY() << "," << c.r << ")";
    }
    bool Contained(const Point& p)const override {
        return Line(p,center).GetLength() <= r;
    }
};
class Triangle :public Shape {
protected:
    Point a;
    Point b;
    Point c;
public:
    Triangle(Point a,Point b,Point c):a(a),b(b),c(c) {
        ostringstream oss;
        oss << a << b << c;
        if(Line(a,b).IsParallel(Line(a,c))) throw invalid_argument(oss.str()+"三点不能共线");
    }
    float GetLength()const {
        return Line(a,b).GetLength() + Line(a,c).GetLength() + Line(b,c).GetLength();
    }
    virtual float GetArea()const {
        float p = GetLength()/2.0;
        return sqrt(p*Line(a,b).GetLength()+p*Line(a,c).GetLength()+p*Line(b,c).GetLength());
    }
    friend ostream& operator<<(ostream& os,const Triangle& t) {
        return os << "[" << t.a << "," << t.b << "," << t.c << "]";
    }
    bool Contained(const Point& p) const override {
        // TODO: 
        return false;
    }
};
class RightAngleTriangle:public Triangle {
public:
    RightAngleTriangle(const Point& a,const Point& b,const Point& c):Triangle(a,b,c) {
        bool vertical = Line(a,b).IsVertical(Line(a,c)) || Line(a,c).IsVertical(Line(b,c)) || Line(a,b).IsVertical(Line(b,c));
        if(!vertical) throw invalid_argument("不能构成直角三角形");
    }
    float GetArea()const override {
        cout << "直角三角形面积:" << endl;
        Line l1(a,b);
        Line l2(a,c);
        Line l3(b,c);
        if(l1.IsVertical(l2)) {
            return l1.GetLength()*l2.GetLength()/2.0;
        } else if(l1.IsVertical(l3)) {
            return l1.GetLength()*l3.GetLength()/2.0;
        } else if(l2.IsVertical(l3)) {
            return l2.GetLength()*l3.GetLength()/2.0;
        } else {
            return 0;
        }
    }
};
void PrintTriangle(const Triangle& t) {
    cout << t.GetLength() << " " << t.GetArea() << endl;
}
int main() {
    Point p(3,4);
    cout << p << endl;
    Circle c(1,2,3);
    cout << c << endl;

    Point o(0,0);
    Line l(o,p);
    cout << l << ":" << l.GetLength() << endl;

    Triangle t(p,o,Point(3,0));
    cout << t << ": Length" << t.GetLength() << " Area:"  << t.GetArea() << endl;

    Line l1(o,Point(0,1));
    Line l2(o,Point(0,2));
    cout << l1.IsParallel(l2) << " " << l1.IsVertical(l2) << endl;

    //Triangle t2(o,Point(0,1),Point(0,2));

    RightAngleTriangle t3(o,p,Point(3,0));
    cout << t3.GetArea() << endl;

    //多态
    /*  //不常用写法
    Triangle& t4 = t3;
    cout << t4.GetArea() << endl;
    */
    //常用写法
    PrintTriangle(t3);

    Shape* arr[] = {&p,&c,&o,&l,&l1,&l2,&t,&t3};
    Point point(0,0);
    cout << "相交图形";
    for(int i = 0;i<8;++i){
         if(arr[i]->Contained(point)){
            cout << i << endl;
         }
    }	
}
(3,4)
(1,2,3)
(0,0)~(3,4):5
[(3,4),(0,0),(3,0)]: Length12 Area:8.48528
1 0
直角三角形面积:
6
12 直角三角形面积:
6
相交图形1
2
3
4
5
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

七、C++语言初阶:模板 的相关文章

随机推荐

  • Spring中AOP

    1 概述 AOP 面向切面编程 将程序中的非业务代码抽取 在不修改业务代码的前提下 为其添加功能 功能增强 面向切面的编程思想底层是为目标创建一个代理对象 让代理对象调用目标类中方法 在代理对象调用时 可以额外的调用其他的方法 增强的方法
  • hduoj 2011

    多项式求和 Problem Description 多项式的描述如下 1 1 2 1 3 1 4 1 5 1 6 现在请你求出该多项式的前n项的和 Input 输入数据由2行组成 首先是一个正整数m m lt 100 表示测试实例的个数 第
  • Linq to Sql : 并发冲突及处理策略

    0 并发冲突的示例 单用户的系统现在应该比较罕见了 一般系统都会有很多用户在同时进行操作 在多用户系统中 涉及到的一个普遍问题 当多个用户 同时 更新 修改或者删除 同一条记录时 该如何更新呢 下图展示了开放式并发冲突的一个示例 假设数据库
  • OpenGL片段列表渲染:实现流畅的大规模场景渲染

    OpenGL片段列表渲染 实现流畅的大规模场景渲染 在实时渲染领域 处理大规模场景是一项重要的任务 然而 传统的渲染方式存在着效率低下 内存消耗大等问题 为了解决这些问题 最近的研究中提出了使用片段列表进行场景渲染的方法 本文将介绍如何使用
  • python批量写入数据

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 python批量写入文件内容 前言 一 使用步骤 1 引入库 前言 提示 这里可以添加本文要记录的大概内容 python批量写入文件内容 提示 以下是本篇文章正文内容 下面案
  • 数字后端——信号完整性分析

    随着光刻和集成电路制造工艺的不断进步 以及芯片的特征尺寸从深亚微米到纳米的迅速采用 人们一方面因为芯片的功能极大提高而受益 另一方面 当逻辑门的沟道长度减小时 门的开关时间会减小 这意味着输出驱动器上升时间变短 或者说时钟频率可以更高 同时
  • Web和Servlet

    Web web开发概述 学习web开发 需要先安装一台web服务器 将开发好的web项目部署在web服务器中供外界访问 web开发环境搭建 Web服务器是指驻留于英特网上某种类型计算机的程序 可以向浏览器等Web客户端提供文档 也可以放置网
  • 万亿级KV存储架构与实践

    一 KV 存储发展历程 我们第一代的分布式 KV 存储如下图左侧的架构所示 相信很多公司都经历过这个阶段 在客户端内做一致性哈希 在后端部署很多的 Memcached 实例 这样就实现了最基本的 KV 存储分布式设计 但这样的设计存在很明显
  • JavaScript 实现 -- 希尔排序

    文章目录 希尔排序 代码实现 时间复杂度和稳定性 希尔排序 希尔排序是插入排序的一种 又称 缩小增量排序 Diminishing Increment Sort 是插入排序的一种更高效的改进版本 希尔排序实际上就是分组的插入排序 希尔排序以步
  • Java图片Base64格式压缩大小至40k之内

    Maven中用到的依赖
  • Docker安装Portainer

    前言 Portainer是一个可视化的容器镜像的图形管理工具 利用Portainer可以轻松构建 管理和维护Docker环境 而且完全免费 基于容器化的安装方式 方便高效部署 Docker安装 拉取镜像 docker pull portai
  • Vue2+Vue3

    文章目录 Vue快速上手 Vue是什么 第一个Vue程序 插值表达式 Vue核心特性 响应式 Vue指令 v html v show 与 v if v else 与 v else if v on v bind v for v model 指
  • ElasticSearch一:简介、概念、安装、Kibana客户端安装使用、IK分词器、数据管理

    目录 一 ElasticSearch简介 ElasticSearch 简称ES 应用场景 ElasticSearch与Lucene的关系 哪些公司在使用Elasticsearch ES vs Solr比较 总结 ES vs 关系型数据库 L
  • VS2015 设置了包含路径(包含目录),还是提示说打不开 .h 头文件

    项目 属性 VC 目录 注意上方的平台是否选错 设置后再设置包含路径 另一个原因 有时代码从别的地方复制过来的时候 因为字符编码的问题 一样会造成很多未定义的问题 本人亲测 第一个方法很有用
  • jmeter 接口快速创建

    通过cURL命令创建测试计划 从浏览器获取接口 从postman获取接口
  • postgresql 创建自增索引(序列)、以及索引(序列)相关操作

    PostgreSQL 没有自增索引的概念 需要通过序列来完成相关操作 PostgreSQL 使用序列来标识字段的自增长 数据类型有 smallserial serial 和 bigserial 这些属性类似于 MySQL 数据库支持的 AU
  • java 的循环输入

    书本7 3 计算数字出现的次数
  • 在MFC中怎样添加一个非模态的对话框

    添加一个非模态对话框1 点击菜单 插入 资源 选择Dialog 点击新建 2 编辑对话框 添加一些控件什么的 3 双击对话框或者打开ClassWizard 为对话框建立一个类 如CMyDialog 4 在 view cpp的开头添加新建立的
  • SSM之mybatis:修改了Mybatis的xml文件不生效

    这两天写一个项目是 修改了下mybatis的xml文件中的一个sql语句 结果发现修改后的xml文件始终不生效 情况是这样的 一开始我的语句
  • 七、C++语言初阶:模板

    7 模板 7 1 基本概念 什么是模板 模板 Template 是允许函数或者类通过泛型 generic types 的形式表现或运行的特性 模板有什么用 模板可以使函数或者类只写一份代码而对应不同的类型 模板编程 泛型编程 一种独立于特定