C++函数模板与重载解析超详细讲解

目录
  • 1.快速上手
  • 2.重载的模板
  • 3.模板的局限性
  • 4.显式具体化函数
  • 5.实例化和具体化
  • 6.重载解析
    • 6.1 概览
    • 6.2 完全匹配中的三六九等
    • 6.3 总结
  • 7.模板的发展

1.快速上手

函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数。

#include<iostream>
using namespace std;
template <typename T>
void Swap(T &a,T &b);//模板原型
struct apple{
    string name;
    double weight;
    int group;
};
void show(apple x);
int main(){
    int a,b;
    a=1;
    b=2;
    Swap(a,b);
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;
    apple c={"Alice",200,1};
    apple d={"Bob",250,2};
    Swap(c,d);
    cout<<"c:"<<endl;
    show(c);
    cout<<"d:"<<endl;
    show(d);
}
template <typename T>
void Swap(T &a,T &b){
    T temp;
    temp=a;
    a=b;
    b=temp;
}
void show(apple x){
    cout<<"name:"<<x.name<<endl;
    cout<<"weight:"<<x.weight<<endl;
    cout<<"group:"<<x.group<<endl;
}

a:2
b:1
c:
name:Bob
weight:250
group:2
d:
name:Alice
weight:200
group:1

模板函数也可以有原型:

template <typename T>

void Swap(T &a,T &b);

这里的typename也可以换成class

不过模板原型实际上不常见。

模板函数定义:

template <typename T>
void Swap(T &a,T &b){
    T temp;
    temp=a;
    a=b;
    b=temp;
}

模板函数隐式实例化:

Swap(a,b); 模板函数会根据实参的类型,给出函数定义。 还有显式实例化: Swap<int>(a,b); 显式的定义typename。 对于这两种实例化,我推荐使用显式实例化,因为隐式实例化容易出错。对于这块知识的详细解读,需要有对编译器有充分的理解,在文章后面会给出。

一般我们不会用到模板函数的原型,因为我们一般把模板函数的定义放在头文件里面,再需要使用的时候,包含头文件就行了。

不推荐的做法:模板原型放在头文件,模板定义放在cpp文件里。

2.重载的模板

如果对函数的重载不了解,可以翻看我之前的文章:

内联函数、引用变量、函数重载

模板函数也可以重载,语法和常规函数的重载差不多;被重载的模板函数必须要特征标不同。

#include<iostream>
using namespace std;
template <typename T>
void Swap(T &a,T &b);//模板原型
template <typename T>
void Swap(T *a,T *b,int n);//模板原型
struct apple{
    string name;
    double weight;
    int group;
};
void show(apple x);
int main(){
    int a,b;
    a=1;
    b=2;
    Swap(a,b);
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;
    apple c={"Alice",200,1};
    apple d={"Bob",250,2};
    Swap(c,d);
    cout<<"c:"<<endl;
    show(c);
    cout<<"d:"<<endl;
    show(d);
    char e[10]="hello";
    char f[10]="bye!!";
    Swap(e,f,10);
    cout<<"e:"<<e<<endl;
    cout<<"f:"<<f<<endl;
}
template <typename T>
void Swap(T &a,T &b){
    T temp;
    temp=a;
    a=b;
    b=temp;
}
template <typename T>
void Swap(T *a,T *b,int n){
    T temp;
    for(int i=0;i<n;i++){
        temp=a[i];
        a[i]=b[i];
        b[i]=temp;
    }
}
void show(apple x){
    cout<<"name:"<<x.name<<endl;
    cout<<"weight:"<<x.weight<<endl;
    cout<<"group:"<<x.group<<endl;
}

a:2
b:1
c:
name:Bob
weight:250
group:2
d:
name:Alice
weight:200
group:1
e:bye!!
f:hello

3.模板的局限性

#include<iostream>
using namespace std;
template<class T>
const T& foo(const T &a,const T &b){
    if(a>b)return a;
    else return b;
}
struct apple{
    string name;
    double weight;
    int group;
};
void show(apple x);
int main(){
    apple c={"Alice",200,1};
    apple d={"Bob",250,2};
    apple max=foo(c,d);
    show(max);
}
void show(apple x){
    cout<<"name:"<<x.name<<endl;
    cout<<"weight:"<<x.weight<<endl;
    cout<<"group:"<<x.group<<endl;
}

上面这段代码是出错的,因为T如果是结构体,我们无法对其做>操作。当然解决这个问题的方法也是有的—显式具体化函数。

