





















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) ...
优点:
GetResult({1}) 会赋值到一个 临时的 Result 对象中,拿不到返回值。正常来说也不会这样做。class Result
{
public:
int result;
};
Result* GetResult() ...
优点:
class Result
{
public:
int result;
};
Result GetResult() ...
优点:
一般如果是 简单结构体,用 返回一个临时对象的方式解决。
如果使用 返回一个参数指针,一般改成返回一个id,用一个manager来管理内存机制。或者 共享内存,内存池来解决内存泄露后续的问题
用 参数引用来返回的话,一般会这么定义 int GetResult(Result& result) 函数返回值,用来返回状态码,真正的数据,放到 result 中。
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 其实就是一个函数对象,会在编译期展开成一个函数对象体。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。