C++函数探幽

1、内联函数

        常规与内联函数的区别:不在于编写的方式,而是C++如何将他们组合到程序中,编译生成可执行程序(机器指令),储存在内存(堆栈)中。当执行函数时,会保存当前指令的地址,执行完函数后,返回当地址。而内联函数与其他程序代码“内联”起来,也就是用相应的函数定义去替换函数调用,减少了跳转到函数调用的时间,以及函数调用完毕后返回的时间。速度会快,但是将会占用更多的内存空间。

        使用的情况:代码量很少的时候。

                            (1)函数声明前需要加上inline

                             (2)函数定义前加上inline

一般直接将定义放在主函数前面(注:当程序过大或者函数中有递归时,编译器可能不会将它编译成内联函数)。

#include <iostream>
using namespace std;

inline double square(double x){return x * x;}

int main()
{
    double a, b;
    double c = 13.0;
    a = square(5.0);
    b = square(4.5 + 7.5);
    cout <<"a = " << a << ", b = " << b << endl;
    cout << "c = " << c << endl;
    cout << "Now c = " << square(c++) << endl;
    cout << "c = " << c << endl;
    return 0;
}

2、引用变量

引用:定义变量的别名,又作函数参数(形参),将可操作原始数据,处理大型结构比较方便。

创建引用变量:

int rats;
int & rodents = rats;

引用更接近const指针,必须在创建时进行初始化,一旦关联某个变量,将一直效忠与它。

int * const pr = & rats   == int & rodents = rats //他们作用相同
//在关联变量后,若在对rodents 进行下列语句
rodents = A;  //这里不再是将将变量A与rodents绑定,而是将A的值赋值给rodents。

3、引用用作函数的参数

应用和指针一样,都是对原数据进行操作,在一些不希望改变原数据的情况,最好使用按值传递或则const修饰引用。

double cube(double a);
double recube(double &ra);
int main()
{
    double x = 3.0;
    cout << cube(x) << "cube of " << x << endl;
    cout << recube(x) << "cube of " << x << endl;//不适合使用引用
                                                 //可将函数声明为
                                                 //double recube(const double &ra)

    return 0;
}

double cube(double a)
{
    return a * a * a;
}
double recube(double &ra)
{
    ra *= ra * ra;
    return ra;
}

注意:

        (1)对于常引用,当时参与形参不匹配时,将生成临时变量与引用相关联(其行为类似于按值传递)。

          (2)对于非常引用,不能将常量与引用关联,而常引用可以。

使用const的好处: 可以避免无意的编程错误。

                                可以接受const 和 非const。

                                使用const引用使函数能够正确生成临时变量。

4、将引用用于结构体

引用非常适合结构体和类

对于一个函数,若返回值为结构体,则返回值为结构体的拷贝,此时,将花费时间去拷贝复制,造成效率降低以及浪费内存空间。若返回值为结构体的引用或指针,则返回值为结构体本身。

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

struct  free_throws
{
    string name;
    int made;               // 成功的次数
    int attempts;           //尝试的次数
    float percent;
};
void display(const free_throws &ft);
void set_pc(free_throws &ft);

free_throws & accumulate(free_throws &target, const free_throws &source);
int main()
{
    free_throws one = {"Rick", 13, 14};
    free_throws two = {"Jack", 10, 16};
    free_throws three = {"Jerry", 7, 9};
    free_throws four = {"Jason", 5, 9};
    free_throws five = {"Micheal", 6, 14};
    free_throws team = {"Class 1", 0, 0};


    set_pc(one);
    display(one);
    
  
    display(accumulate(team, one));
    display(accumulate(accumulate(accumulate(team,two),four),five));  


    return 0;
}

void set_pc(free_throws &ft)
{
    if(ft.attempts != 0)
    {
        ft.percent = 100* float(ft.made) / float(ft.attempts);
    }
    else 
        ft.percent = 0;
}

void display(const free_throws &ft)
{
    cout << "Name: " << ft.name << endl;
    cout << "Made: " << ft.made << '\t';
    cout << "Attempts: " << ft.attempts << '\t';
    cout << "percent: " << ft.percent << endl;
}
free_throws & accumulate(free_throws &target, const free_throws &source)
{
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);
    return target;
}

