c++入门

命名空间

解决c语言命名冲突问题
1.我们自己定义的变量,函数可能和库里面重名冲突
2.但是进入公司项目组里面,做的项目通常比较大,多人协助,会导致代码中命名冲突 c语言无法解决这个问题,除非换名字 c++提出了新语法,叫命名空间,
3.命名空间里面可以包含各种东西,函数,结构体,变量

#include<stdio.h>
#include<stdlib.h>
int rand = 0;

int main()
{
	printf("%d", rand);//如果没加stdlib.h这个头文件的化,
	//是可以正常运行的,但是如果包了这个头文件时,rand就是里面的函数
	//因为在预处理的时候,头文件展开,里面有rand这函数
	printf("%d", rand);//以十进制方式打印,出来的是地址
	return 0;
}

namespace空间-----是一个域

#include<stdio.h>
//定义了一个叫b的命名空间,命名空间是全局的
namespace b
{
	//他们还是一个全局变量,放到静态域,
	int rand = 0;//加这个就是为了避免冲突
	int a = 1;
}

int a = 0;//全局域

int main()//函数里面叫局部域
//编译器会先在局部去找,再去全局去找,否则就会报错
{
	printf("%dn", rand);
	printf("%dn", b::rand);//::叫做域作用限定符,意思是去取左边这个域里面的,很明显要去b这个域里卖弄去找
	int a = 1;
	printf("%dn", a);//a为局部域里面去找
	printf("%dn", ::a);//a到左边的空白域里面去找,就是去全局域里面去找
	return 0;
}

namespace也可以嵌套包含
对嵌套包含的要用两层::

//命名空间里面还可嵌套
namespace bt//命名空间里面不仅可以定义变量,还可以定义函数,类型
{
	int rand = 10;
	int add(int left, int right)
	{
		return left + right; 
	}
	struct node
	{
		int val;
		struct  node* next;
	};
	namespace n1
	{
		int d = 2;
	}
}

//使用命名空间里面的的东西
//不同文件里面定义的同样的命名空间,会被合并成一个,命名空间
int main()
{
	bt::rand = 10;//使用其与其他进行隔离
	struct bt:: node nt;//要加bt::才可以找得到里面的东西
	bt::add(1, 2);//这样就可以找到了,只是影响了编译器找的规则
	bt::n1::d = 2;//运用指定,这样子可以把嵌套的引用出来
	//这种可以做到最好的命名隔离,
	return 0;
}

c++的输入输出

#include<iostream>
//输入输出流

//using namespace std;
//c++库的实现定义在一个std的一个命名空间中,


int main()
{
	cout << "hello world" << endl;
	return 0;
}
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
int main()
{
	std::cout << "hello world" << std::endl;
	return 0;
}
#include<iostream>
using namespace std;
//cout可以自动识别类型
int main()
{
	cout << "hello world" << endl;//endl是用来换行的
	double d = 1.2;
	int k = 12;
	cout << d <<" "<<k<< endl;//自动识别类型不用%d,不可以控制小数点的位数,c++d输入输出可以混在一起写
	cin >> k >> d;//把数据流向k和d
	return 0;
}

缺省

全缺省和半缺省

//缺省参数
void func1(int a = 0)//如果func1里面传了参数,就和传的是一样的,如果func1里面没有传参数,相当于实参是0,形参是a
//把缺省值去当作实参传给a
{
	cout << a << endl;
}

void func2(int a = 1, int b = 2, int c = 3)//全缺省
{
	cout << a << " " << b << " " << c << endl;
}

//半缺省,缺省一部分参数,必须从右往左缺省,必须连续缺省
void func3(int a,int b=32,int c=89)
{
	cout << a << " " << b << " " << c << endl;
}
int main()
{
	func(1);
	func1();
	func2();//啥都不传
	func2(13, 42);//从左往右给
	func3(13);//a必须要传,缺省的可以不传
	return 0;
}

struct stack
{
	int* a;
	int top;
	int capacity;
};

void stackinit(struct stack*ps,int capcity=4)
{
	ps->a = (int*)malloc(sizeof(int) * capcity);
	ps->top = 0;
	ps->capacity = capcity;
}

/*int main()
{
	struct stack st;
	stackinit(&st);//不知道栈最多可以存多少数据,就用缺省值初始化
	stackinit(&st, 100);//知道最多可以存多少数据,就传值,减少增容次数,提高效率
	return 0;
}*/

//缺省参数不能同时在声名与定义中出现,推荐大家写在声名,方便查找

函数重载

函数重载:是函数的一种特殊情况,同一个函数有不同功能, 这些同名函数的形参列表(参数个数或类型或顺序)不同,用来处理实现功能类似的不同问题

函数名相同的不同函数

//1.参数的类型不同
int add(int left, int right)
{
	cout<<left << " " << right << endl;
	return left + right;
}

double add(double left, double right)
{
	cout << left << " " << right << endl;
	return left + right;
}

