目录
一、函数的默认值
1、自右向左依次赋值(原因:与匹配 顺序有关,参数匹配是从左向右);
2、不能重复赋值;
3、一般给在声明上。
二、函数符号生成规则
1、C函数符号生成规则:
与函数名有关
_cdel调用约定(C标准调用约定):函数名前加下划线。(调用方开辟,调用方清理)
_stdcall调用约定:函数名前加下划线,函数名后加“@”符号和其参数字节。(调用方开辟,被调用方清理)
_fastcall调用约定:函数名前加“@”符号,函数名后加“@”字符和其参数字节。
2、C++函数符号生成规则:
函数原型有关:
1)函数返回值(不能作为重载的依据);
2)函数名称(不能作为重载的依据);
3)函数形参(类型、个数、顺序)。
_cdecl调用约定:“?”+函数名+参数表的开始标识 “@@YA” + 函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。
_stdcall调用约定:“?”+函数名+参数表的开始标识“@@YG”+函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。
_fastcal调用约定:“?”+函数名+参数表的开始标识 “@@YI”+ 函数返回类型代号+参数类型代号 +结束标识“@Z”或“Z”(无参数)。
int Max(int a, int b)//?Max@@YAHHH@Z
{
return a > b ? a : b;
}
double Max(double a, double b)//?Max@@YANNN@Z
{
return a > b ? a : b;
}
char Max(char a, char b)
{
return a > b ? a : b;
}
int main()
{
int max = Max(10, 20);
char max = Max('a', 'b');
double max = Max(0.23,0.21);
return 0;
}
三、函数重载
同名函数的关系
函数重载的三要素:
1)同名;
2)形参不同(①形参类型不同、②形参个数不同、③形参顺序不同)
3)同作用域
注意:同函数名、同参数列表、返回值不同的函数重载 和 有默认实参函数的重载 不能构成函数重载。编译器只使用形参列表来区分重载的函数,所以同函数名,同参数列表,返回值不同的函数重载 和 有默认实参函数的重载,编译无法区分,然后报错。
四、内联函数
将规模小,而使用频繁的函数声明为内联函数。
1、定义:
inline函数:在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去。
指:当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。这样做的好处是省去了调用的过程,加快程序运行速度。这样做的不好处:由于每当代码调用到内联函数,就需要在调用处直接插入一段该函数的代码,所以程序的体积将增大。内联函数并不是必须的,它只是为了提高速度而进行的一种修饰。要修饰一个函数为内联型,使用如下格式:
inline 函数的声明或定义
简单一句话,在函数声明或定义前加一个 inline 修饰符。
inline int max(int a, int b)
{
return (a>b)? a : b;
}
简而言之就是:把代码在函数的调用点直接展开(以空间换时间)
2、注意事项:
1)写在头文件下;在一个文件中定义的内联函数不能在另一个文件中使用。它们通常放在头文件中共享。
2)只在release版本生效;
3)是给编译器的一个建议(由编译器决定);
4)加在声明点一定无效(inline是基于实现的,不是基于声明的)内联函数要在函数被调用之前声明。关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。
5)一定不能设置成inline 的有(递归、循环、switch),否则,函数定义时即使有inline关键字,编译器也会把该函数作为非内联函数处理。
3、inline函数和宏的区别 (内联是一种更安全的宏)
inline函数是在编译阶段展开;有类型检查和安全检查
宏是在预编译阶段展开代码,不需要额外的空间和时间方面的开销。无类型检查和安全检查。
宏的不足:
1)宏不能访问对象的私有成员。
2)宏的定义很容易产生二意性。
内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。可以向调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。
当执行开销 小于 调用开销(开栈开销) 建议用inline
执行开销 大于 调用开销 不建议用inline
五、C/C++的相互调用
C 编译器编译函数时不带参数的类型信息,只包含函数的符号名字。如 void foo( int x ) , C 编译器会将此函数编译成类似 _foo 的符号,C 链接器只要找到了调用函数的符号,就会认为链接成功。而 C++ 编译器为了实现函数重载,会在编译时带上函数的参数信息。如它可以把上面的函数编译成类似于 _foo_int 这样的符号。
在C/C++相互调用时用 extern"C "声明 把包含的代码以C的规则进行处理。
1、C++调用C
在.cpp文件加上extern"C"
/* cfile.c */
#include <stdio.h>
void C_Func()
{
puts("Hello, I'm C_Func");
}
/* cppfile.cpp */
#include <iostream>
extern "C" void C_Func(void);
int main()
{
C_Func();
return 0;
}
2、C调用C++
1)C++源文件可以修改 在.cpp加extern“C”(整个函数)
2)C++源文件不可修改 加中间层
/* cppfile.cpp */
#include <iostream>
#include "cpphfile.h"
using namespace std;
void Myclass::print()
{
cout << "Hello, I'm print function." << endl;
}
/* cpphfile.h */
#ifndef _CPPCLASS_H__
#define _CPPCLASS_H__
class Myclass{
public:
void print();
};
#endif
/* wrapper.cpp 这个文件实现调用 C++ 的接口*/
#include "cpphfile.h"
extern "C" {
struct wrapper_class{
Myclass w_class;
};
struct wrapper_class* get_object()
{
return new struct wrapper_class;
}
void call_cpp_print(struct wrapper_class* p)
{
p->w_class.print();
}
}
/* cfile.c */
#include <stdio.h>
struct wrapper_class *test;
int main()
{
/* 创建对象 */
test = get_object();
/* 调用 C++ 函数 */
call_cpp_print(test);
system("pause");
}
.c被调用 用 #ifdef_cplasplas
六、引用
符号:& 别名
#include<iostream>
using namespace std;
int main(){
double a=10.3;
int &b=a; //错误,引用的类型必须和其所绑定的变量的类型相同
cout<<b<<endl;
}
引用的特点:
1)引用一定要初始化(要先有内存单元再起别名)
底层使用指针支持,在所有引用变量使用的地方,系统自带解引用。
2)引用不能引用不能取地址的数据(只能引用能取地址的数据)
3)引用不能改变(别名一旦赋予,便不能改变)
4)引用变量使用时使用的是引用变量(别名)所引用的 变量
5)引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用
#include<iostream>
using namespace std;
int main(){
int value=10;
int &new_value=value;
cout<<"value在内存中的地址为:"<<&value<<endl;
cout<<"new_value在内存中的地址为:"<<&new_value<<endl;
return 0;
}
//value 地址:0x6ffe44
//new_value 地址: 0x6ffe44
常引用 引用 不能取地址的数据 数据生成一个临时量,取临时量的地址,常引用引用临时量。
引用作为函数的返回值
引用作为函数的返回值时,必须在定义函数时在函数名前加&
七、const
C++ const修饰的属性为local属性
在C语言中:const修饰的是常变量 在编译阶段 常变量是否被做左值,其他处理与变量一样。
在C++中 :是常量 在编译阶段 把用到常量的地方替换成常量初始化的值。
语法:const 类型 &引用名=目标变量名;
常引用不允许通过该引用对其所绑定的变量或对象进行修改
#include<iostream>
using namespace std;
int main(){
int a=10;
const int &new_a=a;
new_a=11;//错误!不允许通过常引用对其所绑定的变量或对象进行修改
return 0;
}
#include<iostream>
#include<string>
using namespace std;
string func1(){
string temp="This is func1";
return temp;
}
void func2(string &str)//改为 void func2(const string &str)
{
cout<<str<<endl;
}
int main(){
func2(func1());
func2("Tomwenxing");
return 0;
}
//程序报错:这是由于func1()和“Tomwenxing”都会在系统中产生一个临时对象(string对象)来存储它们,而在C++中所有的临时对象都是const类型的,而上面的程序试图将const对象赋值给非const对象,这必然会使程序报错。如果在函数func2的参数前添加const,则程序便可正常运行了