1.函数的默认值
1.自右向左依次赋予
2.不能重复赋值,否则会报重定义的错误
3.一般赋在声明上
4.默认值的限制
1.不能使用局部变量
2.能使用全局变量,函数
int Get(int a, int b, int c = 20)//_Get//自右向左赋值
{
return a + b + c;
}
int main()
{
cout<< Get(10, 20) << endl;
cout<< Get(10, 20, 30) << endl;
return 0;
}
2.函数重载
C++的函数生成规则和什么有关系
函数原型 返回值+函数名+形参
形参
形参的个数
形参的顺序
形参的类型
函数重载的三要素
1.函数名相同
2.形参的个数,形参的顺序,形参的类型不同
3.同作用域
const / volatile 指针 / 引用可以作为函数重载的前提
- 防止编译器对汇编指令进行顺序上的优化
- 防止编译器存储变量的副本值
int Sum(int a, int b)
{
cout << "Sum(int ,int)" << endl;
return a + b;
}
float Sum(float a, float b)
{
cout << "Sum(float ,float)" << endl;
return a + b;
}
char* Sum(char* a, char* b)
{
cout << "Sum(char* ,char*)" << endl;
return strcat(a, b);
}
//int a = 10;
int main()
{
//int a = 20;
int Sum(int, int);
//cout << "a:" << a << endl;
Sum(10, 20);
Sum(10.1f,20.1f);
Sum("hello","world");
return 0;
}
3.inline 函数
编译阶段
1. 调用点直接代码展开
inline 函数和普通函数的区别
普通函数有开栈和清栈的开销
inline 函数没有开栈和清栈的开销
缺点:
inline 是以代码膨胀为代价 空间换时间
代码体小的情况下 函数执行的开销 < 函数开栈清栈的开销 建议用 inline
代码体大的情况下 函数执行的开销 > 函数开栈清栈的开销 不建议用 inline
inline 和 static 修饰的函数的区别
inline 无开栈清栈的开销,代码直接展开
static 有开栈清栈开销
inline 不生成符号
inline 函数和宏的区别
inline 函数在编译阶段展开代码段,有安全检查和类型检查,是一个高级的宏
宏在与编译阶段进行字符替换,但是没有安全检查和类型检查
inline 函数的注意事项
1.inline 的实现写在头文件中
2.inline 只在 release 版本生效,release 不能调试。debug 版本有开栈和清栈的开销
3.inline 只是给编译器的一个建议 ,具体处理由系统决定,递归、switch、循环不支持
4.inline 基于实现的 不是基于声明的
inline int Sum(int, int);
int Sum(int a, int b)
{
return a + b;
}
int main()
{
Sum(10, 20);//10+20;
return 0;
}
4 .c .cpp的相互调用
extern “C” 包含代码是以 c 规则处理
1.C++调用C的接口
.cpp 文件中加 extern “C”
2.c调用C++的接口
1.修改 C++ 的文件
.cpp 文件中加 extern “C”
2.不修改 C++ 的文件
加中间层处理
3.源文件不确定什么编译器编译
#ifdef __cplusplus
静态链接和动态链接的区别
- 静态链接在 windows 链接的是 *.lid 库,在 Linux上 链接的是 *.a 库
动态链接在 windows 链接的是 *.dll 库,在 Linux上 链接的是 *.so 库 - 静态链接在程序运行前就要链接,生成的可执行文件里面包含定义到的函数,即使函数没有用到,依旧存在
动态链接在可执行文件运行的时候链接,函数的代码段不占用可执行文件,减少可执行文件的大小,动态链接的速度不如静态链接快
#ifdef __cplusplus
extern "C"
{
#endif
int Sum(int a, int b)
{
return a + b;
}
#ifdef __cplusplus
}
#endif
5.引用
引用:别名
底层处理:
C++ 中和指针处理方式相同,在用到引用变量的地方系统会自动来解引用
定义引用变量需要开辟内存,但是无法访问到开辟的内存。因为只要访问引用,就会进行解引用,访问的永远都是他引用的内存。
引用的特点
1. 定义一定要初始化,因为定义一个引用变量底层要开辟 4 个字节的内存空间,而这个空间无法访问。如果不初始化无法把地址放入内存里。 int &a
2. 初始化的值必须要能取地址 int &a = 10;l
3. 引用不能改变 int &a = b; &a = c;
4. 引用只能使用引用变量所引用的内存单元(变量)
float a = 10.5;
int b1 = (int)a;//把float类型强转成int;
int b2 = (int) &a;// &a指的是a的地址,前面的int是将十六进制表示的地址强制转换为int类型;
int b3 = (int &)a;//将a的引用强制转换成整型,就是说a所在的内存地址中数据本来是float类型储存的,引用时非要按int来用。
int a = 10;
int &b = a;
int *p = &a;
&a、&b、&*p 指的是同一块内存,访问 &b,无法访问到 b 的内存,就会解引用访问到 a
不能返回局部变量的地址
不能返回局部变量的地址或引用
int main()
{
int a = 10;
int &b = a;
int array[10] = {0};
int *p1 = array;
int (&q)[10] = arrqy;
}
6.const
参考:https://blog.youkuaiyun.com/Mylily_123/article/details/78438936
C
- 不是必须初始化,但是,如果不初始化后面无法给一个合法的值。
- 修饰的量是常变量,编译阶段 常变量不能做为左值出现
C++
- 修饰常量,是一个不能修改的量,必须初始化。
- 编译阶段使用到常量名字的地方把常量替换成常量初始化的值
- const 修饰的符号生成的符号是 local,在 const 常量定义外加 extern,符号变成全局变量
const int data = 20;// data--->local
//const 把所有的 data 替换成20,但是只是在本文件中,因此 const 只在本文件有效,是一个局部变量
extern const int data = 20; // data--->global
const 修饰的常变量和普通变量的唯一区别:常变量定义以后,不能作为左值存在。两者之间的编译方式一样
const 一级指针的结合
int a= 10;
int *p = &a;
const int *p = &a;//const修饰*p
int const *p = &a;//const修饰*p
int *const p = &a;//const修饰p
const int* const p =&a;//const修饰*p和p
const int a = 10;
int *p = &a;//编译肯定是通不过的,通过*p可能会修改a的值,要杜绝这种风险,所以报错。
int a = 10;
const int*p = &a;//const 修饰*p也就是不会通过解引用修改a的值,在这里是没有问题的
int *q = p;//p存放的就是整型常量的地址,const int* 和int*都可以赋给p,如果是const int*就要杜绝,所以这里也是有错的。
const 二级指针的结合
int a = 10;
int *p = &a;
const int **q = &p;//const修饰**q,也就是a的值不可改变,但是*p可能改变a的值,错误
int a = 10;
const int*p = &a;
int **q = &p;//const修饰*p,也就是a的值不能改变,但是**q可能修改a的值,错误
//应该是:
int a = 10;
const int *p = &a;
const int **p = &p;
const 引用的结合
常引用可以引用立即数
立即数放到临时量中
常引用来引用临时量
临时量的产生:1、函数调用之前 2、函数的return语句处 3、函数调用之后
int GetInt()
{
int val = 10;
return val;
}
int * GetIntPtr()
{
int val = 10;//不能返回局部变量的地址和引用
static int val = 10;
return val;
}
int main()
{
int &a = GetInt();//返回值通过寄存器eax带回,不能取地址,就是相当于带回一个立即数,int &a=10;错误
const int &a = GetInt();//产生临时量
// const int temp = GetInt();
// const int &a = temp;
int *p = &GetInt(); //返回值没地址可取,也是不正确的
int *&p = GetIntPtr(); //返回值通过寄存器eax带回一个立即数 //err
int * const &p = GetIntPtr(); //函数调用之后产生临时量 //ok
int **p = &GetIntPtr(); //无法取地址没有左值
int &val = *GetIntPtr(); //*GetIntPtr();代表存储寄存器带回来的值的内存 //ok
return 0;
}
形参加上 const 的作用
1.防止实参被修改
2.引用立即数
const &做形参
1.避免实参被修改
2.接收不能取地址的数据
引用做形参 普通变量做形参
1.引用修改实参的值
2.引用不能引用立即数,部分实参无法调用
不能返回局部变量的地址或引用
7.new ,delete
new
1.开辟内存并初始化
2.内存不足 抛出异常
delete
new和malloc的区别
- new 是关键字,malloc 是函数
- 在栈上,new 是自由存储区域,malloc 在堆上
int a;
char *p = new(&a)char;//在a上开辟一个char
- new 内存分配成功时,会返回对象类型的指针,两者之间不用进行类型转换,malloc 内存分配成功时,返回 void * ,要强转成需要的类型
- new 内存分配失败,抛出异常,不会返回 NULL,malloc 内存分配失败,返回NULL
- new 分配内存不用指定内存块的大小,malloc 分配内存要指定内存的大小。
- new 可以重载,malloc 不能重载
- new 在数组方面调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用析构函数。
malloc 分配对象时不会调用构造函数,析构函数,单纯开辟内存,要动态分配一个数组的内存,需要手动自定数组的大小 - malloc 开辟的内存不够,可以使用 realloc 扩容,new 没有这样的函数
- new可以开辟 const 内存, malloc 不能开辟
- new 可以调用 malloc,malloc 不能调用 new
动态开辟二维数组
malloc
int** pbarr = (int**)malloc(sizeof(int*)* 10);
for (int i = 0; i < 10; i++)
{
pbarr[i] = (int*)malloc(sizeof(int));
}
for (int i = 0; i < 10; i++)
{
free(pbarr[i]);
}
free(pbarr);
new delete
int** cppbarr = new int*[10];
for (int i = 0; i < 10; i++)
{
cppbarr[i] = new int[10];
}
for (int i = 0; i < 10; i++)
{
delete[] cppbarr[i];
}
delete[] cppbarr;
8.名字空间作用域
C++:namespace 名字空间作用域
class
C中:全局作用域
局部作用域
同名的名字空间作用域会合并