C++入门基础

1.第一个C++程序

#include <stdio.h>
int main()
{
	printf("Hello world\n");
	return 0;
}

这样的代码在.cpp文件中运行也是没有问题的,这说明C++兼容C语言,那么C语言中的绝大部分语法那一套东西全可以搬过来直接用.

不过,既然要学C++,下面是C++的"版本".

#include <iostream>
using namespace std;
int main()
{
	cout << Hello world << endl;
	return 0;
}

2. 命名空间

2.1命名空间的意义

在C语言以及C++中,变量,函数,还有C++中的类都是大量存在,它们的名字大都存在于全局作用域中,很容易导致冲突.
不说给各个变量或函数命名时可能导致的冲突,这里有一个头文件stdlib.h,其中"rand"为一个函数名,如果包含了这个头文件(即使不是为了使用rand函数),在后续代码中又定义了rand变量,将会导致重定义报错!

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
printf("%d\n", rand);
return 0;
}

在这里插入图片描述
所以C++中给出了命名空间namespace的解决办法,简单的理解就是,把自己要用的名字放在一个作用域中,用的时候告诉编译器用的是谁的名字,避免了名字冲突.

2.2namespace的定义

语法:

namespace jaychou
{
	char jay = abc;
	int  a = 10;
	float b = 100.1;
	void print()
	{
		printf("hello\n");
	}
	class myClass
	{
	}
	namespace _jaychou
	{
	}
}
  1. 命名空间就是关键字"namespace" 跟 命名空间的名字(自定义),然后接一对大括号{},这个大括号就是作用域的范围.在这个命名空间中,可以定义变量,函数,,也可以嵌套定义命名空间.
  2. 命名空间的本质就是定义了一个作用域,这个作用域和全局独立,不同的命名空间里可以定义同名的变量,函数…
  3. C++中有函数局部域,全局域,命名空间域,类域. 命名空间域和类域和前两者不同之处是不影响变量生命周期和编译查找逻辑.
  4. namespace命名空间只能定义在全局.
  5. 项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。
  6. C++标准库都放在⼀个叫std(standard)的命名空间中。
namespace one
{
	int a = 100;
}
namespace two
{
	int a = 10;
}
int main()
{
	printf("%d\n",one::a);
	printf("%d\n",two::a);
}

在这里插入图片描述

2.3命名空间的使用

namespace one
{
	int a = 100;
}
int main()
{
	printf("%d",a);
}

在这里插入图片描述
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找,所以程序会编译报错.
要使用命名空间中定义的变量/函数/类/命名空间,有三种方法:

  • 指定命名空间访问,项目中推荐这种方式.
  • using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式.
  • 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便可以使用.

1.指定命名空间访问,语法是命名空间的名字::被访问名字.(在用到的地方这么写就行)

namespace myname
{
	int a = 0;
}
int main()
{
	printf("%d",myname::a);
}

2.using将命名空间中某个成员展开,语法是using 命名空间的名字::被访问的名字(不强制在全局域,在用到的作用域里有这句话就行)

namespace myname
{
	int a = 0;
}
//using myname::a # 全局
int main()
{
	using myname::a;//局部
	printf("%d",a);
}

3.展开命名空间中全部成员,语法是using namespace 命名空间的名字(同样在全局域,局部域都可以).

namespace myname
{
	int a = 0;
}
//using namespace myname # 全局
int main()
{
	using namespace myname;//局部
	printf("%d",a);
}

3.C++的输入和输出

C++中引入了新的输入和输出的方法.

  • 是 Input Output Stream 的缩写,是标准的输入,输出流库,定义了标准的输入,输出对象.
  • std::cin 是 istream 类的对象,它主要面向窄字符的标准输入流.
  • std::cout 是 ostream 类的对象,它主要面向窄字符的标准输出流.
  • std::endl 是⼀个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区.
  • <<是流插⼊运算符,>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移).
  • 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型(本质是函数重载),其实最重要的是C++的流能更好的支持自定义类型对象的输入输出.
  • cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们.
  • ⼀般日常练习中可以直接using namespace std,实际项目开发中不建议using namespace std。
  • 这里没有包含<stdio.h>,也可以使用printf和scanf,在包含间接包含了.vs系列编译器是这样的,其他编译器可能会报错.
#include <iostream>
using namespace std;
int main()
{
	int a = 0;
	double b = 0.1;
	char c = 'x';
	
	cout << a << " " << b << " " << c << endl;
	std::cout << a << " " << b << " " << c << std::endl;
	
	scanf("%d%lf", &a, &b);
	printf("%d %lf\n", a, b);
	// 可以⾃动识别变量的类型
	cin >> a;
	cin >> b >> c;
	cout << a << endl;
	cout << b << " " << c << endl;
	return 0;
}

4.函数的缺省参数

  • 缺省参数是声明或定义函数时为函数的参数指定⼀个默认值.在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参.
  • 缺省参数分为全缺省和半缺省参数.全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值.C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值.
  • 带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参.
  • 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值.
#include <iostream>
using namespace std;

void Func(int a = 0) //a就是缺省参数
{
	cout << a << endl;
}
int main()
{
	Func(); // 没有传参时,使⽤参数的默认值
	Func(10); // 传参时,使⽤指定的实参
	return 0;
}

