C++——入门

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

C++是在c语言的基础上,容纳进去了面向对像编程思想,并增加许多有用的库,以及编程范式。
补充c语言的一些不足的地方,以及对不合理的地方进行优化。
为后续类和对象学习打基础。


一、c++关建字

C++总计63个关键字,C语言32个关键字。

这里只是简单的展示,后面通过继续学习了解这些关键字的含义。

二、命名空间

1.命名空间定义

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都有在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
代码如下(示例):

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

//命名空间定义
namespace xdt		//在stdilb.h中包含了rand函数,如果在定义rand的话就会产生冲突,
					//使用自己的一个独立的命名空间,就可以你避免冲突
{
	int  rand = 1;
}
//命名空间还可以嵌套使用
namespace xdt_3
{
	int x = 3;
	int y = 6;
	namespace xdt_4
	{
		int x = 10;
		int y = 100;
	}
}

2.命名空间的使用

命名空间的三种使用方法
1、加域作用限定符
2、使用using将空间中的某个成员引入
3、使用using namespacem命名空间引入
代码如下(示例):

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

namespace xdt_2
{
	int rand = 2;
}
namespace xdt_3
{
	int x = 3;
	int y = 6;
	namespace xdt_4
	{
		int x = 10;
		int y = 100;
	}
}

namespace xdt_5
{
	int a = 1000;
}

int main()
{
	printf("%d\n", xdt:: rand);
	printf("%d\n", xdt_3::x);
	printf("%d\n", xdt_3::xdt_4::x);	
	printf("%d\n", xdt_3::y);
	printf("%d\n", xdt_3::xdt_4::y);
	printf("%d\n", y);
	printf("%d\n", a);

	return 0;
}

三、输入和输出

#include <iostream>
using namespace std;

int main()
{
	int x=0;
	cin >> x;
	cout << x << endl;
	cout << "Hello C++" << endl;
	return 0;
}

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


四、缺省参数

概念

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

void fun(int a=10)
{
	cout << a << endl;
}

int main()
{
	fun();		//没有传参时,使用函数的默认参数
	fun(6);		//传参时,使用指定的参数
}

分类

全缺省
void Func(int a = 10, int b = 20, int c = 30)
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl << endl;
}

int main()
{
    Func();
    Func(1);
    Func(1, 2);
    Func(1, 2, 3);
	  return 0;
}

 半缺省(缺省值只能从右往左给,必须是连续给)
1、半缺省参数必须从右往左依次给出来,不能间隔空着
2、缺省参数不能在函数声明和定义中同时出现


void Func(int a = 10, int b, int c = 30)      //错误示范
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl << endl;
}

void Func(int a, int b =10, int c = 30)
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl << endl;
}

五、函数重载

概念

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

int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;

	return left + right;
}

double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;

	return left + right;
}

// 构成重载,不传参数的时候调用存在二义性
void f()
{
	cout << "f()" << endl;
}

//void f(int a = 0)	//0的话和无参一样
//{
//	cout << "f(int a)" << endl;
//}

void f(int a)
{
	cout << "f(int a)" << endl;
}

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 1.2) << endl;
	f();
	f(10);
}

c语言之所以不能用函数重载,是因为C在链接函数地址时,就用函数名去找。
而C++在链接函数时,在Linux环境下,函数名的修饰规则是这样的:
_Z 函数名字符个数 函数名 参数首字母 如: _Z3Addii _Z3Adddd
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办
法区分。


六、引用

概念

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

特性

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

//1、引用在定义时必须被初始化
//2、一个变量可以有多个引用
//3、引用一旦引用一个实体,就不能再引用其他实体了
int main()
{
	int a = 10;
	int A = 100;
	int& b = a;
	int& c = a;

	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	
	b = A;		//这里是把A的值赋给b,而不是b再引用了A
	cout << b << endl;
}

常引用