4.显式具体化函数

显式具体化函数的诞生是因为模板对于某些类型的数据,定义得的函数,例如上例中得foo(c,d)出错,我们就单独对这个类型,写一个特殊的函数。

所以,就是一句话,原先模板不适用于某种类型的数据,我们就单独给这种类型的数据,单独来一个函数定义。

#include<iostream>
using namespace std;
struct apple{
    string name;
    double weight;
    int group;
};
template <typename T>
void Swap(T &a,T &b);//模板原型
template<>
void Swap<apple>(apple &a,apple &b);//显式具体化函数原型,这里<apple>可以省略
void show(apple x);
int main(){
    int a,b;
    a=1;
    b=2;
    Swap(a,b);
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;
    apple c={"Alice",200,1};
    apple d={"Bob",250,2};
    Swap(c,d);
    cout<<"c:"<<endl;
    show(c);
    cout<<"d:"<<endl;
    show(d);
}
template <typename T>
void Swap(T &a,T &b){
    T temp;
    temp=a;
    a=b;
    b=temp;
}
template<>
void Swap<apple>(apple &a,apple &b){
    cout<<"explicit specialization for apple!"<<endl;
    int temp;
    temp=a.group;
    a.group=b.group;
    b.group=temp;
}
void show(apple x){
    cout<<"name:"<<x.name<<endl;
    cout<<"weight:"<<x.weight<<endl;
    cout<<"group:"<<x.group<<endl;
}

a:2
b:1
explicit specialization for apple!
c:
name:Alice
weight:200
group:2
d:
name:Bob
weight:250
group:1

可以看出来,我们单独为 结构体apple 搞了个显式具体化函数,目的就是只交换group成员变量。

显式具体化函数和常规模板很类似。

显式具体化函数的原型:

template<>

void Swap<apple>(apple &a,apple &b);

这里<apple>可以省略.

显式具体化函数的定义:

template<>
void Swap<apple>(apple &a,apple &b){
    cout<<"explicit specialization for apple!"<<endl;
    int temp;
    temp=a.group;
    a.group=b.group;
    b.group=temp;
}

实际上这段代码也意味着,显式具体化的优先级高于常规模板。

5.实例化和具体化

切记!函数模板本身不会生成函数定义,它只是一个生成函数定义的方案!

编译器使用模板为特定类型生成函数定义时,得到的是模板实例。生成函数定义就是实例化。

实例化有隐式和显式之分。

隐式实例化:

Swap(a,b);或者Swap<int>(a,b);

隐式实例化是指等你调用了这个函数的时候,它才会生成函数定义。

显式实例化:

template void Swap<int>(int,int);

显式实例化是指不需要等你调用这个函数,使用上面那段代码,直接能生成Swap<int>函数的定义。 一般来说,我们会把模板放到一个头文件中,然后很多源文件会include它,然后编译的时候就会在这些源文件中生成具体化的代码。但是如果我们采用显式实例化,在其中一个源文件里面实例化一份代码,然后其他cpp文件用到的时候,通过链接程序找到这个代码并调用它,程序的大小就会少一些。这就是显式实例化的好处。

下面这段代码展示了Add<double>(a,b)相较于Add(a,b)的优越性:

#include<iostream>
using namespace std;
template <typename T>
T Add(const T &a,const T &b){
    return (a+b);
}
int main(){
    int a=5;
    double b=6.1;
    cout<<Add<double>(a,b)<<endl;
}

如果把Add<double>(a,b)换成Add(a,b)会出错,因为a是int类型的,而b是double类型的,这样就无法隐式实例化了。Add<double>(a,b)会实例化一个函数定义,然后int类型的a,传参给double的引用形参的时候,会产生临时变量,从而完成函数调用。总之,最好使用<type>而不是根据参数类型自动生成模板的实例化.

显式隐式实例化和显式具体化统称为具体化或者实例化

上一节中我们提到了显式具体化,我们可以发现实例化和显式具体化的相同之处在于,他们都是使用具体类型的函数定义,而不是通用描述。

显式具体化函数是否是模板? 我的回答是:显式具体化函数是一个特殊的模板,它是专门为一种类型设计的模板。

