c++函数参数和返回值

c++一直以来是一个关注效率的代码,这样关于函数的参数传递和返回值的接收,是重中之重。下文提供了一些个人的见解。

函数存储位置

函数参数在编译期展开,目前各平台的编译期均有不同。

名称 存储位置
函数名称和逻辑 代码段存储
函数参数和返回值 栈中或者寄存器(64位会有6个寄存器使用)
new malloc 的变量

函数参数入栈顺序

微软有几种编译期属性,用来定义函数参数的顺序和堆栈。

关键字 堆栈清理 参数传递
__cdecl 调用方 在堆栈上按相反顺序推送参数(从右到左)
__clrcall 不适用 按顺序将参数加载到 CLR 表达式堆栈上(从左到右)。
__stdcall 被调用方 在堆栈上按相反顺序推送参数(从右到左)
__fastcall 被调用方 存储在寄存器中,然后在堆栈上推送
__thiscall 被调用方 在堆栈上推送;存储在 ECX 中的 this 指针
__vectorcall 被调用方 存储在寄存器中,然后按相反顺序在堆栈上推送(从右到左)

所以直接在函数参数上,调用表达式和函数来回去值的话,非常危险

初始化列表

class Init1
{
public: void Print()
{
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
} int c, a, b;
};

A这个类,可以通过 A a{1,2,3}; 来初始化对象。

看着很美好,但是有几个问题需要注意。

参数是的入栈顺序是跟着类的属性的顺序一致, 当前是 c, a, b;

int i = 0;
Init1 a = {i++, i++, i++};
a.Print();

当我如此调用的时候,得到的返回值是 1 2 0

i++的执行顺序是从左到右,跟函数调用顺序无关。 另外不能有 构造函数

	class Init1
{
public:
Init1(int ia, int ib, int ic)
{
std::cout << "construct" << std::endl;
a = ia;
b = ib;
c = ic;
}
Init1(const Init1& other)
{
std::cout << "copy " << std::endl;
a = other.a;
b = other.b;
c = other.c;
} void Print()
{
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
} int c, a, b;
};

当我添加了构造函数的时候。 用下面代码测试。会得到两种结果

void Test_InitilizeList()
{
int i = 0;
//Init1 a = { i++, i++, i++ }; // 0 1 2
Init1 a(i++, i++, i++); // 2 1 0
a.Print();
}

函数的返回值

函数返回值的声明周期在函数体内。

用参数引用来返回

class Result
{
public:
int result;
};
void GetResult(Result& result) ...

优点:

  • 效率最高,因为返回值的对象在函数体外构造,可以一直套用, 可以一处构造,一直使用。
  • 安全,可以定义对象,并不用new或者malloc, 没有野指针困扰。

    缺点:
  • 代码可读性低,不够优美
  • 无法返回nullptr. 一般在 Result 中定义一个; 用来表示一个空对象。
  • 容易赋值到一个临时对象中,当调用GetResult({1}) 会赋值到一个 临时的 Result 对象中,拿不到返回值。正常来说也不会这样做。

返回一个参数指针

class Result
{
public:
int result;
};
Result* GetResult() ...

优点:

  • 简洁明了
  • 参数传递快速

    缺点:
  • 指针如果在 函数内 static 需要考虑多线程。 如果是 new 出来的,多次调用效率不高
  • 指针无法重复使用,(可以用 std::share_ptr 增加对象池来解决问题。但会引入新的复杂度。)
  • 需要考虑释放的问题

返回一个对象

class Result
{
public:
int result;
};
Result GetResult() ...

优点:

  • 没有内存泄露的风险
  • 简洁明了

    缺点:
  • 但有个别编译期优化选项问题,会导致一次构造两次拷贝, 第一次是函数体内对象向返回值拷贝,第二次是 返回值拷贝给外面接收参数的。
  • 开启编译期优化选项,并且是 在 return Result 的时候构造返回对象,才能优化。

总结

一般如果是 简单结构体,用 返回一个临时对象的方式解决。

如果使用 返回一个参数指针,一般改成返回一个id,用一个manager来管理内存机制。或者 共享内存,内存池来解决内存泄露后续的问题

用 参数引用来返回的话,一般会这么定义 int GetResult(Result& result) 函数返回值,用来返回状态码,真正的数据,放到 result 中。

函数的几种变体

inline 函数

  • inline 函数是内联函数,是编译期优化的一种手段,一般是直接展开到调用者代码里,减少函数堆栈的开销。
  • inline 标识只是建议,并不是一定开启内联。
  • 函数比较复杂或者递归有可能编译期不展开。
  • dll 导出的时候,可以不用加导出标识,会直接导出到目标处。
  • inline 在msvc的平台,只要实现头文件中,加不加内联是一样的. (警告顶级调到最高/Wall, 不加inline标识的函数会提示,未使用的内联函数将被删除。)
  • inline 函数比全局函数更快,但是全局函数无法定义在头文件中(会报多重定义函数。)所以一般用class 包一层 static inline 函数,用来写工具类。

函数对象

class A {
public :
int value;
int operator() (int val) {
return value + val;
}
}

上述代码是一个函数对象,重载operator()得到一个函数对象。

int a = A{10}(1) 会返回11, 显示构造了一个A{value=10}的对象,然后调用重载函数operator(), 返回 10 + 1 = 11

上述代码因为是在头文件实现的,所以编译期会自动把operator()函数当成inline函数,执行效率很高。

lambda 函数

lambda 其实就是一个函数对象,会在编译期展开成一个函数对象体。

