当前位置: 首页 > ds >正文

九、内联函数(Inline Functions)

九、内联函数(Inline Functions)

引言

  • 如何提高效率
  • 在C语言中,为了保持效率,常用的方法之一使用预处理宏(preprocessor macros) 。预处理器会将所有的宏调用直接替换成对应的宏代码
  • 在C++中,预处理宏存在两个问题:
    • 宏可能会隐藏难以发现的bug
    • 预处理宏不能作为类的成员函数使用
  • 为了既保持效率 ,又能拥有真是函数的安全性类作用域 ,C++引入了内敛函数(Inline function)。

9.1 预处理器陷阱(Preprocessor pifalls)

  • 宏只是简单文本替换,可能导致不可预料的错误。

  • 下面是一个简单例子,思考一下

    #define F(x) (x + 1)
    
    • 它应该像F(1) 这样被调用

    • 预处理器会将它展开为:

      (x)(x+1)(1)
      

9.2 内联函数(Inline Functions)

概述

  • 当一个函数代码只有几行,但会被频繁调用时,我们可以使用inline 来节省时间并提高效率。

  • 内联函数是一个真正的函数,它在调用处被直接展开 ,就像预处理宏一样,因此可以消除函数调用的开销。

    内联函数 = 真正的函数 + 具有宏替换的效率。

  • 因此,我们在写代码是,应该永远不要使用宏,而是使用内联函数。

示例

inline int add(int x,int y,int z){return x+y+z;
}
void main(){int a(1),b(2),c(3),sum(0);sum = add(a,b,c);
}

注意事项

  • 内联函数必须定义在调用之前。
  • 函数体中不能包含循环(for/while)、switch语句、异常处理。
  • 不能递归 调用自己。

类的内联函数

  • 在类定义内部写出的函数,默认是inline 的,即使不写inline 关键字。

示例:类内的内联函数(默认)

//C09:Inline.cpp
#include <iostream>
#include <string>
using namespace std;class Point
{int i, j, k;
public:Point() :i(0), j(0), k(0) {}Point(int ii, int jj, int kk) :i(ii), j(jj), k(kk) {}void print(const string& msg = "") const {if (msg.size() != 0) cout << msg;cout << i << "," << j << "," << k << endl;}
};void main() {Point p, q(1, 2, 3);p.print("value of p: ");q.print("value of q: ");
}

输出

value of p: 0,0,0
value of q: 1,2,3

示例:类外的内联函数

#include <iostream>
#include <string>
using namespace std;
class Point{int i,j,k;
public:Point();Point(int ii,int jj,int kk);void print(const string& msg = "")const;
}
inline Point::Point():i(0),j(0),k(0){}
inline Point::Point(int ii,int jj,int kk):i(ii),j(jj),k(kk){}
inline void Point::print(const string& msg) const{if (msg.size() != 0) cout << msg;cout << i << "," << j << "," << k << endl;
}

如果内联函数定义写在类外,需要加上inline 关键字

inline#define 对比

  • define 可能导致运算歧义。
  • inline 保证正确的表达式求值。
#define示例:
#include <iostream>
using namespace std;
#define f(x) x*xvoid main(){int x(2);cout << f(2) << endl;cout << f(x+1) << endl;
}

输出:

4
5

这里我们想要的不是5而是9

inline示例
#include <iostream>
using namespace std;
inline int f(int x)
{return x*x;
}
void main(){int x(2);cout << f(x) << endl;cout << f(x+1) << endl;
}

输出:

4
9

9.3 Stash和Stack中的内联应用

  • 小函数适合内联,大型复杂函数不适合内联。
  • 一个内联函数应该定义在与其类声明相同的文件中

Stash

//C09:Stasha4.h
…………
class Stash{…………void inflate(int increase);
public:Stash(int sz):size(sz),quantity(0),next(0),storage(0){}Stash(int sz,int initQuantity):size(sz),quantity(0),next(0),storage(0){inflate(initQuantity);}~Stash{if(storage != 0) delete []storage;}int add(void* element);void* fetch(int index) const{…………}int count const{return next;}
};

Stack

//C09:Stack4.h
…………
class Stack{struct Link{…………Link(void* dat,Link* nxt):data(dat),next(nxt){}}* head;
public:Stack():head(0){}~Stack(){assert(head == 0 && "Stack not empty");}void push(void* dat){head = new Link(dat,head);}void* peek() const{return (head!=0)?head->data : 0;}void* pop(){…………}
};

9.4 内联函数与编译器(Inlines & the compiler)

  • 和任何函数一样,编译器会将函数类型 (即函数原型,包括函数名和参数类型 ,以及返回类型的组合)记录在它的符号表(symbol table)中。
  • 对于一个内联函数 ,当编译器看到这个函数的类型并且函数体语法正确时,函数体的代码 也会被一并带入符号表中。