//函数模板6.cpp
#include<iostream>
using namespace std;
struct apple{
    string name;
    double weight;
    int group;
};
template<class T>
void Swap(T &a,T &b);//模板函数原型
template<>void Swap(apple &a,apple &b);//显式具体化原型
template void Swap<char>(char&,char&);//显式实例化
void show(apple x);
int main(){
    short a=1;
    short b=2;
    Swap(a,b);//隐式实例化
    cout<<"a:"<<a<<endl<<"b:"<<b<<endl;
    apple c={"Alice",200,1};
    apple d={"Bob",250,2};
    Swap(c,d);//显式具体化
    cout<<"c:"<<endl;
    show(c);
    cout<<"d:"<<endl;
    show(d);
    char e='a';
    char f='b';
    Swap<char>(e,f);//调用显式实例化函数
    cout<<"e:"<<e<<endl<<"f:"<<f<<endl;
}
template<>
void Swap(apple &a,apple &b){
    int temp;
    temp=a.group;
    a.group=b.group;
    b.group=temp;
}
void show(apple x){
    cout<<"name:"<<x.name<<endl;
    cout<<"weight:"<<x.weight<<endl;
    cout<<"group:"<<x.group<<endl;
}
template<class T>
void Swap(T &a,T &b){
    T temp;
    temp=a;
    a=b;
    b=temp;
}

a:2       
b:1       
c:        
name:Alice
weight:200
group:2   
d:        
name:Bob  
weight:250
group:1
e:2.01
f:1

这里问个问题,如果把上面代码中的e变成 int类型会出现问题吗?

会报错,因为实参int和函数中引用形参char&的类型不一样,且此时不是const引用形参,也不会有临时变量产生。如果你不清楚,且看引用变量的语法。 内联函数、引用变量、函数重载

6.重载解析

6.1 概览

对于常规函数,函数重载,函数模板,函数模板重载,编译器需要有一个良好的策略,从一大堆同名函数中选择一个最佳函数定义。这一过程是非常复杂的过程–重载解析。这就是我们这一节要阐述的内容。

重载解析过程:

  • step1:创建候选函数列表。其中包含与被调用函数名称相同的函数和模板函数。
  • step2:从候选函数列表中筛选可行函数。其中包括参数正确或者隐式转换后参数正确的函数。
  • step3:确定是否存在最佳的可行函数。如果有则使用他,否则函数调用出错。

其中最复杂的就是step3,这些可行函数也有优先级之分,优先级 从高到低是:

  1. 完全匹配
  2. 提升转化 (如,char short 转化成int,float 转化成 double)
  3. 标准转化 (如,int 转化成 char ,long转化成double)
  4. 用户定义的转化 (如类声明中定义的转换)

而完全匹配中也有细小的优先级之分。

总而言之,在step3中如果优先级最高的可行函数是唯一的那么就调用他,否则会出现诸如ambiguous的错误。

这一节的目的就是完全理解编译器如何让处理如下代码:

#include<iostream>
using namespace std;
void may(int);//#1
float may(float,float=3);//#2存在默认参数
void may(char &);//#3
char* may(const char*);//#4
char may(const char &);//#5
template<class T> void may(const T &);//#6
template<class T> void may(T *);//#7
int main(){
    may('B');
}
void may(int a){
    cout<<1<<endl;
}
float may(float a,float b){
    cout<<2<<endl;
    return a;
}
void may(char &a){
    cout<<3<<endl;
}
char* may(const char* a){
    cout<<4<<endl;
    return NULL;
}
char may(const char &a){
    cout<<5<<endl;
    return a;
}
template<class T>
void may(const T & a){
    cout<<6<<endl;
}
template<class T>
void may(T *){
    cout<<7<<endl;
} 

上述代码没有一点问题,甚至连warning都没有,你可以自己试一下结果是什么。

'B'是const char类型的

#1~#7都是候选函数,因为函数名字相同。

其中#1、#2、#3、#5、#6是可行函数,因为const char 类型无法隐式转换成指针类型,所以#4、#7不行,而其他函数通过隐式转换后参数是正确的。

#1是提升转换,#2是标准转换,#3、#5、#6是完全匹配,完全匹配中非模板函数比模板函数优先级高,所以#3、#5优先级高于#6,而由于const参数优先和const引用参数匹配,所以#5的优先级更高。

则#5>#3>#6>#1>#2,所以调用#5。

6.2 完全匹配中的三六九等

首先什么是完全匹配?

完全匹配函数包括:

  • 不需要进行隐式类型转化的函数(即参数正确的函数)显然是完全匹配函数。
  • 需要进行隐式类型转换,但是这些转换是无关紧要转换。

完全匹配允许的无关紧要转换:

实 参 形 参
Type Type&
Typc& Type
Type[] * Type
Type (argument-list) Type ( * ) (argument-list)
Type const Type
Type volatile Type
Type * const Type
Type* volatile Type *

