【C++学习】C++入门(1)


C++是在C的基础上容纳进去了类和对象的编程思想,并添加了许多有用的库,以及编程范式等。

一.C++关键字

C++ 有63个关键字,C有32个关键字。关键字的功能不在一一详讲,用的时候在进行细讲。
在这里插入图片描述

二.命名空间

C++/C 中,变量、函数和类的名大量存在,他们的名字会发生重复,为了能让重复的名同时存在创作出了命名空间。

举个会报错的例子:

#include<iostream>
#include<stdlib.h>
int malloc = 1;
int main()
{
	printf("%d",malloc );
	return 0;
}
//error C2365: “malloc”: 重定义;以前的定义是“函数”

1.命名空间的定义

用到 namespace 关键字,后面跟命名空间的名字,接着一个{}

namespace test
{
	//命名空间内可以定义变量
	int malloc = 1;
}

int main()
{
	//使用命名空间test的变量
	printf("%d",test:: malloc);
	return 0;
}
namespace test
{
	int malloc = 1;//命名空间内可以定义变量
	//命名空间内可以定义结构体
	struct test
	{
		int* arr;
		int size;
	};
	//命名空间内可以定义函数,等等
	void Swap(int* a, int* b)
	{
		int tmp = *a;
		*a = *b;
		*b = tmp;
	}
}

2.命名空间的嵌套

命名空间中可以嵌套命名函数

namespace test_1
{
	int i_1 = 1;
	char arr_1[] = { 1,2,3 };

	namespace test_2
	{
		int i_2 = 2;
		char arr_2[] = { 4,5,6 };
	}
}

3.命名空间的合并

在一个工程中,允许存在多个相同名称的命名空间,编译器编译时会把相同名称的命名空间合并成一个
如:前两个test命名空间会合并成一个(编译器合成)

4.命名空间的使用

有三种方式:

  1. 命名空间名称+作用域限定符
namespace test_3
{
	int i_3 = 1;
}

int main()
{
    //命名空间名称+成员
	printf("%d ", test_3::i_3);
	return 0;
}
  1. 使用 using 将某个命名空间的成员引入
namespace test_4
{
	int arr[] = { 1,2,3,4,5 };
	int i = 0;
}
//using+作用域限定符
using test_4::arr;

int main()
{
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
  1. 使用 using 将某个命名空间的成员全部引入
//把std命名空间全部展开
using namespace std;

int main()
{
	//cout和endl 是std命名空间的关键字
	cout << "Hello World" << endl;
	return 0;
}

三.C++输入&输出

  1. 1.使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
  2. cout和cin是全局的流对象,end是特殊的C++符号,表示换行输出,他们都包含在包含头文件中。
  3. <<是流插入运算符, >>是流提取运算符。
  4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样, 需要手动控制格式。C++的输入输出可以自动识别变量类型。
  5. 实际上cout和cin分别是ostream和istream类型的对象, >>和<<也涉及运算符重载等知识,这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有一个个章节更深入的学习流用法及原理。

注意:早期标准库将所有功能在全局域中实现,声明在h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,此推荐使用+std的方式。此推荐使用+std的方式。

#include<iostream>
//std 是C++标准库的命名空间,标准库的定义都在这个命名空间中
using namespace std;

int main()
{
	int a ;
	float b ;
	char c;
	cin >> a;
	cin >> b >> c;

	cout << "Hello World" << endl;

	cout << "a:" << a << "  b:" << b << "  c:" << c << endl;
	return 0;
}

四.缺省参数

1.概念

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

2.全缺省参数

调用的函数不传一个参数

//打印出 10 20 30
void Function(int a = 10, int b = 20, int c = 30)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}

int main()
{
	Function();//不传参数
	return 0;
}

3.半缺省参数

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现
  3. 缺省值必须是常量或全局变量
//打印出 1 2 30
void Function(int a = 10, int b = 20, int c = 30)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}

int main()
{
	Function(1,2);//不传参数
	return 0;
}

五.函数重载

C语言中不允许重复命名函数,而C++在这里进行可改进

1.函数重载的概念

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

2.函数重载的用法

//函数名相同,形参类型不同
int Add(int x, int y)
{
	return x + y;
}

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

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

int main()
{
	int n_1 = 1;
	int n_2 = 2;
	float n_3 = 1.0;
	float n_4 = 2.0;
	Add(n_1, n_2);//int 和int 类型
	Add(n_3, n_4);//float 和float类型
	Add(n_3, n_1);//float 和 int 类型
	return 0;
}

3.函数重载的原理

首先我们要了解一下一个程序运行起来要经历的四个阶段:
预处理 -> 编译 -> 汇编 -> 链接
C语言在汇编阶段中不会处理函数名,而C++在汇编阶段中会把函数名处理成其他形式
下面是在Linux环境下函数被处理的形式:
_Z + 名字符个数 + 函数名 + 类型名的首字母

