首页 > 编程语言 >c++函数参数和返回值

c++函数参数和返回值

时间:2023-05-19 17:48:02浏览次数:41  
标签:函数 int ++ c++ 函数参数 Result 返回值

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 其实就是一个函数对象,会在编译期展开成一个函数对象体。

标签:函数,int,++,c++,函数参数,Result,返回值
From: https://www.cnblogs.com/zijian-yang/p/17415864.html

相关文章

  • Qt C++5.9开发指南
     第1章认识Qt1.1Qt简介1、Qt是一套应用程序开发类库,但与MFC不同,Qt是跨平台开发类库。2、跨平台意味着只需要编写一次程序,在不同平台上无需改动或只是需要少许改动后再编译,就可以形成不同平台上运行的版本。1.2Qt的获取与安装1.2.1Qt的许可类型1.2.2Qt的版本1、如果不......
  • c++ 输入文件流ifstream用法详解[转]
    目录文章目录输入流的继承关系:成员函数Publicmemberfunctions1,(constructor)2,ifstream::open3,ifstream::is_open4,ifstream::close5,ifstream::rdbuf6,ifstream::operator=Publicmemberfunctionsinheritedfromistream7,std::istream::operator>>8,istream::gcount9,istr......
  • C++ Primer 学习笔记—— 第三章
    第三章字符串、向量和数组前言标准库是C++必不可少的一部分,作为C++的延伸,标准库的优雅令人陶醉。如标题所言,在这一章我们将要学习数组、字符串和向量。若学习过其他编程语言,相信对数组并不陌生。其作为固定存储序列,能够为我们提供很多数据结构的解决思路,但是其在灵活性方面的......
  • c++ 子类与父类的构造函数继承关系
    规范上,子类构造函数肯定是会调父类的构造函数。 如果代码中没写,就会隐含调用父类的默认构造函数(即那个无参构造函数)。如果父类没有,编译报错。 1,展示了当子类要调用父类中带参数的构造函数时:#include<iostream>#include<cstdio>classA{public:A(){printf("......
  • C++实现查询本机信息并且上报
    业务需求共享文件夹、盘会导致系统安全性下降,故IT部门需要搜集公司中每台电脑的共享情况,并且进行上报关键字WMI查询、Get请求、C++网络库mongoose前置需要1、简单C++语法知识2、mongoose库的导入3、C++项目的启动代码复制并不能直接使用,需导入mongoose库完整github项目代码......
  • 详解c++STL—容器list
    1、list基本概念1.1、概念描述链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的链表的组成:链表由一系列结点组成功能:将数据进行链式存储1.2、结点的组成一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域STL中的链表是一......
  • c++ stoll
    stoll,字符串转换为longlong(114条消息)C/C++编程笔记:stol和stoll函数,函数调用中的字符串转换_c++stoll_一起学编程的博客-CSDN博客......
  • C/C++银行自助存取款机模拟程序[2023-05-18]
    C/C++银行自助存取款机模拟程序[2023-05-18]设计一个银行自助存取款机模拟程序,银行自助存取款机的用户包括银行管理员和客户,程序可实现这两类用户的基本操作需求。银行管理员:凭身份密码登录后可查看银行自助存取款机的余额、查询给定时间段内所有的交易信息(卡号、交易类型、交......
  • 从C到C++:学习C++的高级语法、STL和面向对象编程
    好的,那我为您写一篇有关C++编程的文章,详情如下。标题:从C到C++:学习C++的高级语法、STL和面向对象编程开头:C++语言是对C语言的扩展,是一种面向对象的程序设计语言。它具有丰富的数据类型、函数模板、类模板、标准模板库等高级特性。掌握高级语法、STL和面向对象编程,可以使程序员更加高......
  • C++趣味编程
    分糖果1#include<iostream>2usingnamespacestd;3intmain()4{5inti,count=0;6inta[10]={10,2,8,22,16,4,10,6,14,20};7intb[10]={10,2,8,22,16,4,10,6,14,20};8do{9a[0]=b[0]/2+b[9]/2;10for(intj=1......