//2参数个数不一样
void f()
{
	cout << "f()" << endl;//没传参数就调用他
} 

void f(int a)
{
	cout << "f(int a)" << endl;//传了参数就调用他
}


//顺序不同

void fs(int a, char b)

{
	cout << " fs(int a, char b)" << endl;
}

void fs(char b,int a)

{
	cout << " fs(char b,int a)" << endl; 
}

//返回值不同不能构成承载,返回值不同不能区分,参数不同可以区别的
//void a(int a)
//int a(int a)

//缺省值不同,不能构成重载

//1.
//void cz(int a)
//{
//	cout << "as" << endl;
//}
//void cz(int a = 21)
//{
//	cout << "asd" << endl;
//}

//2.形参个数不同,构成重载,但是使用的时候要小心

void as()
{
	cout << "ead" << endl;
}
void as(int a = 21)
{
	cout << "as" << endl;
}

int main()
{
	
	cout << add(3, 2) << endl;
	cout<<add(3.3, 8.3)<<endl;
	f();
	f(1);
	fs(2, 's');
	fs('a', 32);
	as();//存在歧义
	return 0;
}

引用

引用就是给别的变量取别名

int main()
{
	int a = 10;
	int b = a;//把a的值赋给b
	//引用定义。
	int& c = a;//引用,在变量名和类型的中间就是一个引用,是a的引用,
	//c是a的别名
	//c和a的地址和值都是一样的
	//引用在语法层,我们要理解为这里没有开一个新空间,就是对原来的取了一个新名称叫做b
	//如李逵,黑旋风都是一个人
	int a = 20;//a和c都改成20,
	int c = 30;//c和a都改成了30,
	//如李逵吃饭了,黑旋风也吃了
	//黑旋风吃饱了,李逵也吃饱了
	int* p = &a;//单独就是取地址
	return 0;
}

引用的特性

1.引用必须初始化
2.一个变量可以有多个引用
3.引用只能用一个实体

int main()
{
	//1.引用在定义的时候必须初始化,引用是取别名,要说清楚是给谁取别名
	int a = 10;
	int& b = a;
	//2.一个变量可以有多个引用,
	int k = 3;
	int& kt = k;
	int& ktt = k;//相当于可以有很多个外号,
	int& m = kt;//这样也可以,kt,k,m,kkt都是一个空间,一个人可以有很多个名字
	//3.引用一旦引用了一个实体,再不能引用其他实体了,
	int d = 10;
	int& dt = d;
	int c = 20;
	//1.这里有两层,是让dt变成c的别名呢?     否
	//还是把c赋值给dt呢?                    是
	//经过调试是把c赋值给dt,dt和d的值都变成了20
	dt = c;
	return 0;
}

引用的应用

1.引用做函数参数

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//cpp的实现
//1.做参数
void swap(int& r1, int& r2)//r1是x的引用,x的别名,传引用,传地址和传引用功能相同 ,形参的改变可以改变实参
{
	int t = r1;
	r1 = r2;
	r2 = t;
}

//传值和传地址,传引用构成重载,但是调用时,传值和传引用存在歧义,不知道是传值还是传引用,
int main()
{
	int x = 0, y = 1; 
	swap(&x, &y);
	swap(x, y);//传引用  
	return 0;
}
void slistpushback(slistnode*& phead, slistdate x)//指针的引用,别名不混在一起,phead的改变就是改变plist,不用传二级指针,好认识
{
	slistnode* newnode = buynode(x);
	//那么我们这个时候要找尾
	//找到尾节点的指针
	if (phead == NULL)
	{
		phead = newnode;
	}
	else
	{
		slistnode* tail = phead;//我们要让tail走到尾部去,而非走到空
		while (tail->next != NULL)
		{
			tail = tail->next;
		}//找到了尾节点,链接新节点
		tail->next = newnode;
	}
}
//指针也可以使用引用
int main()
{
	int a = 10;
	int& b = a;
	int* p = &a;
	int*& p2 = p;
	//p2也是地址,p也是地址,p2是p的别名
	return 0;
}
int* single(int* num, int numsize, int& returnsize)//returnsize就是传回去的长度的别名
{}

2.引用做返回值

传值返回会有拷贝,就会有空间
传引用返回就不会有拷贝,原样返回
如果使用的变量在函数使用完之后没有销毁或者返回的空间较大,就用引用返回


int dd(int a, int b)
{
	int c = a + b;
	return c;//返回的是c的临时拷贝,相当于int tmp=3被传回去了,只能调用一次,调用完了之后就会变成随机值
}

int& d(int a, int b)
{
	int c = a + b;
	return c;//f这里返回的就是c别名,
	//c传给int& tmp,就是c,所以不会有拷贝造成的空间浪费
}

//但是如果函数返回的时候,返回的对象出了函数的作用域还没有销毁,就可以用传引用返回
int& cout()
{
	static int n = 12;
	n++;
	//……
	return n;//n是一个静态变量,出了函数域不会消失,所以用传引用返回
}