void TestConstRef()
{
	//权限不能放大
	const int a = 10;
	//int& ra = a;		// 该语句编译时会出错,a为常量
	const int& ra = a;

	//权限可以缩小
	double d = 12.34;
	//int& rd = d; // 该语句编译时会出错,类型不同
	const int& rd = d;
}

使用场景

1、做参数

void func(int& a)
{
	a++;
}

int main()
{
	int x = 10;
	func(x);
	cout << x << endl;
	return 0;
}

2、做返回值

如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

//函数返回后栈帧会销毁,返回对象就销毁了,不能用引用返回,否则结果是不确定
int& fun(int n)
{
	n++;
	cout << &n << endl;
	return n;
}

int main()
{
	int& ret = fun(100);
	cout << ret << endl;
	cout << &ret << endl;
}
int& Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	cout << "Add(1, 2) is :" << ret << endl;
	cout << &ret << endl;

	Add(3, 4);

	cout << "Add(1, 2) is :" << ret << endl;
	cout << &ret << endl;
	return 0;
}

引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
在这里插入图片描述

指针和引用的不同点

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

七、内联函数

概念

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

//内联函数 inline
//用来替代宏的不足
//宏的缺点:
// 1、容易出错,语法细节多
// 2、不能调试
// 3、没有类型安全的检查

 enum const inline 替代宏
 enum const -> 宏常量
 inline ->宏函数

// 可以调试
// 效率高,会展开
// 好写,语法简单

#define Add(x,y) ((x)+(y))
inline int func(int x, int y)
{
	return x + y;
}

inline int func_2(int x, int y)
{
	int c = x + y;
	int c1 = x + y;
	int c2 = x + y;
	int c3 = x + y;
	int c4 = x + y;
	int c5 = x + y;
	int c6 = x + y;
	int c7 = x + y;
	int c8 = x + y;
	int c9 = x + y * c8;
	int c10 = x + y;
	int c11 = x + y;
	return c1 + c10 - c9;
}
int main()
{
	//这里同样都是用展开的方式,并没有开辟栈桢
	int ret_1 = Add(6, 1);
	int ret_2 = func(1, 2);
	int ret_3 = func_2(5, 6);
	cout << ret_1 << endl;
	cout << ret_2 << endl;
	cout << ret_3 << endl;
	return 0;
}

/*inline只是向编译器发出是否展开的请求,如果代码行数过多,
编译器可以选择不展开,选择和函数一样的方式进行*/

//内联函数的定义和声明不能分开写!!!

八、auto关键字

auto修饰的变量,是具有自动存储器的局部变量。
用来解决类型声明过长的一些场景,在后续中会体现。

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


int main()
{
	int a = 10;
	double A = 3.14;
	auto b = a;
	auto c = &a;		//指针可以加*也可以不加
	auto& d = a;		//引用的话一定要加&

	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;

	//在同一行定义多个变量时,类型要相同,因为编译器是对以第一个类型进行推导的
	//auto f = a, F = A;
	//auto g = 1, G = 3.14;

}

auto不能推导的场景

1、不能做函数的参数
2、不能直接用来声明数组


九、基于范围的for循环

//范围for的语法
//使用条件
//1、for循环迭代的范围必须是确定的(对于数组而言,就是数组中第一个元素和最后一个元素的范围)
//2、迭代的对象要实现++和==的操作。(后面讲)

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	for (auto e : arr)
	{
		e *= 2;		
		cout << e << " ";
	}

	cout << endl;
	//只是把数组里的值给e,所以e修改并不会修改到数组里面的值
	for (auto e : arr)
	{
		cout << e << " ";
	}
	return 0;
}

十、指针nullptr

//指针空值nullptr

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

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

int main()
{
	//int* ptr = NULL;
	int* ptr = nullptr;

	f(0);
	//f(NULL);		//这里会调用到第一个函数,因为NULL的定义是0,真正的定义应该是((void*)0)
	f(nullptr);		//后续用nullptr这个就行

	return 0;
}

总结

以上是C++入门的全部内容。