C++ : 入门

本文介绍了C++中的一些关键特性,如命名空间用于解决命名冲突,缺省参数简化函数调用,函数重载实现多态,引用作为别名提高效率,auto关键字自动类型推导,以及基于范围的for循环和内联函数的概念与应用。此外,还讨论了nullptr作为指针空值的新关键字。
摘要由CSDN通过智能技术生成


前言

这篇博客主要介绍C++是如何弥补C语言在某些地方的不足。
接下来一起看看吧。


一、命名空间

1.0 C语言中的问题

#include <stdio.h>
#include<stdlib.h>

int rand = 10;
int main()
{
	//rand在stdlib.h是一个函数,上面又对它定义了一次。
	printf("%d ", rand);
	return 0;
}

问题:

  1. 程序员定义的与C语言标准库定义的变量名、函数名等冲突
  2. 程序员之间定义的变量名、函数名等冲突。

1.1 命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
举个例子:

//example是命名空间的名字
namespace example
{
	int rand=5;
	int printf=10;

	//也可以定义函数
	int Swap(int* p1, int* p2)
	{
		int tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
	}
}

1.2 命名空间的特点

1.2.1 可以嵌套定义

namespace A
{
	int a = 1;
	int b = 2;
	namespace B
	{
		int a = 2;
		int b = 1;
	}
}

1.2.2 在同一个工程中的相同名称的命名空间会合并

//test.h
namespace A
{
	int i = 10;
	double d = 5.20;
}


//Test.cpp中
#include "test.h"
namespace A
{
	int a = 1;
	int b = 2;
	namespace B
	{
		int a = 2;
		int b = 1;
	}
}

int main()
{
	
	printf("d\n", A::i);//i在Test.cpp中没有定义,但是能读取说明test.h和Test.cpp中同名的命名空间已经合并了。
	printf("%d\n", A::a); // "::"作用域限定符
	printf("%d\n", A::B::a);
	return 0;
}

1.3 命名空间的使用

1.3.1 方法一:命名空间+域作用限定符

namespace A
{
	int a = 1;
	int b = 2;

	int Add(int x, int y)
	{
		return x + y;
	}
}

int main()
{
	printf("%d\n", A::a); // "::"作用域限定符

	printf("%d\n", A::Add(5 ,56));
	return 0;
}

1.3.2 方法二:使用using namespace 命名空间名称 引入

namespace A
{
	int a = 1;
	int b = 2;

	int Add(int x, int y)
	{
		return x + y;
	}
}

//展开全部命名空间:using namespace 命名空间的名称
using namespace A;

int main()
{
	printf("%d\n", a); // "::"作用域限定符

	printf("%d\n", Add(5 ,56));
	return 0;
}

1.3.2 方法三:使用using将命名空间中某个成员引入

namespace A
{
	int a = 1;
	int b = 2;

	int Add(int x, int y)
	{
		return x + y;
	}
}

using A::Add;

int main()
{
	//由于a没有展开,所以a不能被读取
	printf("%d\n", a); // "::"作用域限定符

	printf("%d\n", Add(5 ,56));
	return 0;
}

二、缺省参数

2.1 概念

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
例:

#include <iostream>
using namespace std;

void Fun1(int x = 10)
{

	cout << x << endl;
}


int main()
{
	Fun1();//没有给实参
	Fun1(5);

	return 0;
}

运行结果:
在这里插入图片描述

2.2 缺省参数的分类

2.2.1 全缺省

//函数的全部参数都是缺省参数
void Fun(int x = 10, int y = 15, int z = 20)
{
	cout << x << " " << y << " " << z << endl;
}

int main()
{
	//Fun(8, ,10) 这样填参数是会报错的。
	Fun();
	Fun(55, 11, 22);
	return 0;
}

2.2.2 半缺省

//函数的部分参数是缺省参数(从右往左依次缺省)
void Fun(int a, int b, int c = 5)
{
	cout << a << " " << b << " " << c << endl;
}

int main()
{
	Fun(5, 20);
	Fun(5, 20, 10);
	return 0;
}

2.3 特点

  1. 半缺省一定要从右往左依次为缺省参数,不能间隔。
  2. 缺省参数的声明和定义不能都为缺省参数,一般是声明给缺省参数就可以了。
  3. 缺省值一般是常量和全局变量。