完全匹配中的优先级法则

  • 常规函数优先级高于模板。
  • 对于形参是指针或引用类型的函数,const修饰的实参优先匹配const修饰的形参,非const修饰的实参优先匹配非const修饰的形参。
  • 较具体的模板优先级高于较简略的模板。(例如,显式具体化函数优先级高于常规模板)
#include<iostream>
using namespace std;
struct apple{
    string name;
    double weight;
    int group;
};
void may(const apple & a){
    cout<<1<<endl;
}
void may(apple &a){
    cout<<2<<endl;
}
int main(){
    apple a={"Alice",250.00,1};
    may(a);
}

结果是2

#include<iostream>
using namespace std;
struct apple{
    string name;
    double weight;
    int group;
};
void may(const apple & a){
    cout<<1<<endl;
}
void may(apple &a){
    cout<<2<<endl;
}
void may(apple a){
    cout<<3<<endl;
}
int main(){
    apple a={"Alice",250.00,1};
    may(a);
}

这个编译器会出错,因为这三个函数都是完全匹配,但是#2 和 #3的优先级无法区别,记得吗,完全匹配中的优先级法则的第2条法则,只适用于形参是引用或者指针。

#include<iostream>
using namespace std;
struct apple{
    string name;
    double weight;
    int group;
};
template<typename T>
void may(T a){
    cout<<1<<endl;
}
template<typename T>
void may(T *a){
    cout<<2<<endl;
}
int main(){
    apple a={"Alice",250.00,1};
    may(&a);
}

终端输出是2,&a的类型是 apple*,而#2明确指出形参是个指针,所以#2更具体。

关于如何找出最具体的模板的规则被称为部分排序规则。

部分排序规则:在实例化过程中,函数优先和转换少的模板匹配。也可以这么说,实参和形参越相似,模板越优先。

举个栗子:

#include<iostream>
using namespace std;
template<typename T>
void may(T a[]){
    cout<<1<<endl;
}
template<typename T>
void may(T *a[]){
    cout<<2<<endl;
}
template<typename T>
void may(const T *a[]){
    cout<<3<<endl;
}
int main(){
    double a[5]={1,2,3,4,5};
    const double* b[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};
    may(a);
    may(b);
}

may(a)会和#1匹配,因为a的类型是double数组,double数组无法转换成指针数组,所以#2,#3不是可行函数。而对于may(b),他会和#3匹配。b的类型是cont指针数组,首先#1和#2和#3都是可行函数,而且都是完全匹配函数,因为#1 会实例化成may<const double*>(b),#2 他实例化成may<const double>(b),#3会实例化为may<double>(b)所以我们看看那个模板更具体?#3模板直接指出了 形参是一个const指针数组,所以他最具体,#3优先级最高;其次是#2因为它的形参指出了是指针数组;#1是最不具体的,#3>#2>#1.

6.3 总结

可行函数中优先级从高到低排列    
完全匹配 常规函数 形参若是指针或引用,注意const和非const
  模板 较具体的模板优先级更高
提升转换    
标准转换    
用户定义转换    

Swap<>(a,b)这种代码,类似于显式实例化,但是<>中没有指出typename,所以这段代码是要求优先选择模板函数。

对于多参数的函数,优先级会非常复杂,就不谈了。

7.模板的发展

关键字decltype 和 auto

#include<iostream>using namespace std;template<typename T1,typename T2>auto Add(T1 a, T2 b){ decltype(a+b) c; c=a+b; return c;}int main(){ int a=2; double b=2.123; cout<<Add(a,b);}#include<iostream>
using namespace std;
template<typename T1,typename T2>
auto Add(T1 a, T2 b){
    decltype(a+b) c;
    c=a+b;
    return c;
}
int main(){
    int a=2;
    double b=2.123;
    cout<<Add(a,b);
}

关键字decltype 和 auto ,在模板中无法确定数据类型时,发挥了巨大的作用。

