c++拾遗-----函数探幽

1、内联函数

内联函数是c++为提高程序运行速度而做出的改进。常规函数域内联函数的区别在于:常规函数的调用使得程序跳到该函数的地址,并在函数结束时返回。而内联函数被编译器使用相应的代码替换函数的调用,程序无需跳到一个内存地址,在跳回来,速度稍快,但牺牲的是内存空间。同样

要使用这项特性,方法如下:

  • 在函数声明前加上关键字inline;
  • 在函数定义前加上关键字inline;

但实际上内联函数还要看编译器是否开放这种特性,在vs2010上可以将内联函数进行递归调用。而一般来说是不允许的。内联函数实际上是通过宏来实现的。

#include<iostream>
 inline int func(int n)
 {
     if (n == 0)
         return 1;
     else
         return n*func(n-1);
 }

int main()
{
    using namespace std;
    cout<<func(5);

    system("pause");
    return 0;
}

2、引用变量

引用变量是以创建变量的别名。与以创建变量公用同一内存空间。

创建引用变量

int a=5;
int &b=a;

通过指针可以更改指向内存的内容,但指针不必在声明时初始化,而且可以更改指向的地址。但引用变量必须在声明时初始化,且一旦与某个变量关联,就只能一直效忠它。所以引用更像指针常量。

函数参数传递

c++中参数传递方式有三种:

按值传递,这种方式会生成一个参数副本,函数内的操作并不会改变参数本身,当然数组除外,因为它传递的是数组的地址。

地址传递,如数组和指针,可以改变指针指向空间的内容。但其实同样传递后产生了一个指针副本,所以无法改变指针的指向的地址。

引用传递,传递的是变量的别名,与变量公用同一内存空间,所以可以直接改变参数的值。

临时变量,引用参数和const

当参数是const引用时,以下两种情况会生成临时变量:

  • 实参类型正确,但不是左值。
  • 实参类型不正确,但可以转换成正确的类型
#include<iostream>
double refcube(const double &ra)
{
    return ra*ra*ra;
}

int main()
{
    using namespace std;
    cout << refcube(7);
    system("pause");
    return 0;
}

传入值7,回生成临时变量,ra指向它,该变量只存在于函数调用期间。而把参参数列表中的const去除,则报错。

如果不是const引用,也生成临时变量,那么函数最终改变的对象就是临时变量,而不是希望的引用。所以其他情况无法生成临时变量

函数返回局部变量

函数返回一般局部变量是可以的,但如果返回的是局部变量的地址或引用时,调用会出错,因为指针指向的的内存空间在函数运行结束时被释放掉了。当然如果数据存储在静态存储区,那么该空间在程序结束时才会被释放。

#include<iostream>
char * fun();
char *fun1();
int main()
{
    using namespace std;
    char* p = fun();
    //数据当作常量存储在静态存储区,无法修改
    //p[0] = 'c';
    cout << p << endl;
    char *q = fun1();
    cout << q << " ";
    q[0] = 'c';
    cout << q << endl; 
    system("pause");
    return 0;
}

char * fun()
{
    char *p= "abcd";
    return p;
}

char *fun1()
{
    static char p[5] = { "abcd" };
    return p;
}

何时使用引用参数

使用引用参数的原因主要有:

  • 能够修改调用函数中的数据对象
  • 通过传递引用而不是整个对象,可以提高程序的运行速度。

对于使用传递的值而不做修改的函数

  • 如果数据对象很小,则使用按值传递
  • 如果数据对象是数组,使用指针,并声明为常量指针
  • 如果数据对象是较大的结构,则使用const指针或const引用。
  • 如果数据对象是类对象,则使用const引用

对于修改调用函数中数据的函数

  • 如果是内置对象,则使用指针
  • 如果是数组,使用指针
  • 如果是结构,指针或引用
  • 如果是对象,则使用饮用

3、默认参数

