C++学习笔记--C++新的函数特性


前言

C++为函数增加了一些新的特性,优化了C语言函数在某些方面设计的不够好的地方
以下是C++函数的新特性:
缺省参数内联函数函数重载运算符重载



一、缺省参数

1. 缺省参数的概念

  缺省参数是声明或定义函数时,给函数参数指定的一个值。在调用该函数时,如果没有传实参,形参则为给定的缺省值

void Func(int a = 10, int b = 20, int c = 30)
{
    cout << "a = " << a << endl;	// a = 10
    cout << "b = " << b << endl;	// b = 20
    cout << "c = " << c << endl;	// c = 30
}

int main()
{
    Func();
    return 0;
}

2. 缺省参数的作用

  某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值。这时就可以给这些形参一个指定的缺省参数,便于以后调用该函数时不需要显式的给该形参传参

以 string 中的 erase 函数为例:
在这里插入图片描述
  该函数的作用为从字符串下标为 pos 的位置开始向后删除 len 个字符。当调用该函数时,如果不传参数,即为清空整个字符串

3. 缺省参数的使用

  • 某个形参被赋予缺省值,它后面的所有形参都必须有缺省值

    void Func(int a = 10, int b, int c = 30)
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "c = " << c << endl;
    }  
    // 报错:形参 b 没有缺省值
    

    原因: 传参时会有歧义,调用 Func(10,20) 时,实参如何分配无法确定

  • 缺省参数不能在函数声明和定义中同时出现

    void Func(int a = 10, int b = 20, int c = 30);
    
    int main()
    {
        Func();
        return 0;
    }
    
    void Func(int a = 10, int b, int c = 30)
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "c = " << c << endl;
    }  
    // 报错:缺省值在声明时已经给出
    

    原因: 声明与定义位置提供的值不同,那编译器就无法确定到底该用哪个缺省值

    补充: 一般一个函数只声明一次,但声明多次也是合法的。但每个形参只能被赋予一次缺省值,函数的后续声明只能为没有缺省值的形参添加缺省值,而且该形参右侧的所有形参必须都有缺省值

    void Func(int a, int b, int c =30);
    void Func(int a, int b, int c =20);	// 报错:重复声明
    void Func(int a = 10, int b = 20, int c =30);	// 正确:添加缺省参数
    


二、内联函数

1. 概念

  以 inline 修饰的函数叫做内联函数。编译阶段 C++ 编译器会在调用内联函数的地方展开,即用函数体替换函数调用,没有建立栈帧的开销

  以取 a 和 b 的较大值为例:

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

std::cout << GetMax(10,20) << std::endl;
// 编译阶段内联展开后相当于如下代码:
// std::cout << (10 > 20 ? 10 : 20) << std::endl;

2. 作用

  内联函数在调用处展开,既是内联函数的优点,也是内联函数的缺点。优点在于没有函数建立栈桢的开销,缺点在于展开函数体会使目标文件变大

  内联函数一般适用于规模较小、流程直接、调用频繁且无递归的函数

  实际上,内联函数的缺点可以忽略,因为 inline 对于编译器只是个请求,编译器可以忽略这个请求,只有当该函数符合内联的适用场景,编译器才会采纳 inline 请求

  然而,可能会有人有这样的疑问,C语言的宏函数貌似也可以做到和内联函数一样的事情啊,C++为什么还要引入内联函数呢?

  在 Effective C++(改善程序与设计的55个具体做法)这本书中有这样一个条款:条款 02:尽量以 const,enum,inline 替换 #define

还是以取出 a 和 b 的较大值为例

  • 宏函数
    #define GETMAX(a, b) ((a) > (b) ? (a) : (b))
    
    要写出一个宏函数可不容易,因为宏的本质是替换,所有你必须记住为宏中所有实参加上小括号,以避免有人调用宏时传递表达式。但纵使如此,还有一些事情无法避免,如下代码为例:
    void func1()
    {
        int a = 5, b = 0;
        cout << GETMAX(++a, b) << endl;	// 7 
        cout << a << endl;		// 7 :a 被累加 2 次
    }
    void func2()
    {
        int a = 5, b = 0;
        cout << GETMAX(++a, b + 10) << endl;	// 10
        cout << a << endl;		// 6 :a 被累加 1 次
    }
    
    调用该宏函数,a 的累加次数竟然取决于“它被拿来和谁比较”

  • 内联函数
    template<typename T>
    inline const T& GetMax(const T& a, const T& b)
    {
    	return a > b ? a : b;
    }
    
    还是用以上两个例子调用内联函数,没有发生像宏函数那样不可思议的情况!

  综上来看,内联函数既继承了宏函数的优点(运行效率高,没有建立栈桢的开销),又避免了宏函数的缺点(代码安全性高)