//在C语言中:Add      在C++中是:_Z3Addii  
int Add(int x, int y)
{
	return x + y;
}
//在C语言中:Add      在C++中是:_Z3Addff
float Add(float x, float y)
{
	return x + y;
}
//_Z3Addfi
float Add(float x, int y)
{
	return x + y;
}

通过这样的处理C语言就无法支持函数重载,因为同名函数没有区分,编译器无法识别。而C++的同名函数进行了区分,编译器可以识别,可以支持函数重载。

六.引用

从语法语法角度来说 不开空间,实际上 开空间。

1.引用的概念

引用不是新创建一个变量,而是给已经存在的变量起一个别名
比如:周星驰 是 大名, 在电影中叫他 星星,粉丝喜欢叫他 星爷,无论叫哪一个,都指的是独一无二的他
在这里插入图片描述

2.引用类型的定义

用符号 & .

  • 引用类型必须和引用实体是同类型
  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,就不能在引用其他实体
    int& i;//这个语句存在时编译会出错
    
    int n = 0;
    //引用 n,给n取一个别名 m 和 nm
	int& m = n;
	int& nm = n;

	typedef struct Node
	{
		int a;
		float b;
	}Node;
	
	Node test_1;
	//引用 test_1,给test_1取一个别名test_2
	Node& test_2 = test_1;

3 .引用的特殊使用场景

(1)做参数

//交换值不用再用指针,可以使用引用
int Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 1;
	int b = 2;
	Swap(a, b);
	return 0;
}

(2)做返回值

先给大家讲返回值的返回原理
在这里插入图片描述
给大家看一个错误代码:

int& Double_function(int n)
{
	int Double = n * 2;
	return Double;
}

int main()
{
	int n = 2;
	int ret = Double_function(n);
	return 0;
}

错误的原因:
在这里插入图片描述
正确代码:

int& Double_Function(int n)
{
	static int Double = n * 2;//Double储存在静态区
	return Double;
}

int main()
{
	int n = 2;
	int ret = Double_Function(n);
	return 0;
}

(3).引用和指针权限大小问题

权限只能缩小和保持,但权限不能放大
权限放大:

const int n = 0;
	//权限放大
	int& nn = n;

	const int* p = NULL;
	//权限放大
	int* pp = p;

权限缩小和保持:

    //权限缩小
	int n = 0;
	const int& nn = n;

	const int* p = NULL;
	const int* pp = p;

	//权限保持
	const int n = 0;
	const int& nn = n;

	const int* p = NULL;
	const int* pp = p;

3.引用和指针的区别

在语法概念上,引用是一个别名,不占用空间,和引用实体共用一个空间。但是,引用在底层实现上是有空间的,因为引用是按指针的方式实现的

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,而指针没有要求。
  3. 引用在初始化时引用一个实体后不能再引用其他实体,而指针没要求
  4. 没有NULL引用,而有NULL指针
  5. 有多级指针,但是没有多级引用
  6. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  7. 引用比指针使用起来相对更安全

七.内联函数

以 inline 修饰的函数叫做内联函数,编译时c++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率(类似c语言的定义宏define)

一般来说,内联机制用于优化规模较小,流程直接,频繁调用的函数,很多编译器都不支持内联函数递归。而且当一个内联函数的的函数太多时就不在实行内敛机制(在调用内联函数的地方展开)

内联函数不建议声明和定义分开,因为inline没有函数地址,会导致链接错误。直接在声明处定义函数(头文件处定义)

1.宏定义的缺点(define)

  • 不方便调试宏。(因为预编译阶段进行了替换)
  • 导致代码可读性差,可维护性差,容易误用。
  • 没有类型安全的检查。

C++中可代替宏的技术:

  • 常量定义用 const enum。
  • 短小函数用内联函数定义。
#define Add(x,y) ((x)+(y))

int main()
{
	int a = 1;
	int b = 2;
	int c = Add(a, b);
	return 0;
}

2.内联函数的定义

//用inline修饰
inline int Add(int x, int y)
{
	int c = x + y;
	return 0;
}

int main()
{
	int a = 1;
	int b = 2;
	int c = Add(a, b);
	return 0;
}

八.auto关键字(C++11)

1.为什么有auto关键字

随着不断深入的学习,程序复杂度增加,类型名越来越长,体现在:

  • 类型难于拼写
  • 含义不不明确容易导致出错

auto关键字的优点:

  • 增强代码的复用性
  • 提高性能

举例:

int main()
{
	std::map<std::string, std::string>  m{{ "学号","123" }, {"姓名", "张三"}, { "性别", "男"}
};
	//std::map<std::string, std::string> ::iterator 是迭代器类型
    std::map<std::string, std::string> ::iterator it = m.begin();
	//auto 自动识别类型
	auto it = m.begin();

	return 0;
}

2.回顾 typedef