5.函数重载

5.1函数重载

C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同参数类型顺序不同.这样C++函数调用就表现出了多态行为,使用更灵活.C语言是不支持同⼀作用域中出现同名函数的.

int add(int a,int b){}
int add(int a,int b,int c){}//参数个数不同
int add(int a,float b){}//参数类型不同
int add(float a,int b){}//参数类型顺序不同
void add(int a,int b){}//不是重载函数

注意,重载函数必须至少满足上面三种情况之一,函数返回值类型不同不是重载函数,这不是重载函数的决定因素,所以,int add(int a,int b)和void add(int a,int b)同时出现会被认为重定义,是同一个函数,会报错!

5.2 函数重载和缺省参数结合时出现的一个小问题

// 下⾯两个函数构成重载
// 但是调⽤f1()时会报错,存在歧义,编译器不知道调⽤谁
void f1()
{
	cout << "f()" << endl;
}
void f1(int a = 10)
{
	cout << "f(int a)" << endl;
}

6.引用

6.1引用的概念和定义

引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同⼀块内存空间。
语法: 类型& 引用别名 = 引用对象;

int main()
{
	int a = 0;
	int& b = a;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

#include<iostream>
using namespace std;
int main()
{
	int a = 0;
	// 引⽤:b和c是a的别名
	int& b = a;
	int& c = a;
	// 也可以给别名b取别名,d相当于还是a的别名
	int& d = b;
	++d;
	// 这⾥取地址看到是⼀样的
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	cout << &d << endl;
	return 0;
}

在这里插入图片描述

6.2 引用的特点

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,再不能引用其他实体

6.3 引用的使用

引用的应用场景非常多,这里有一个非常典型的例子

void Swap(int& rx, int& ry)
{
	int tmp = rx;
	rx = ry;
	ry = tmp;
}

以前要实现交换就要用到指针的传值调用,这里的引用同样可以解决这个问题.

6.4 const引用

  • 可以引用⼀个const对象,但是必须用const引用.const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大.
  • 不需要注意的是类似 int& rb = a3; double d = 12.34; int& rd = d; 这样一些场景下a3的和结果保存在一个临时对象中,int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是说,rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这里触发了权限放大,必须要用常引用才可以.
  • 所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++中把这个未命名对象叫做临时对象。
int main()
{
	const int a = 0;
	//int& b = a;//权限被放大
	const int& b = a;
	return 0;
}
int main()
{
	int a = 0;
	const int& b = a;//权限缩小被允许
	a++;
	//b++;//错误,这个"别名"没有写的权限,只读
	return 0;
}
int main()
{
	int a = 10;
	const int& ra = 30;//const属性,可以给常量引用
	
	//int& rb = a * 3;//a*3 计算得到临时对象,被认为是常量
	const int& rb =  a * 3;

	double d = 12.34;
	int f = d;//隐式转换
	// int& rd = d;
	const int& rd = d;//隐式转换的过程中,d的整数部分为临时对象
	return 0;
}

6.5指针和引用的关系

  1. 引用必须初始化,不可以空引用,指针语法角度上不必要初始化,存在空指针,建议初始化,防止野指针.
  2. 引用语法上是给一个变量取别名,不会开辟新的内存空间,而指针存储一个变量的地址,需要开辟空间.
  3. 引用在初始化引用一个对象后就不能引用其他对象,而指针可以改变指向.
  4. 引用时直接访问对象,而指针要解引用
  5. 引用更加安全,指针会出现野指针问题,同时代码可读性下降.
  6. sizeof大小,指针根据平台不同,32位环境占四字节,64位占8字节,是固定的,引用的类型就是变量的类型,大小随之改变.

7.inline内联函数

  • 用inline修饰的函数叫做内联函数,编译时C++编译器会在调的地方展开内联函数,这样调用内联函数就需要建立栈帧了,就可以提高效率.
  • 但是,这对于编译器来说,只是一个建议,因为不当的展开并不会提高效率.
  • 适用于频繁调用的短小函数.

8.nullptr

C++中新增了一个特殊的关键字nullptr,字面上就是空指针.

#include<iostream>
using namespace std;
void f(int x)
{
	cout << "f(int x)" << endl;
}
void f(int* ptr)
{
	cout << "f(int* ptr)" << endl;
}
int main()
{
	f(0);
	// 本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(intx),因此与程序的初衷相悖。
	f(NULL);
	f((int*)NULL);
	// 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型
	// f((void*)NULL);
	f(nullptr);
	return 0;
}

NULL实际上是一个宏替换,在C语言中它被替换成((void*)0),而C++中被替换成字面常量0,这会出很多问题,而且C++比C语言检查严格,如下程序:

int main()
{
	int* a = NULL;
	int* b = (void*)0;//在C语言中可以通过,但C++中会报错,类型其实不同
	return 0;
}

对于这种情况,在之前的数据结构中初始化init创建各种空间时,都会用到这个(void*),那时候没问题,在C++中问题就大了,所以引入了一个新的关键字nullptr,就是空指针.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等你涅槃重生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值