三、函数重载

1. 概念

  函数重载是函数的一种特殊情况,C++ 允许在同一作用域中声明几个功能类似的同名函数,但这些同名函数的形参列表必须不同(参数个数、参数类型、参数顺序)

  注意,返回值不同不在构成函数重载的要求范围内

  编译器根据传递的实参列表,来决定调用哪一个函数,以加法函数为例:

int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}
double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

int main()
{
	Add(1,2);		// int Add(int left, int right)
	Add(1.0,2.0);	// double Add(double left, double right)
	return 0;
}

2. 作用

  函数重载一般用于实现操作非常相似的函数,以 C++ 输入输出函数为例:
在这里插入图片描述
  C++ 的标准输出之所以能够自动识别类型,就是因为重载了不同类型版本的函数!

  尽管函数重载在一定程度上减轻我们为函数起名字、记名字的负担,但是最好只重载那些非常相似的操作。因为有些情况下,给函数起不同的名字能使代码更易理解!!!

3. 补充

  • 函数名修饰规则
    • C 语言在编译后,函数名没有发生改变
    • 而 C++ 在编译后,函数名被其参数的相关信息修饰,尽管函数名相同,但只要参数列表不同,经过修饰后的函数名也就不同
    • 通过这里也就理解了 C 语言没办法支持重载而 C++ 支持重载的原因:C 语言无法区分同名函数,而 C++ 可以通过函数名修饰规则来区分同名函数
  • 函数匹配原则
    • 编译器会找一个与实参最佳匹配的函数,并调用该函数
    • 如果找不到任何一个函数与实参匹配,编译器会发出无匹配的报错
    • 没有最佳匹配,但仍有多个函数与实参匹配,编译器不知道到底该调用哪一个函数,会发出调用二义性的报错
  • 重载与作用域的关系
    • 只有在同一作用域的同名函数才构成重载,如果不同作用域有同名函数(例如在局部域中声明函数),内部作用域就会隐藏外部作用域的同名实体,以下代码为例:
    string read();
    void print(const string &);
    void print(double);
    
    int main()
    {
        bool read = false;
        read();	// 报错,read 为 bool 类型,无法调用
        void print(int);
        print(5);		// 报错,无法找到函数的实现
        return 0;
    }
    
    string read()
    {
        cout << "string read()" << endl;
        return "";
    }
    void print(const string &)
    {
        cout << "void print(const string &)" << endl;
    }
    void print(double)
    {
        cout << "void print(double)" << endl;
    }
    
    • 局部域中的 read 隐藏全局域中的 read() 函数,编译器在找到局部域中的 read 后就不会到全局域中找了,故发出 read 为 bool 类型,无法调用的报错
    • print(int) 在局部进行声明隐藏了全局域中同名函数的声明,在调用 print 函数时,编译器在局部域中找到了 print(int) 的声明,就不会去全局域中找了,然而 print(int) 没有被实现,故发出无法找到函数实现的报错


四、运算符重载

1. 概念

  大家都知道,C++ 是一个经典的面向对象的编程语言。为了能让自定义类型也能像内置类型一样进行基本的运算,C++ 引入了运算符重载

  当运算符作用于自定义类型的对象时,可以通过运算符重载来重新定义该运算符的含义,代码如下:

class item
{
public:
    item(const char *name = "", int price = 0)
        : _name(name), _price(price)
    {
    }
    int operator+(const item &it)
    {
        return _price + it._price;
    }

private:
    std::string _name; // 商品名称
    int _price;        // 商品价格
};

int main()
{
    item it1("apple", 3);
    item it2("pine", 4);
    cout << it1 + it2 << endl;
    return 0;
}

该代码重载了 + 运算符,重新定义了 + 运算符作用于 item 对象的含义,为两个商品的价格总和

明智地使用运算符重载能令我们的代码更易于编写和阅读

2. 语法

  • 重载运算符是具有特殊名字的函数,它的名字由关键字 operator 和其后要定义的运算符号共同组成。和其它函数一样,该函数也具有返回类型、参数列表以及函数体
  • 不能对内置类型重载运算符,例如:int operator+(int a, int b);
  • 我们只能重载已有的运算符,不能创造新的运算符
  • 因为运算符重载针对自定义类型,故在类和对象部分再深入讲解


总结

C++ 关于函数引入了许多特性,来弥补 C 语言的不足之处

缺省参数:某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,这时就可以给这些形参一个指定的缺省参数

内联函数: 尽量用 inline 替换 #define 宏函数

函数重载: 对于操作非常相似的函数,重载成不同的版本

运算符重载: 当运算符作用于自定义类型的对象时,可以通过运算符重载来重新定义该运算符的含义

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值