int main()
{
	int ret = dd(1,2);
	//int& ret=dd(1,2),这样是不行的
	int r = d(1, 2);
	//这样写会存在非法访问,因为add(1,2)的返回值是c的引用,所以add栈帧销毁了以后,回去访问c位置的空间
	//如果add函数栈帧销毁了,清理空间那么取c值的时候,取到的就是随机值,给ret就是随机值,当然这个取决于编译器的的实现
	cout << ret << endl;
	cout << r << endl;
	return 0;
}
struct stack
{
	int* a;
	int top;
	int capacity;
};

void func1(struct stack st)//传值参数,会有拷贝,有空间开辟,原样拷贝
{

}

void func(struct stack* st)//传地址参数,指针开辟了4个字节
{

}

//引用做参数,随时都可以,可以1.提高效率,2.形参的修改可以影响实参,输出型参数
void func(struct stack& rst)//传引用参数,rst就是st的别名,没有开辟空间
{

}

  int main()
{
	struct stack st;
	func1(st);
	//指针和引用可以构成函数重载,因为指针和引用是两种不同类型的参数
	func(&st);
	func(st);
	return 0;
}

//传引用返回的价值
//1.提高效率,2.修改返回变量

int& at(int i)//处理作用域,还在就很有用
{
	static int a[10];//第一次进入就创建了一个数组
	return a[i];//返回这数组第i个位置的别名
	//变成了可读可写的,
}

常引用

用const进行修饰,在做参数的时候尽量都加一个const进行修饰

//尽量使用const引用进行传参
 void f(const int& a)
{
	cout << a << endl;
}

//const 引用通吃
// const type& 可以接收各种类型的对象

int main()
{
	//权限被放大了,a从一个只读不写的,变成b一个可读可写的就是错的
	//const int a = 10;
	//int& b = a;
	
	//权限不变,就可以
	const int a = 10;
	const int& b = a;//
	
	//权限缩小
	int c = 2;
	const int& d = c;//从可读可写的变成只读不写的是可以的
	
	double d = 1.11;
	int i = d;//类型转化,把d的整形部分转过去,不加引用可以
	//int& k = d;//
	//d的临时变量是具有常性,只读不写,而int&具有可读可写, 
	//普通引用引的是左值,const引用引的是右值
	//临时变量具有常性,不能被修改,
	//const 接收就是常量了
	return 0;
}

引用的优点:

引用和指针的不同点
1.引用在概念上定义一个变量的别名(没有开空间),指针存储一个变量的地址
2.引用在定义的时候必须初始化,指针最好初始化,但是不初始化也不会报错(会变成野指针)
3.引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个相同类型的实体(如我们在链表的时候指向不同的地方)
4.没有NULL的引用,但是有NULL 的指针
5.在sizeof里面的含义不同,引用为引用类型的大小,但是指针是地址空间所占字节大小(32位下是4个字节或64位里面的8个字节
6.引用自加**++是引用的那个实体+1**,而指针是指针指向偏移一个类型的大小
7.访问实体方式不同,指针需要解引用,引用编译器里面会自己处理
8.有多级指针没有多级引用,引用比指针更加安全

extern “C”

c++程序,调用c的库,再c++程序中加extern "C"
告诉c++编译器,{}里面的函数是用c的编译器编译的,链接的时候,用c的函数名规则去找,就可以链接上了,只有c++才认识extern “c”

c程序,调用c++的库,再c++库中加extern “C”
c掉c++,c++的静态库就会用c的函数名修饰规则去处理以下函数,所以就不能用重载

//在c程序调用c++时候,在c++库里面加extern "C"
#ifdef  __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C
#endif //  __cplusplus

内联函数inline

我们在每次调用函数都会建立栈帧,有栈帧就会有消耗,需要建立栈帧,栈帧中要保存一些寄存器,结束后又要恢复,
可以看到这些都是有消耗的,对于频繁调用的小函数,能否优化一下呢,
c语言可以用宏,来操作避免栈帧的建立

#define ad(x,y) ((x)+(y));

c++的实现方法inline,内联函数

有了inline就不需要去用c的宏,因为宏很复杂,很容易出错 inline但是展开的话,指令就会特别多
1.inline是一种以空间换时间的做法,省去调用函数栈帧的过程,所以代码量长的函数,或者递归函数就不适合用inline,展开之后就会有特别多的指令,变得很大
所以小函数比较适合用inline
2.inline对于编译器只是一个建议,编译器会自动识别,递归或长的编译器会自动忽视inline
3.inline函数声名和定义不能分离,.h和.cpp都要加一个inline,

结论:短小频繁使用的函数就定义成inline

inline int add(int x, int y)//这样就不会建立栈帧,内联函数,默认是不会展开的,没有函数调用了
{
	int ret = x + y;
	return ret;
}

inline void swap(int& a, int& b)//swap也会经常会出现
{
	int tmp = a;
	a = b;
	b = tmp;
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>