到此这篇关于C++函数模板与重载解析超详细讲解的文章就介绍到这了,更多相关C++函数模板与重载解析内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++简单又好用的基本运算符重载

    目录 运算符重载概念 加号运算符重载 成员函数实现 全局函数实现 运算符实现函数重载 总结 运算符重载概念 对已有的运算符进行重新定义,赋予其另外一种功能,以适应不同的数据类型 我们知道已有的运算符有'+'.'-'.'*'.'/'等,这些运算符对于内置数据类型可以直接使用,例如int.float.double.char等等.但是如果我们定义一个类,想实现类中对象属性的加减乘除运算,该怎么实现呢?那就用到运算符重载的知识点了. 加号运算符重载 学会一个顶四个,这篇博文只举例加号运算符重载 成员函数

  • 浅析C++函数模板和类模板

    目录 一.函数模板 1.函数模板的定义和使用 2.函数模板的编译原理 3.函数模板的声明 二.类模板 1.类模板的定义和使用 2.类模板的编译原理 3.类模板的继承和派生 C++语言全盘继承了C语言的标准库,其中包换非常丰富的系统函数,例如输入/输出函数.数学函数.字符串处理函数和动态内存分配函数等.C++语言另外又增加了一些新的库,我们把C++语言新增的这部分库称为C++标准库.C++语言的模板技术包括函数模板和类模板.模板技术是一种代码重用技术,函数和类是C++语言中两种主要的重用代码形式.

  • C++函数模板与类模板相同与不同介绍

    目录 1.模板 1.1何为模板 1.2C++的模板的形式有两种 1.3如何定义一个函数模板 1.4语法形式 1.5模板的编译机制 2.函数模板 2.1调用方式 2.2函数模板的特化与调用优先级 3.可变参函数模板 3.1概念 3.2代码实现(实现一个c中的printf的函数) 4.类模板 4.1类模板的定义形式 4.2代码实例 5.类模板中的特殊属性的初始化方式及继承与多态 5.1代码实例 5.2使用类模板去实现一个数据结构 5.3类模板的特化 5.4C++中类模板中的内嵌类 1.模板 1.1何

  • C++函数模板的使用详解

    函数模板可以适用泛型来定义函数,其中泛型可以是(int, double, float)等替换.在函数重载过程中,通过将类型作为参数传递给模板,可使编译器自动产生该类型的函数. 工作原理:比如需要定义一个比大小的max函数,有三种类型的数据(int,double,float),可能就需要编写三个函数,这样既浪费时间,且容易出错.如: #include <iostream> using namespace std; int Max(int a, int b); double Max(double

  • C++ 函数模板和类模板详情

    目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式化 2.3 函数模板原理 2.4 函数模板实例化 2.5 模板参数的匹配原理 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数? 在C++中可以用到函数重载 class A { public: void Swap(int& x1,int& x2) { int temp=x1; x1=x2; x2=temp; } void Swap(double&

  • C++类模板与函数模板基础详细讲解

    目录 函数模板 类模板 总结 函数模板 当我们想要定义一个可以支持泛型的函数时,就要采用函数模板的方式了.所谓泛型就是可以支持多种类型的操作,比如我们定义一个compare操作,他可以根据传递给他的参数类型动态调用对应的函数版本,实现多种类型的比较. template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1;

  • C++深入讲解函数重载

    目录 函数重载 概念 重载依据 值型别 判断函数重载的规则 名字粉碎-名字修饰 函数重载 概念 在C++中可以为两个或者两个以上函数提供相同的函数名称,只要参数类型不同,或者参数数目不同,参数顺序不同,即参数表不同,那么就认为是函数的重载.(函数名+参数表) // my_max + 参数表 int my_max(int a,int b) { return a > b ? a : b; } char my_max(char a,char b) { return a > b ? a : b; }

  • C++浅析函数重载是什么

    目录 前言 函数重载 一些其他问题 2.1 int和char怎么区分 2.2 传值 前言 这是一个非常重要的点 函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数. 这些同名函数的 形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题 或者说,给一个名字赋予第二层意义,一词多义,有点内涵那意思 函数重载 首先C语言不允许定义同名的函数,但是C++可以,原因就涉及到了函数重载 函数重载的要求:函数名相同,参数不同(参数类型不同,

  • C++函数模板与重载解析超详细讲解

    目录 1.快速上手 2.重载的模板 3.模板的局限性 4.显式具体化函数 5.实例化和具体化 6.重载解析 6.1 概览 6.2 完全匹配中的三六九等 6.3 总结 7.模板的发展 1.快速上手 函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数. #include<iostream> using namespace std; template <typename T> void Swap(T &a,T &b);//模板原型 struct apple{ st

  • C++超详细讲解函数重载

    目录 1 函数重载的定义 2 构成函数重载的条件 3 编译器调用重载函数的准则 4 函数重载的注意事项 4.1 避开重载带有指定默认值参数的函数 4.2 注意函数重载遇上函数指针 4.3 C++编译器不能以 C 的方式编译重载函数 1 函数重载的定义 函数重载:使用同一个函数名定义不同的函数.从本质上来看,就是互相独立的不同函数,每一个函数类型不同.因此,函数重载是由函数名和参数列表决定的. 注意:函数返回值不能作为函数重载的重要依据! 2 构成函数重载的条件 当满足以下三个条件之一时,便可以构

  • Go Web 编程中的模板库应用指南(超详细)

    如果你有过Web编程的经验,那么或多或少都听说过或者使用过模板.简而言之,模板是可用于创建动态内容的文本文件.例如,你有一个网站导航栏的模板,其中动态内容的一部分可能是根据当前用户是否登录显示登录还是退出按钮. Go提供了两个模板库 text/template和 html/template.这两个模板库的使用方式是相同的,但是 html/template包在渲染页面模板时会在后台进行一些编码以帮助防止造成代码注入(XSS 攻击). 因为两个模板库都使用相同的接口,因此本文中介绍的所有内容均可用于

  • C++超详细讲解函数对象

    目录 一.客户需求 二.存在的问题 三.解决方案 四.函数对象 五.小结 一.客户需求 编写一个函数 函数可以获得斐波那契数列每项的值 每调用一次返回一个值 函数可根据需要重复使用 下面来看第一个解决方案: #include <iostream> using namespace std; int fib() { static int a0 = 0; static int a1 = 1; int ret = a1; a1 = a0 + a1; a0 = ret; return ret; } in

  • C++超详细讲解操作符的重载

    目录 一.需要解决的问题 二.操作符重载 三.小结 一.需要解决的问题 下面的复数解决方案是否可行? 下面看一下复数的加法操作: #include <stdio.h> class Complex { int a; int b; public: Complex(int a = 0, int b = 0) { this->a = a; this->b = b; } int getA() { return a; } int getB() { return b; } friend Comp

  • C++超详细讲解数组操作符的重载

    目录 一.字符串类的兼容性 二.重载数组访问操作符 三.小结 一.字符串类的兼容性 问题:string 类对象还具备 C 方式字符串的灵活性吗?还能直接访问单个字符吗? string 类最大限度的考虑了 C 字符串的兼容性 可以按照使用 C 字符串的方式使用 string 对象 下面看一个用 C 方式使用 string 类的示例: #include <iostream> #include <string> using namespace std; int main() { stri

  • C++超详细讲解运算符重载

    目录 概念 赋值运算符重载 const成员 取地址及const取地址操作符重载 概念 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似. 函数名字为:关键字operator后面接需要重载的运算符符号. 函数原型:返回值类型 operator操作符(参数列表) 需要注意的几点: 不能通过连接其他符号来创建新的操作符:比如operator@,必须是已有的操作符: 重载操作符必须有一个类类型

  • C++超详细讲解模板的使用

    目录 一.函数模板 1.1函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 二.类模板 2.1 类模板的定义格式 2.2类模板的实例化 总结 一.函数模板 1.1函数模板概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本. 1.2 函数模板格式 template<typename T1, typename T2,…,typename Tn> 返回值类型 函数名(参数列表){} template<

  • SpringBoot超详细讲解Thymeleaf模板引擎

    Jsp是最早的模板技术,用来处理视图层的,用来做数据显示的模板 B S结构: B:浏览器:用来显示数据,发送请求,没有处理能力 发送一个请求,访问a.jsp,a.jsp在服务器端变成Servlet,在将输出的数据返回给浏览器,浏览器就可以看到结果数据,jsp最终翻译过来也是个html页面 模板技术你就可以把它们当成字符串的替换,比如说:这里{data}这里有一个字符串,你把它换成固定值其他值,但是这个替换有一些附加的功能,通过模板技术处理视图层的内容 第一个例子: pom.xml:Thymele

  • C语言函数超详细讲解下篇

    目录 前言 函数的声明和定义 函数声明 函数定义 举例 简单的求和函数 把加法单独改写成函数 添加函数声明 带头文件和函数声明 静态库(.lib)的生成 静态库文件的使用方法 函数递归 什么是递归? 递归的两个必要条件 练习1 一般方法 递归的方法 练习2 一般方法 递归方法 练习3 一般方法 递归方法 练习4 一般方法 递归方法 递归与迭代 递归隐藏的问题 如何改进 选递归还是迭代 总结 前言 紧接上文,继续学习函数相关内容. 函数的声明和定义 函数声明 告诉编译器有一个函数叫什么,参数是什么

随机推荐