域 缺省参数 函数重载 引用
一:域
C++库为了防止命名冲突,把自己库的东西都定义再std里。
局部变量:在栈存放
全局和静态变量:在静态区
常量:在常量区
动态分配:在堆
cout 是ostream类型的全局对象
cin 是istream类型的全局对象
endl 是个全局的换行符号
cout 和 cin 对比 c 的 printf 和 scanf 的优势是自动识别类型,原理是函数重载+运算符重载。
#include <iostream>
using namespace std; //整个展开std 但可能会导致命名冲突。(自己定义的东西和库中的命名冲突)
// 部分展开,对常用的展开就不用加std了。
using std::cout;
using std::endl;
int main()
{
int a;
cout << "hello world" << endl;
cin >> a;
return 0;
}
namespace定义的是域,本质解决c语言命名冲突问题。域不仅可以定义变量也可以定义函数。
命名冲突就是如下问题:有自己定义scanf后再使用scanf的库函数,就会冲突。
#include <stdio.h>
int main()
{
int scanf = 10;
scanf("%d", &scanf);
return 0;
}
解决方法:
#include <iostream>
namespace na
{
int scanf = 10;
}
int main()
{
printf("%x", scanf);
//指定访问命名空间中的
printf("%x", na::scanf);
return 0;
}
int a = 1;
int main()
{
int a=10;
printf("%d", a); //打印的是10,访问是局部优先
printf("%d", ::a); // ::代表访问左边的域,左边是空白代表全局域。
}
struct Preson
{
char name[10];
int age;
};
int main()
{
struct Preson p = {"校长", 10};
printf("name: %s, age: %d\n", p.name, p.age);
cout << "name: " << p.name << " " << "age: " << p.age << endl;
return 0;
}
二:缺省参数
缺省参数:声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。调用变得更灵活。
void TestFunc(int a = 0)
{
cout<<a<<endl;
}
int main()
{
TestFunc(); // 没有传参时,使用参数的默认值 0
TestFunc(10); // 传参时,使用指定的实参 10
}
全缺省和半缺省:
1.半缺省参数必须从右往左依次来给出,不能间隔着给,就是说半缺省只能缺第一个(最左边的),对于下面的函数而言就是不能给a,b值而不给c值。
2. 缺省参数不能在函数声明和定义中同时出现
3. 缺省值必须是常量或者全局变量
4. C语言不支持(编译器不支持)
void TestFunc(int a = 10, int b = 20, int c = 30)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
void TestFunc(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
三:函数重载
函数重载:就是一词多义,c语言不允许定义同名的函数,但是c++可以。是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,(要求)这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
要求:参数的 个数、类型、顺序不同。返回值不同无法构成重载。
int Add(int left, int right)
{
return left+right;
}
char Add(char left, char right) // char也可以重载
{
return left+right;
}
double Add(double left, double right)
{
return left+right;
}
void f(int a, int b, int c = 1) // 11
{
}
void f(int a, int b) // 22
{
}
int main()
{
Add(10, 20); // 字面量 给的整形默认是常量
Add('1', '2');
Add(10.1, 20.2);
// f是构成重载的,
f(1,2,3) //调用 11
f(1, 2)// 无法调用 因为不知道调用哪个。
return 0;
}
1.为什么C不支持重载,C++支持?:函数名修饰规则。
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
f.h f.cpp main.cpp 三个文件
预处理:头文件展开 + 宏替换 + 去掉注释 + 条件编译 预处理后 f.h 和 f.cpp 生成 f.i ,f.h 和 main.cpp生成 main.i
编译:检查语法,生成汇编代码 生成 f.s 和 main.s 有了声名,main.i 函数就可以被放过去
汇编:将汇编代码转换为二进制的机器码 生成 f.0 和 main.o
- C语言在汇编阶段生成的 f.o 文件,会把同名函数不做区分,这样 main 就找不到想用的那个函数是哪个了。
- 而C++中会加入函数名修饰规则支持函数名重载。函数名修饰规则会把执行函数的名字前后加一些东西,eg:在 linux 下,如果是 add()函数,他会把函数名改成 _Z3addii,ii 代表的是如果 add 的两个参数是整形就是 ii ,如果 add 的参数是 double,就是在后面是 _Z3adddd,dd代表的是它的两个参数是 double。修饰规则是对所有函数都修饰,不止在同名函数才发生。
链接:链接到一起生成可执行程序 a.out
2.extern “c” 作用是什么?
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译,不要用C++的规则修饰函数名去编译。
eg:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决。
中间件程序 通常会把它编译成 静态库或者动态库。如果用C++写的程序提供接口函数给使用者,C++使用者可以直接使用,但如果是一个C使用者调用函数会找不到地址,因为在汇编过程中C使用不修饰的函数名找调用函数,而C++在汇编会产生修饰的函数名,因此无法调用。 如果在C++的程序函数前面加上 extern “C”,则就不会修饰,C可以直接调用。这时候如果用C++去调用的话,只需要加上extern “C” 就可以调用。加上extern "C"函数就不会重载了。
四:引用
当参数和返回值是比较大的变量时,传引用传参和传引用做返回值还可以提高效率。
只要符合条件,尽量使用引用传参,还可以防止深拷贝。
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。引用在定义时必须初始化, 一个变量可以有多个引用。权限可以缩小但不能放大。
int main()
{
int a = 10;
int x = 1;
int& b = a; // b是a的别名(引用)就是重新给a这个地方起了一个新的叫法。
int& r = a;
r = x; // 这里是把x的值给r 也就是给a 不是让r这个引用引用到x上。
const int a = 10; // const变量是可以读,但不能写不能改不能修改。
//int& ra = a; // 该语句编译时会出错,a为常量。a是const 而ra是int的话类型就放大了可以修改,因此错
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
int b = 10;
const int& rb = b;//可以 因为rb引用是权限的缩小,所以可以。
int c = 10;
double d = 1.11;
//double&rc = c; // 不可以,因为c给rc引用会先产生一个double类型的临时变量值存放c,rc在引用这个临时变量,rc是这个临时变量的别名
const double& rc = c; //可以,因为临时变量具有常性
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d;
}
引用的作用: 引用在定义时必须初始化, 一个变量可以有多个引用。系统查越界基本是抽查,设置检查位查找你的读是否越界。
// 1.做参数
void swap(int& r1, int& r2)
{
int tmp = r1;
r1 = r2;
r2 = tmp;
}
int main()
{
int a = 1, b = 2;
swap(a, b);
return 0;
}
// 2.做返回值
int add(int a, int b) // 传值返回 先把c给临时变量 相当于是返回的是c的拷贝
{
int c = a + b;
return c;
}
//结合下面的使用,因此这块儿代码不能用引用返回,引用返回会导致返回临时空间的别名,函数结束返回值销毁返回这个位置的值
int& Add(int a, int b)// 传引用返回 返回对象c的引用
{
int c = a + b;
return c;
}
//这个可以使用引用返回,因为定义static是在静态区,出了函数(栈)后值还在
int& Add1(int a, int b)
{
static int c = a + b;
return c;
}
int main()
{
int ret1 = add(1, 2); // 把临时变量给ret1
const int& ret2 = add(1, 2); // 证明是临时变量而不是直接把C给ret2 因为int& ret2 = add(1,2)编译不过
//ret的值不确定,因为c返回的c的引用,但函数结束Add函数结束了c的值没了,但空间在,这个空间的值无法确定
int& ret = Add(1, 2);
Add(3, 4);
// 值是7 因为那块临时空间的值被覆盖了
cout << "Add(1, 2) is :"<< ret <<endl; //Add(1, 2) is 7 这块儿相当于越界访问了。越界读可能不报错
printf("hellow world");
cout << "Add(1, 2) is :"<< ret <<endl; //纯随机值,因为printf使用空间后空间返回这块的值被修改
return 0;
}
原文地址:https://blog.csdn.net/cleveryuoyuo/article/details/142591219
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!