限制

  • 内联函数应该是小型的或简单的。
  • 如果函数太复杂(例如有循环等等),编译器就无法进行内联展开。
  • 如果函数的地址被获取(无论显示还是隐式地),编译器也无法进行内联展开。也就是说,不能有内联函数的函数指针。

向前引用(Forward refrences)

//C09:EvaluationOrder.cpp
class Forward
{int i;
public:Forward():i(){}int f() const{return g() + 1;}int g() const{return i;}
};
int main(){Foraward frwd ;frwd.f();return 0;
}

构遭函数与析构函数中的隐藏操作

  • 成员对象的构造、析构也是自动进行的。

  • 示例:

    // C09:Hidden.cpp
    #include <iostream>
    using namespace std;class Member {int i, j, k;
    public:Member(int x = 0) : i(x), j(x), k(x) {cout << "Member(" << x << ")" << endl;}~Member() {cout << "~Member" << endl;}
    };class WithMembers {Member q, r, s;  // 成员对象,定义顺序决定构造顺序!int i;
    public:WithMembers(int ii) : i(ii) {cout << "WithMembers(" << ii << ")" << endl;}~WithMembers() {cout << "~WithMembers" << endl;}
    };int main() {WithMembers wm(1);return 0;
    }

    输出:

    Member(0)
    Member(0)
    Member(0)
    WithMembers(1)
    ~WithMembers
    ~Member
    ~Member
    ~Member
    

    构造顺序:

    1. Member q 调用构造函数,输出 Member(0)
    2. Member r 调用构造函数,输出 Member(0)
    3. Member s 调用构造函数,输出 Member(0)
    4. 然后WithMembers 自己构造,输出 WithMembers(1)

    这说明类中包含其他类对象(即成员对象)时,它们的构造和析构顺序是由定义顺序决定的,而不是在构造函数初始化列表中的顺序。 同时先构造成员对象,最后构造主类体。

9.5 减少接口杂乱(Reducing clutter)

  • 在实际项目中,如果在类内部定义内联函数,可能会使类的接口变得杂乱,从而增加类的使用难度。
  • 为了保持接口简洁,应当将函数的定义放在类外部。
//C09:Noinsitu.cpp
class Rectangle{
public:Rectangle(int w = 0,int h = 0);int getWidth() const;void setWidth(int w);int getHeight() const;void setHeight(int h);
private:int witdth,height;
};inline Rectangle::Rectangle(int w,int h):width(w),height()h {}
inline int Rectangle::getWidth() const (return width;)
inline void Rectangle::setWidth(int w) {width = w;}
inline int Rectangle::getHeight() const {return height;}
inline void Rectangle::setHeight(int h) {height = h;}

9.6 总结

  • 内联函数 = 结合了宏的效率函数的安全性
  • 类内定义的函数默认使inline
  • 大型工程中,最好将inline 函数定义写在类外,保持接口整洁。
http://www.xdnf.cn/news/2422.html

相关文章:

  • 《Keras 3部署全攻略:从新手到实战高手》
  • 网络流之最大流(Dinic)
  • Springboot基础篇
  • 牛客:链表内指定区间反转
  • 在 Ubuntu24.04 LTS 上 Docker 部署英文版 n8n 和 部署中文版 n8n-i18n-chinese
  • 前端自定义文本生成文件下载
  • 第八章 IO流
  • 树状数组详解
  • ZBrush2025建模软件下载 ZBrush中文版免费下载 ZBrush版本大全
  • 数据预处理中比较重要的知识点
  • 【白雪讲堂】
  • CPU与GPU的功能与区别解析
  • 【LCMM】纵向轨迹模型,组轨迹模型
  • c++学习小结
  • AUTOSAR图解==>AUTOSAR_SWS_StandardTypes
  • PotPlayer,强大的高清视频播放器
  • 使用 Spring Boot 进行开发
  • TypeScript基础数据类型详解
  • 多数元素(简单)
  • VSCode远程登录云服务器并设置免密登录全攻略
  • java每日精进 4.26【多租户之过滤器及请求处理流程】
  • llama factory怎么命令行推理图片
  • java—基础
  • A. Everybody Likes Good Arrays!
  • Java 程序运行和类路径处理
  • map和set的应用总结
  • MySQL 常用语句教程
  • Python数值类型修炼手册:从青铜到王者的进阶之路
  • Buffer Pool是什么,有什么作用
  • 【MATLAB第118期】基于MATLAB的双通道CNN多输入单输出分类预测方法