C++基础(命名空间、输入输出、缺省参数、函数重载、引用、auto)

一、命名空间

命名空间可定义函数,也可嵌套定义空间域,可与头文件中命名空间名相同名字的合成一个空间

namespace sxl
{
	int scanf = 10;
	int strlen = 20;

	int Add(int left, int right)
	{
		return left + right;
	}

	//嵌套定义命名空间
	namespace N3
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
  • 访问命名空间
//默认访问全局的
	printf("%x\n", scanf);  
	printf("%x\n", strlen);

	//指定访问sxl命名的空间
	printf("%x\n", sxl::scanf);  
	printf("%x\n", sxl::strlen);

	sxl::Add(1, 2);

	sxl::N3::Sub(10, 3);

namespace标准库使用的三种方式:

  • 1.指定命名空间(最规范)
    使用时都要加std::

  • 2.把std整个展开相当于库里面的东西都到全局域(不推荐,日常练习写法)
    展开:using namespace std;

缺点:若自己定义的东西与库冲突就没办法解决

  • 3.对部分常用的库里面的东西展开(常用方式,1与2的折中方式)
    using std::cout;
    using std::endl;

::作用域操作符

int a = 0;

int main()
{
	int a = 1;
	printf("%d\n", a);     //1
	printf("%d\n", ::a);  //0 访问全局域
}

二、输入输出

  • ostream 类型全局对象 cout
  • istream 类型全局对象 cin
  • endl:换行

对比printf、scanf优势:自动识别类型(原理:函数重载、运算符重载)

int main()
{
	//对比C语言printf,scanf区别:自动识别类型
	int a = 10;
	int* p = &a;
	printf("%d,%p\n", a, p);
	std::cout << a << "," << p << std::endl;
	
	char str[10];
	std::cin >> a;
	std::cin >> str;
	std::cout << a << str << std::endl;  //输入sxl123来了,输出结果仍然是这个
	return 0;
}

三、缺省参数

缺省参数不能在函数声明和定义中同时出现

  • 1.全缺省参数

调用函数时没有指定参数,直接利用函数定义的参数

void TestFunc(int a = 10, int b = 20, int c = 30)
{
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	cout << "c=" << c << endl;
}
int main()
{
	TestFunc(1, 2, 3);
	TestFunc(1);
	TestFunc();
	TestFunc(1, 3);
	return 0;
}
  • 2.半缺省参数(默认参数必须从右往左,并且是连续的)
void TestFunc(int a, int b = 20, int c = 30)
{
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	cout << "c=" << c << endl;
}
int main()
{
//不能一个都不给值
	TestFunc(1, 2,3);
	TestFunc(1,2);
	TestFunc(1);
	return 0;
}

应用:调用时更灵活

struct Stack
{ 
	int* a;
	int size;
	int capacity;
};
void StackInit(struct Stack* ps, int InitCapacity = 4)
{
	ps->a = (int*)malloc(sizeof(int)* InitCapacity);
	ps->size = 0;
	ps->capacity = InitCapacity;
}
int main()
{
//要多少时就直接指定空间个数

	//知道栈里面最多存10个数据
	struct Stack st2;
	StackInit(&st2, 10);

	//不知道栈里面可能存在多少数据
	struct Stack st;
	StackInit(&st);
}

四、函数重载

  • 1.函数重载概念
    C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同。

例如:

int Add(int left, int right)
{
	return left+right;
}

double Add(double left,int right)
{
	return left+right;
}	
  • 函数重载与缺省值无关
//构成重载,但不能调用,不知道调用哪一个
void f(int a,int b,int c =1)
{}

void f(int a,int b)
{}
  • 2.名字修饰

    为什么C语言不支持函数重载,C++支持?C++是如何支持的?
    程序运行过程:预处理、编译、汇编、链接
    (1)预处理
    头文件展开、宏替换、去掉注释、条件编译
    生成.i文件
    (2)编译
    检查语法、生成汇编代码
    生成.s文件
    (3)汇编
    把汇编代码转换成二进制机器码
    生成.o文件
    (4)链接
    链接到一起生成可执行文件
    生成a.out文件

  • ①采用C语言编译,调用函数,去符号表找所在地址是根据编译器命名的函数名去寻找函数

  • ②C语言编译器编译后的函数名与代码所写的函数名一致(若多个源文件写相同函数名的函数,则不知道找哪一个函数),C++编译后的函数修饰变成【_Z+函数长度+函数名+类型首字母】,编译器会将函数类型信息添加到修改后的名字中。
    即:C语言没办法支持重载,因为同名函数没办法区分,而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持重载。

  • 3.extern “C”

在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。

例如:

  • tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么它就使用extern “C”来解决。

  • 在C++程序中添加下面一行代码,C程序即可调用此函数。若C++程序要调用此程序,添加同样的代码,C++兼容C。

extern "C" void* tcmallic(ussigned int n);

五、引用

  • 1.概念
    引用:给已存在的变量取了一个别名,引用变量和它共用一块内存空间。