C语言也有简化类型名的关键字 typedef,但是,typedef 也有缺点

给大家看个代码:

typedef int* ptr;

int main()
{
	const ptr  p1;//错误还是正确?
	const ptr*  p2;//错误还是正确?

	return 0;
}

代码讲解

typedef int* ptr;

int main()
{
	// 实际是 char* const ptr ,const 值必须初始化 
	const ptr p1;//错
	// 实际是 const char** ptr ,是二级指针
	const ptr*  p2;//正确

	return 0;
}

在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义。

2.auto简介和使用

简介:
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的是没有人去一直使用它
C++11中,标准委员会赋予了auto全新的含义即: auto不再是一一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

使用:

int ret()
{
	return 9;
}

int main()
{
	int n = 66;
	auto n_1 = n;// int n_3 = 66;
	auto n_2 = 'n';//char n_2 = "66";
	auto n_3 = ret();// int n_3 = 9;
	cout << typeid(n_1).name() << endl;
	cout << typeid(n_2).name() << endl;
	cout << typeid(n_3).name() << endl;
    //auto i;  无法通过编译,定义 auto 变量必须初始化
	return 0;
}

注意:
的实际类型。因此auto并非是-种"类型”的声明,而是一个类型声明时的”占位符",编译器在编的实际类型。因此auto并非是-种"类型”的声明,而是一个类型声明时的”占位符",编译器在编译期会将auto替换为变量实际的类型。

3.auto的规范使用

(1)auto和指针结合

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须 加&

int main()
{
	int n = 1;
	//auto 是int
	auto& n_1 = n;
	//auto 是int*
	auto n_2 = &n;
	//auto 是int*
	auto* n_3 = &n; 
	cout << typeid(n_1).name() << endl;
	cout << typeid(n_2).name() << endl;
	cout << typeid(n_3).name() << endl;

	return 0;
}

(2)在同一行内定义多个变量

int main()
{
	int a = 1;
	int b = 2;
	char y = 'x';
	//正确
	auto a_1 = a, b_1 = b;
	//错误
	auto a_2 = a, y_2 = y;

	//当在同一行声明多个变量时,这些变量必须是相同的类型,
	//否则编译器将会报错,因为编译器实际只对第一个类型进行推导,
	//然后用推导出来的类型定义其他变量。
	return 0;
}

4.auto不能推导的场景

(1)auto不能作为函数的参数

错误代码:

void Function(auto n)
{

}

int main()
{
	int n = 0;
	Function(n);
	return 0;
}

(2)不能直接用来声明数组

演示:

int main()
{
	int arr_1[] = { 1,2,3,4 };
	//错误代码
	auto arr_2[] = { 1,2,3,4 };
	return 0;
}

九.基于范围的for循环(C++11)

1.范围 for 的语法

在C++98中要遍历一个数组可以用以下方法:

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	//法一
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		cout << arr[i] << endl;
	}
	//法二
	for (int* p = arr; p < arr + sizeof(arr) / sizeof(int); p++)
	{
		cout << *p << endl;
	}
	return 0;
}

C++11中引入了基于范围的for循环。for循环后的括号由冒号”:”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
代码演示:

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	//只是依次把arr的值赋值给i
	//范围for常常和auto搭配使用
	for (auto i : arr)
	{
		cout << i << endl;
	}

	return 0;
}

2.范围 for 使用条件

for循环迭代的范围必须是确定的

  • 对于数组而言,就是数组中第一个元素和最后一个元素的范围;
  • 对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。

注意:以下代码就有问题,因为for的范围不确定

void Function(int* arr)
{
	//这里的arr不能代表整个arr数组,
	//数组传参传的只是数组首元素地址
	for (auto i : arr)
	{
		cout << i << endl;
	}
}

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	Function(arr);
	return 0;
}

十.指针空值(C++11)

1.c++98中的空指针

在良好的C/C++编程环境中,声明一个变量时好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

int main()
{
	int* p1 = NULL;
	int* p2 = 0;
	return 0;
}

NULL实际是一个宏,在传统的头文件(stddef .h )可以看到如下代码:

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

可以看到,NULL可被定义为字面常量0,或被定义成无指针类型void*的常量
不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,
比如:

void Function(int)
{
	cout << "Function(int)" << endl;
}

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

int main()
{
	Function(0);
	Function(NULL);
	Function((int*)NULL);
	return 0;
}
//打印结果
//Function(int)
//Function(int)
//Function(int*)

程序本意是想用NULL调用void Function(int*),但是NULL被定义为0,调用的是void Function(int)。
在C++98中,字面常量0既可以是一个整形数字,也可是无类型的指针void*…但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void*)0。

注意:

  • 在使用nullptr表示指针空值时,不需要用头文件。因为nullptr是C++11作为关键之引入的。
  • 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  • 为了提高代码的健壮性,在后续表示指针空值时建议 好使用nullptr。
  • 39
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值