默认参数指函数调用省略实参时自动使用默认值。

注意事项:

  • 对于带参数列表的函数,必须从右往左添加,也就是说如果为某个参数设置默认值,则必须为他右边的所有参数设置默认值。
  • 实参按从左到右依次付给相应的形参,不能跳过任何参数。

4、函数重载

函数重载主要针对于完成相同的工作,而使用不同的参数列表。c++允许定义定义名称相同的函数,前提是参数列表不同。返回值可以相同,但参数列表必须不同

double cube(double x);
double cube(double &x);

上面两个函数不能共存,因为调用时无法确定使用哪个原型。

double cube(double x);
double cube(const double x);

同上,这两个函数也无法共存。因为非const变量既可赋值给const变量,也可以赋值给非const变量。所以编译器无法确定使用哪个函数。

但当参数为引用或指针,则可以使用const进行重载,这是因为按值传递时,对用户而言,这是透明的,用户不知道函数对形参做了什么手脚,在这种情况下进行重载是没有意义的,所以规定不能重载!当指针或
引用被引入时,用户就会对函数的操作有了一定的了解,不再是透明的了,这时重载是有意义的,所以规定可以重载。

double cube(double &x);
double cube(const double &x);
double cube(double *x);
double cube(const double *x);

如果重载的函数都是引用或指针,const 变量 只能调用带有const 参数的方法,非const 变量既能调用带const 参数的方法,也能调用不带cosnt 参数的方法,优先调用不带const 参数的方法.

5、函数模板

函数模板使用范型来定义函数,其中范型可以使用具体的类型来替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。

#include<iostream>
template <typename T>
void swap(T &a, T&b);
int main()
{
    int a = 3, b = 5;
    std::cout << "a=" << a << ",b=" << b << std::endl;
    swap(a, b);
    std::cout << "a=" << a << ",b=" << b << std::endl;
    double c = 4.5, d = 5.5;
    std::cout << "c=" << c << ",d=" << d << std::endl;
    swap(c, d);
    std::cout << "c=" << c << ",d=" <<d << std::endl;
    system("pause");
    return 0;
}

template <typename T>
void swap(T &a, T &b)
{
    a = a + b;
    b = a - b;
    a = a - b;
}

要建立一个模板,关键字template和typename是必须的,其中typename可以用class替换。

重载的模板

需要对不同类型使用相同算法时,可以使用模板,但并不是所有类型都这样,所以可以想重载常规函数一样重载函数模板,记住,并非所有模板参数都必须是模板参数类型。

#include<iostream>
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;
    }
}

int main()
{
    int a = 5,b=3;
    double c[5] = {1.2,2.3,3.4,4.5,5.6};
    double d[5] = {6.7,7.8,8.9,9.1,10.2};
    swap(a,b);
    std::cout << a <<" "<< b << std::endl;
    swap(c,d,5);
    for (int i = 0; i < 5; i++)
        std::cout << c[i] << " " << d[i] <<std::endl;
    system("pause");
    return 0;
}

显示具体化

因为对于某些特殊类型,可能不适合模板实现,需要重新定义实现,此时就是使用显示具体化的场景.

template <typename T>
void swap(T &a,T&b);

//template <>void swap(int &a,int &b);
template <> void swap<int>(int &a,int &b);

上面两种写法皆可,显示具体化一定要有一个相同的模板函数,将其换成具体类型,所以需要一开始的模板函数。

显式实例化

代码中包含的模板并不会生成相应的定义,他们只是一个生成定义的方案,而显式实例化意味着无论程序是否是否使用,都会生成相应的定义。

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

隐式实例化

后面有程序用了,编译器才会根据模板生成一个实例函数.

编译器选择使用哪个函数

1、进行完全匹配时,c++允许一些无关紧要的转换

这里写图片描述