注意:避免返回引用临时变量的引用(指针),例如下列程序,返回的是临时结构体temp,这是错误的,当程序执行完毕后,临时变量将会被销毁,无法在进行引用。

//例如下列程序
free_trows & example(const free_trows & source)
{
    free_trows temp;
    temp.attempts += source.attempts;
    temp.made += source.attempts;
    return temp;
}

5、引用和类

//可以返回临时变量
string version(const string &s1, const string &s2);
//由于返回的是string类的引用,故不能返回临时变量
const string & version2(string &s1, const string &s2);

6、对象,继承和引用

基类引用可以指向派生类(即可指向基类本身也可指向派生类)。

派生类继承基类的方法,可以使用基类的特性。

7、何时使用引用参数

引用的原因:能够修改调用函数中的对象

                       提高运行的速度

指导原则:数据对象很小时,按值传递

                  若对象是数组,只能使用指针

                  数据对象较大,使用指针或引用

8、默认参数

        对于某些函数,通常使用的是默认的参数(默认值),但偶尔也会传递其他值,这时即可使用默认值,对于带参数列表的函数,必须从右到左添加默认值。

        实参从左到右添加参数,不能跳过任何参数。默认值在声明处给出,不是在函数定义处。

#include <iostream>
using namespace std;

const int ArSize = 80;

char * left(const char * str, int n = 1);

int main()
{

    char sample[ArSize];
    cout << "Enter a string: " << endl;
    cin.get(sample, ArSize);
    char *ps = left(sample, 4);
    cout << ps << endl;
    delete []ps;

    ps = left(sample);
    cout << ps << endl;
    delete []ps;

    return 0;
}

char * left(const char * str, int n)
{
    if(n < 0)
        n = 0;
    char * p = new char[n + 1];
    int i;
    for(i = 0; i < n && str[i]; i++)
    {
        p[i] = str[i];
    }
    while (i <= n)
    {
        p[i++] = '\0';
    }
    return p;
}

9、函数重载(函数多态)

主要体现在函数的形式相同,函数列表不同。

C++ 的左值与右值 左值与右值引用。

        左值与右值相对于 =  += 运算符。左值:能取地址,右值:不能取地址。

const int &d = 10; //将创建临时变量值为10,并与d绑定

int && x = 10;     //右值引用

重载的关键:参数的个数、参数的类型、参数的排列顺序(形参)。

何时使用:执行的任务相同,但使用不同的数据类型。

//函数名相同,但参数不同,类型、排列顺序。返回值不影响函数的重载。
char * left(const char * str, int n = 1);
unsigned long left(unsigned long num, unsigned int ct);

注意:有多个函数重载时,若在调用的时候没有与其中任何一个匹配,可能会终止运行,且不会进行强制转换。

下面情况将产生二义性:编译器将无法判断调用哪一个函数。

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

long gronk(int m, double n);
double gronk(int m, double n);

10、函数模板

基本语法:

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

 在调用时,编译器将根据调用生成不同的函数,一般将函数模板放在头文件里面。

模板的重载:同函数重载一样,函数名相同, 形参的个数,类型,顺序不同即可构成重载。

template <typename T>
void Swap(T ar1[], T ar2[], int n);

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

函数模板很强大,但是同样并不适用所有的数据类型,因为传入的类型不一定是基本的数据类型,传入的形参不变,函数重载也没有办法解决,于是出现了函数模板的显示具体化。

template < > void Swap<job>(job &j1, job &j2);//job为结构体类型

模板的实例化与具体化

        编译器使用函数模板为特定类型生成函数定义时,得到函数模板实例,这个过程是在编译时完成的。

template <typename T>
T max(T a, T b) {
    return a > b ? a : b;
}

int main() {
    int a = 3, b = 5;
    int c = max(a, b);  // 实例化生成一个 int 类型的 max 函数
    return 0;
}

        具体化,指的是显示实例化模板,即在程序中为模板提供实参,强制编译器生成特定类型的函数或类。

template <typename T>
T max(T a, T b) {
    return a > b ? a : b;
}

template int max<int>(int a, int b);  // 显式实例化生成一个 int 类型的 max 函数

int main() {
    int a = 3, b = 5;
    int c = max(a, b);
    return 0;
}

11、重载解析(编译器该选择哪一个函数版本)

原则:谁最批配,最省事,最佳。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值