2.4 应用场景

当我们要开一个动态数组时,但不知道它要开辟多大的空间时。学了缺省参数后我们就可以这样写了。

//确定要多大就写实参,不确定就不写实参。
void ListInit(int* a, int capicity = 4)
{
	int* a = (int*)malloc(sizeof(int) * capicity);
	if (a == NULL)
	{
		//...
	}

	//...
}

三、 函数重载

3.1 概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

3.2 类型

3.2.1 参数个数不同

//1.参数个数不同
int Add(int x, int y)
{
	return x + y;
}

int Add(int x, int y, int z)
{
	return x + y + z;
}

int main()
{

	cout << Add(1, 2) << endl;
	cout << Add(1, 2, 3) << endl;
	return 0;
}

3.2.2 参数类型不同

//2.参数类型不同
int Add(int x, int y)
{
	return x + y;
}

float Add(float x, float y)
{
	return x + y;
}

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.2f, 2.1f) << endl;//数值后加f说明它的类型是float。
	return 0;
}

3.2.3 参数类型的顺序不同

//3.参数类型的顺序不同
void Fun(int i, char c)
{
	cout << "Fun i c" << endl;
}

void Fun(char c, int i)
{
	cout << "Fun c i " << endl;

}

int main()
{
	Fun(1, 'c');
	Fun('c', 1);
	return 0;
}

3.3 为什么C语言不支持函数重载而C++支持

主要就是它们两的函数名修饰规则不同决定的。
因为C语言同名函数没办法区分。而C++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

四、引用

4.1 概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

例:

//类型& 引用变量名=引用实体
int main()
{
	int a = 10;
	int& b = a; //b是a的应用,b的改变会影响a。a也会影响b。

	cout << b << endl;
	b++;
	cout << a << endl;
	cout << b << endl;

	return 0;
}

要注意的是:引用类型要与引用实体的类型相同。

4.2 引用特性

  1. 引用必须初始化。
  2. 一个变量可以有多个引用。
  3. 引用一旦初始化是不能改变引用实体的。

4.3 使用场景

4.3.1 作参数(输出性参数,提高效率)

//做参数
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 10;
	int b = 5;
	cout << a << " " << b << endl;
	Swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

4.3.2 作返回值

//引用作返回值
int& Fun()
{
	int a = 1;
	a++;
	return a;
}

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	//函数Fun返回了临时变量,临时变量的值可以是随机值。
	int& a = Fun();
	Add(4, 5);

	cout << a << endl;
	return 0;
}

特点:

  1. 它相对的减少了拷贝,也提高了效率。
  2. 它可以读写返回值,但这也带有一些风险。

4.4 引用小总结

  1. 基本任何场景下,我们都可以用引用传参。
  2. 我们要谨慎用引用作返回值。如果对象出来函数作用域被销毁了,就不能使用引用返回对象了。如果对象出来函数作用域没有被销毁,就可以使用引用返回。
  3. 在语法层面:不会开空间,只是对变量起别名。 在底层汇编指令实现的角度:是类似指针的方式实现的。

4.5 常引用

只有引用时才考虑权限的问题。
例1:

int main()
{
	int a = 10;
	int& b = a;
	//这里权限被放大了,权限只能被平移和缩小
	//const int d = 10;
	//int& e = d; 所以这是错误的写法

	//权限被缩小了
	const int& c = a;
	b++;
	//c++; //c不能加加了,

	//这里是这里权限的平移
	const int i = 5;
	const int& z = i;

	return 0;
}

例2:

int main()
{
	int a = 10;
	int& b = a;

	//这里权限被放大了,权限只能被平移和缩小
	//const int d = 10;
	//int& e = d; 所以这是错误的写法

	//权限被缩小了
	const int& c = a;
	b++;
	//c++; c不能加加了,

	//这里是这里权限的平移
	const int i = 5;
	const int& z = i;

	return 0;
}

例3:

int funA()
{
	static int s_i = 10;
	s_i++;
	return s_i;
}

int& funB()
{
	static int s_i = 10;
	s_i++;
	return s_i;
}

int main()
{
	//传值返回,赋值时会产生一个临时变量辅助赋值。但临时变量有常性。
	int& a = funA();//权限的放大,会报错。

	int& b = funB();//权限的平移。
	const int& c = funB();//权限的缩小。
	return 0;
}