2、重载解析过程将寻找最匹配的函数。如果只存在一个这样的函数,则选择该函数;如果存在多个这样的函数,但其中只有一个是非模板函数,则选择该函数;如果存在多个模板函数,但其中一个函数比其他的都具体,则选择该函数。当存在多个匹配的非模板函数或模板函数,或者不存在匹配的函数,都将出现错误。

3、通过代码选择

#include<iostream>
template <typename T>
T lesser(T t1, T t2)
{
    return t1 > t2 ? t2 : t1;
}

int lesser(int a, int b)
{
    a = a < 0 ? -a : a;
    b = b < 0 ? -b : b;
    return a < b ? a : b;
}


int main()
{
    using namespace std;
    int m = 20;
    int n = -30;
    double x = 15.5;
    double y = 19.5;
    cout << lesser(m, n)<<endl;
    cout << lesser(x, y) << endl;
    cout << lesser<>(m, n) << endl;
    cout << lesser<int>(x, y) << endl;
    system("pause");
    return 0;
}

第一个函数调用匹配非模板函数和模板函数,所以调用非模板函数。

第二个与非模板函数匹配

第三个<>指出需要使用模板函数

第四个指出要使用显示实例化函数,所以返回的是15而不是15.5

所以同时匹配模板函数和非模板函数时,默认使用非模板函数,但可以通过<>来强制使用模板函数

4、多个参数的函数

当函数有多个参数时,最匹配的函数一定是所有参数匹配程度都不比其他函数差

c++11新增关键字decltype

当在模板函数中无法知道要声明变量的类型时,可以使用关键字decltype来自动声明(有点像加强版的auto)。decltype确定变量类型的步骤如下:

假设有声明如下

 decltype(expression) var;

1、如果expression是一个没有用括号扩起来的标识符,则var的类型与标识符的一致,包括const等限定符。

 double x=5.5;
 double y=7.9;
 double &rx=x;
 const double *pd;
 decltype (x) w;   //w is type double
 decltype (rx) u=y; //rx is type doubel &
 decltype (pd) v;  //pd is const double *

2、如果expression是函数调用,则var的类型为函数返回类型。

3、如果expression是用括号扩起来的,则var指向其类型为引用

int y=5
decltype((y)) z=y; //z type int &
decltype(y) w=y;  //w type int

4、如果上述条件都不满足,则var类型与expression一致

int j=3
int &k=j;
int &r=j;
decltype (k+r) z;//z type int

decltype后置返回类型

在模板函数内不确定要声明变量类型时可以用decltype,但函数返回值无法直接使用decltype

template <typename T1,typename T2>
auto swap(T1 t1,T2 t2) ->decltype(t1+t2)
{
   return t1+t2;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在C语言中,string函数是一个字符串处理函数库,它包含在<string.h>头文件中。其中常用的函数有strcpy、strlen和strnset。 strcpy函数用于将一个字符串复制到另一个字符串中。它的函数原型是:char *strcpy(char *dest, const char *src)。其中,dest是目标字符串,src是源字符串。这个函数会将src字符串的内容复制到dest字符串中,并返回dest字符串的指针。\[1\] strlen函数用于计算字符串的长度。它的函数原型是:size_t strlen(const char *str)。这个函数接收一个字符串的首地址,然后遍历字符串直到遇到'\0'字符,返回字符串的长度。\[2\] strnset函数用于将指定的字符替换字符串中的一部分字符。它的函数原型是:char *strnset(char *str, int c, size_t n)。其中,str是要操作的字符串,c是要替换的字符,n是要替换的字符个数。这个函数会将字符串中的指定部分字符替换为指定的字符。\[3\] 这些函数都是C语言中常用的字符串处理函数,可以帮助我们进行字符串的复制、长度计算和字符替换等操作。 #### 引用[.reference_title] - *1* *3* [C语言中string函数详解](https://blog.csdn.net/weixin_30902251/article/details/99781150)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c语言String字符串函数探幽](https://blog.csdn.net/Duary/article/details/106163396)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值