域 缺省参数 函数重载 引用 编译链接

一:域

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

  1. C语言在汇编阶段生成的 f.o 文件,会把同名函数不做区分,这样 main 就找不到想用的那个函数是哪个了。
  2. 而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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值