总结:

  1. 当传值返回或发生隐形类型转换时,会产生一个临时变量辅助赋值。但它具有常性(类似被const修饰)。
  2. 只有在引用时,才会有权限的问题。
  3. 权限只能被平移和缩小,一定不能被放大。

4.6 引用和指针的区别

  1. 语法层面:引用是不会开空间的,它对对象起别名。而指针会开空间存放的是对象的地址。
  2. 引用必须初始化。而指针没有要求。
  3. 没有多级引用。而指针有多级指针。
  4. 引用没有NULL。而指针可以是NULL。
  5. 引用一旦初始化后就不能改对象了。而指针可以改内容。
  6. 引用比指针更安全。指针有野指针或NULL指针访问的问题。
  7. 引用加1是对实体加1,而指针+1是向后移动一个指针类型的大小。
  8. sizeof()求引用的大小是引用类型的大小。而sizeof求指针大小:在32位机器上是4个字节,在64位机器上是8个字节。

五、关键字:auto

5.1 使用场景

使用auto修饰的变量,是具有自动存储器的局部变量。
例:

int main()
{
	//auto具有自动推导类型的功能。
	//1.能和指针、引用结合使用。
	int a = 10;
	auto* pa = &a;
	auto& aa = a;
	auto d = 1 + 1.2;
	
	//打印变量类型
	cout << typeid(pa).name() << endl;
	cout << typeid(aa).name() << endl;
	cout << typeid(d).name() << endl;

	auto i = 4, j = 5;
	//auto ii = 4, dd = 4.6;    报错//一行不能推导两个类型

	return 0;
}

注意:
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

5.2 不能推导的场景

  1. 不能作函数的参数。
  2. 不能直接用来声明数组。

六、基于范围的for循环

6.1 概念

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

6.2 使用条件

  1. for循环迭代的范围必须是确定的。
int main()
{
	int arr[] = { 2, 4, 6, 8 };
	
	//适用于数组
	//依次将数组中数据赋值给e
	//自动迭代,自动判断介绍。
	for (auto x : arr)
	{
		cout << x <<" ";
	}
	//修改数组中的数据
	for (auto& e : arr)
	{
		e *= 2;
	}
	cout << endl;
	for (auto x : arr)
	{
		cout << x << " ";
	}
	return 0;
}

七、内联函数

7.1 概念

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

7.2 特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

例2.1:

//函数规模较小,会成为内联函数。
inline int Add(int x, int y)
{
	return x + y;
}

int main()
{

	//多次调用
	for (int i = 0; i < 10000; ++i)
	{
		cout << Add(i, i + 1) << endl;
	}
	return 0;
}



例2.2

//inline 只是对编译器的一个建议,函数是否为内联函数取决与编译器,如果函数体过长、没有频繁调用、是递归函数就不会成为内联函数。
inline void Func()
{
	cout << "hhhhhhhhhhh" << endl;
	cout << "hhhhhhhhhhh" << endl;
	cout << "hhhhhhhhhhh" << endl;
	cout << "hhhhhhhhhhh" << endl;
	cout << "hhhhhhhhhhh" << endl;
	cout << "hhhhhhhhhhh" << endl;

}


int main()
{
	Func();

	return 0;
}

在这里插入图片描述

  1. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

例3.1

//test.h中代码
#include <iostream>
#include <assert.h>
using namespace std;
inline void Fun();

//test_2.cpp
#include "test.h"

inline void Fun()
{
	cout << "Fun" << endl;
}

//test.cpp
int main()
{
	Fun();
	return 0;
}

报错原因:
在这里插入图片描述

八、指针空值nullptr

在C语言中,NULL本质上是宏定义的。

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

在一些特定的场景,例如:

void Func(int)
{
	cout << "FuncA" << endl;
}

void Func(int* )
{
	cout << "FuncB" << endl;
}

int main()
{
	Func(0);
	//本来是想调用第二个函数,但调用了第一个函数
	Func(NULL);
	Func((int*)NULL);
	return 0;
}

在这里插入图片描述

8.1 nullptr关键字

  1. nullptr是C++11作为新关键字引入的,用nullptr表示指针空值时,不需要包含头文件。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr

例:

int main()
{
	int a = 10;
	int* pa = nullptr;
	pa = &a;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值