  • 2.结构
    类型& 引用变量名(对象名)= 引用实体
    引用类型必须和引用实体是同种类型的

void TestRef()
{
 int a = 10;
 int& ra = a;//<====定义引用类型
 
 printf("%p\n", &a);
 printf("%p\n", &ra);
}
  • 3.引用特性
    (1)引用在定义时必须初始化
int a = 10;
int& d = a;

(2)一个变量可以有多个引用

	int a = 0;
	int& b = a;
	int& c = a;
	int& d = b;

(3)引用一旦引用一个实体,再不能引用其他实体

void TestRef()
{
 int a = 10;  //引用初始化
 // int& ra; // 该条语句编译时会出错
 int& ra = a;
 int& rra = a;  //多个引用
 printf("%p %p %p\n", &a, &ra, &rra); 
}
  • 4.常引用
int  main()
{
	const int a = 10;  //可读
	//int& ra  =a; ra引用属于权限放大,此处是可读可写
	const int& ra = a; //int& ra  =a;不行

	int b = 10;
	int& rb = b;
	const int& crb = b;  //crb属于权限缩小,所以可以

	int c = 10;
	double d = 1.1;
	d = c;//隐式类型转换

	double& rc = c;  //不行
	const double& rc = c;  //double的临时变量具有常性

	return 0;
}
void TestConstRef()
{
 const int a = 10;
 //int& ra = a; // 该语句编译时会出错,a为常量
 const int& ra = a;
 // int& b = 10; // 该语句编译时会出错,b为常量
 const int& b = 10;
 double d = 12.34;
 //int& rd = d; // 该语句编译时会出错,类型不同
 const int& rd = d; }
  • 5.引用的使用(当数值比较大时,以引用作为函数参数的效率会有提高)
    (1)做参数
void swap(int& left, int& right)
{
	int tmp = left;
	left = right;
	right = tmp;
}

(2)做返回值

//传值返回,返回c的拷贝
int Add(int a, int b)
{
	int c = a + b; 
	return c;
}

int main()
{
	const int& ret = Add(1, 2);
	return 0;
}

//传引用返回,返回传的c的引用
//实际中,出了函数作用域,返回变量就不存在了,不能用引用返回

int& Add(int a, int b)
{
	//int c = a + b;
	//加static就可以用引用返回
	static int c = a + b;  //出了作用域值不销毁
	return c;  //返回c的类型int&
}

int main()
{
	int ret = Add(1, 2);
	Add(5, 7);
	printf("helloworld\n");  //输出为helloworld+随机数
	cout << ret << endl;  //12
	return 0;
}
  • 6.越界问题
int main()
{
	//越界不一定报错
	int a[10] = { 1, 2, 3 };
	//越界读基本不报错,因为编译检查不出来
	cout << a[10] << endl;
	cout << a[11] << endl;

	//越界写,可能会报错
	//越界,系统是抽查
	//a[10] = 0;
	a[11] = 0;
	a[12] = 0;

	return 0;
}

  • 7.引用与指针的区别

(1)概念:引用是一个变量的别名,指针存储的是一个变量地址
(2)引用在定义时必须初始化,指针没有要求
(3)引用在初始化时引用一个实体后,就不能再引用其它实体,而指针可以在任何时候指向任何一个同类型实体
(4)没有NULL引用,但有NULL指针
(5)在sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数
(6)引用自加即引用的实体加1,指针自加即指针向后偏移一个类型的大小
(7)有多级指针,但没有多级引用
(8)访问实体方式不同,指针需要显式解引用,引用是编译器自己处理
(9)引用比指针用起来相对更安全

六、内联函数

  • 1.定义:以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

  • 2.C语言为了避免小函数(代码量少的函数)建立栈帧的消耗,提供宏函数支持,在预处理阶段展开,为什么C++还要提供inline函数呢?(即宏函数的缺点)
    (1)宏函数不支持调试
    (2)宏函数语法复杂,容易出错
    (3)宏函数没有类型安全的检查

注意:

  • 3.宏函数的优点:代码复用性强、 性能比较好

  • 4.C++代替宏函数的方式:
    (1)常量定义 换用const
    (2)函数定义 换用内联函数

  • 5.内联函数的特性:
    (1)inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
    (2)inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
    (3) inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

七、auto

  • 1.auto作为一个新的类型指示符来指示编译器

  • 2.可以自动识别变量的类型(利用typeid().name()函数判断变量类型)

int main()
{
	//int a = 10;
	//auto b = a; //类型声明为auto,可以根据a的类型自动推导b的类型

	int x = 10;
	auto a = &x;  //int*
	auto* b = &x;   //int*
	int& y = x;  //y的类型是int
	auto c = y;   //int
	auto& d = x;  //d的类型是int,但是这里指定了d的引用

	cout << typeid(x).name() << endl;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(y).name() << endl;
	cout << typeid(c).name() << endl;


	return 0;
}
  • 3.利用auto的范围for
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
		cout << array[i] << " ";
	cout << endl;


	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
		array[i] *= 2;

	//范围for  C++新语法遍历,数组都可以
	//自动遍历,依次取出array中的元素,赋值给e,直到结束
	for (auto& e : array)  //auto& 是引用,才能改变array的值  e的名字可以改变,格式不变
	{
		e *= 2;
	}

	for (auto ee : array)
	{
		cout << ee << " ";
	}
	cout << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值