c++函数参数和返回值的更多相关文章

  1. python函数进阶(函数参数、返回值、递归函数)

    函数进阶 目标 函数参数和返回值的作用 函数的返回值 进阶 函数的参数 进阶 递归函数 01. 函数参数和返回值的作用 函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形 ...

  2. javascript函数参数、返回值类型检查

    实现带参数.返回值类型声明的js函数: 类型定义:window.Str = Type.Str = Type.define('STRING', Type.isStr);var Per = Type.de ...

  3. Python基础之函数参数与返回值进阶

    参数作用:如果外界希望在函数内部处理数据,就可以将数据作为参数传入函数内部: 返回值作用:如果希望一个函数函数执行完成后,向外界报告函数的执行结果,就可以使用函数的返回值. 函数的返回值 进阶 利用元 ...

  4. 04 python学习笔记-函数、函数参数和返回值(四)

    函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.函数能提高应用的模块性,和代码的重复利用率.Python提供了许多内建函数,比如print(),我们也可以自己创建函数,这叫做用户自定 ...

  5. JS基础语法---函数---介绍、定义、函数参数、返回值

    函数: 把一坨重复的代码封装,在需要的时候直接调用即可 函数的作用: 代码的重用 函数需要先定义,然后才能使用 函数名字:要遵循驼峰命名法 函数一旦重名,后面的会把前面的函数覆盖 Ctrl +鼠标左键 ...

  6. Java中能否利用函数参数来返回值

    转自https://blog.csdn.net/da_da_xiong/article/details/70039532 我们在写代码时通常会遇到一种情况,就是我们可能希望在一个函数操作完成后返回两个 ...

  7. c++函数参数或返回值为函数指针

    C++中函数指针的形式为:返回值类型 + 参数类型,函数没有值类型,但是却可以声明函数的指针,因为函数是可寻址的,存放在内存中的代码段,可以从指针访问. 函数指针可以声明为: void (*pF)(v ...

  8. ajax中error函数参数与返回值详解 200 300 400 500

    201-206:都表示服务器成功处理了请求的状态代码,说明网页可以正常访问. 200:(成功) 服务器已成功处理了请求.通常,这表示服务器提供了请求的网页. 201:(已创建) 请求成功且服务器已创建 ...

  9. python中函数的参数和返回值

    目录 函数 目标 01. 函数参数和返回值的作用 1.1 无参数,无返回值 1.2 无参数,有返回值 1.3 有参数,无返回值 1.4 有参数,有返回值 02. 函数的返回值 进阶 示例 -- 温度和 ...

  10. c&c++函数的参数和返回值的传递终结版

    c++函数的参数和返回值的传递方式有三种:值传递.指针传递和引用传递. 在这之前先看几个例子: 一, int a=10; int b=a; b+=10; 此时b是a的一个拷贝,改变b的值,a并不会受到 ...

随机推荐

  1. Agora 教程丨一个典型案例,教你如何使用水晶球“数据洞察”

    7 月初,声网Agora 水晶球的"数据洞察"功能正式版上线."数据洞察"可显示两种数据,一种是用量,另一种是质量. "数据洞察"的&quo ...

  2. JOI 简单题选做

    就是把洛谷上评分为紫的题做了一下(汗) 前两道题没做出来,暴露了自己在 dp 上的短板. イベント巡り 2 一开始想到贪心,但发现我们只要选 \(k\) 个即可,所以可以尝试一些更劣但是编号更小的做法 ...

  3. 针对im输入框的一种处理方式

    针对im输入框的一种处理方式 <template> <div class="chatInput"> <!-- 通过contenteditable使普通 ...

  4. 记一次 .NET 某传感器采集系统 线程爆高分析

    一:背景 1. 讲故事 前段时间有位朋友微信找到我,说他的程序使用 hsl 库之后,采集 plc 时内存溢出,让我帮忙看一下怎么回事,哈哈,貌似是分析之旅中的第二次和 hsl 打交道,既然找到我,那就 ...

  5. 在 Linux 内公网、云服务器搭建一套 K8s 集群

    前言 本文讲述如果在 Linux 搭建内/公网 Kubernetes 集群的详细步骤,解决搭建过程中的问题. 准备工作 Linux CentOS 7.x 两台及以上,本文用的 7.6 本文配置默认是在 ...

  6. @property、关联关系、继承、多继承

    1.@property使用来修饰属性的,将属性私有化改为公有化,但是可以设置属性条件 格式: class Person: def __init__(self,name,age): self.name ...

  7. ASP.NET Core如何知道一个请求执行了哪些中间件?

    第一步,添加Nuget包引用 需要添加两个Nuget包分别是:Microsoft.AspNetCore.MiddlewareAnalysis和Microsoft.Extensions.Diagnost ...

  8. Matlab蚁群算法求解旅行商问题

    目录 问题展现 解决代码 代码1 输出结果 代码2 输出结果 代码3 输出结果 问题展现 假设有一个旅行商人要拜访全国 31 个省会城市,他需要选择所要走的路径,路径的限制是每个城市只能拜访一次,而且 ...

  9. PyG 图神经网络依赖环境安装(Anaconda)

    1.默认用户在Anaconda的虚拟环境中已安装Pytorch 2.打开anaconda prompt命令窗, activate "你的虚拟环境名称" 3.在激活后的虚拟环境下输入 ...

  10. 如何根据需求选择合适的数据库管理工具?Navicat OR DBeaver

    1.写在前面 在阅读本文之前,糖糖给大家准备了Navicat和DBeaver安装包,在公众号内回复"Navicat"或"DBeaver"或"数据库管理 ...