C++入门

输入流输出流

在c语言中,在屏幕上输出内容需要用到printf函数,获取键盘上输入的内容需要用到scanf函数,不论是输出和输入都需要指定类型:


int main()
{
	int a = 1;
	char b = '2';
	double c = 3.0;

	//输入
	scanf_s("%d", &a);
	getchar();
	scanf_s("%c", &b);
	scanf_s("%f", &c);

	//输出
	printf("%d\n", a);
	printf("%c\n", b);
	printf("%f\n", c);

	return 0;
}

而在C++中对输入和输出进行了改良,分别使用cout输出,和cin输入来代替,

  1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
  2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含头文件中。
  3. <<是流插入运算符,>>是流提取运算符。
  4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型

语法规范:
输出:cout << 变量名/自定义内容<<
输出:cin >> 变量名

int main()
{
	int a;
	char b;
	double c;

	//输入
	cin >> a;
	cin >> b;
	cin >> c;

	//输出
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;

		
	return 0;
}

命名空间

C++是在C语言的基础上进行了改良,它既有面向对象也有面向过程,并且兼容C语言的语法,在C语言中,是没有办法解决命名冲突的,比如说:我想定义一个叫qsort的函数,但是在C语言的标准库中也有一个函数叫做qsort,但是我又特别想使用这个函数名,怎么办,所以C++的作者创造了命名空间这个概念,什么是命名空间呢,比如说:

#include<iostream>//C++标准库
using namespace ::std;//局部展开

int a = 10;//全局变量

void f1()
{
	int a = 20;
	cout << "f1 = " << a << endl;//打印输出
}

int main()
{
	cout << a << endl;
	f1();
	return 0;
}

在main函数中打印变量a,一定打印的是全局变量a,而不是打印f1函数中的a,因为f1函数中的a只会在函数被调用的时候创建,那如果我想在f1函数中访问全局变量a怎么办,这个时候可以在a前面加上 ::

void f1()
{
	int a = 20;
	cout << "f1 = " << a << endl;//打印输出
	cout << "全局变量a = " << ::a << endl;
}

那么在c++中如果避免这种命名冲突的情况发生,这时就要用到命名空间
命名空间就是将一段代码封装到一个域下,在使用的时候通过域名来指定访问那个空间下的变量或者函数。
命名空间的关键字:namespace
语法规范:
namespace 命名空间 //定义命名空间
{
代码;
}
代码:

命名空间 :: 变量/函数 //使用命名空间

#include<iostream>
using namespace ::std;


namespace A
{
	int a = 10;
	int b = 20;

	void Print()
	{
		cout << "A中的Print函数" << endl;
	}
}

namespace B
{
	int a = 30;
	int b = 40;

	void Print()
	{
		cout << "B中的Print函数" << endl;
	}
}
#include"test.h"

int main()
{
	cout << A::a << endl;
	cout << A::b << endl;

	cout << B::a << endl;
	cout << B::b << endl;

	A::Print();
    B::Print();


	return 0;
}

可以看到,即使使用的是同样的变量名,函数名,但是依旧可以区分开来,这个就是命名空间的作用,使用namespace来划分命名空间,在不同的命名空间下可以写相同名字的变量,函数等,但是这个并不影响正常的使用,在使用的时候,想要用哪个命名空间的变量,只需要在命名空间后面加上 ::变量名/函数名即可,这个::的意思就是去指定的命名空间找到要使用的变量/函数

命名空间展开
在使用域中的东西,如果不想再前面加上命名空间::,可以使用using展开
比如说我经常使用A中的函数,但是B中的函数我不经常使用,我可以将A中的内容展开,这样使用的时候就可以不加上 命名空间::

#include"test.h"
using namespace A;//命名空间展开
int main()
{
	cout << a << endl;//由于展开了命名空间A,所以在使用的时候会默认找到A中的变量和函数
	cout << b << endl;

	cout << B::a << endl;
	cout << B::b << endl;

	Print();
    B::Print();


	return 0;
}

using:展开命名空间
语法规范:using 命名空间
说明:在写上这条语句后,使用展开的命名空间中的内容,可以不用再加上::
但是一般不建议展开

部分展开
如果有一些经常使用的函数,我们可以部分展开,其他的仍旧需要使用::
语法规范:using 命名空间::函数名

如果经常使用一个命名空间中的某个函数或者变量,但是又不想将命名空间全部展开,这时可以部分展开,只展开自己想要的:
比如说,我经常使用标准库中的cout,但是我又不想将标准库全部展开,这个时候可以将标准库中cout函数部分展开:

#include<iostream>
using std::cout;//部分展开


namespace A
{
	int a = 10;
	int b = 20;

	void Print()
	{
		cout << "A中的Print函数" << std::endl;
	}
}

namespace B
{
	int a = 30;
	int b = 40;

	void Print()
	{
		cout << "B中的Print函数" << std::endl;
	}
}
#include"test.h"
using A::Print;//命名空间展开
int main()
{
	cout << A::a << std::endl;//由于只展开了Print函数,所以其他的变量前面仍旧要加上A::
	cout << A::b << std::endl;

	cout << B::a << std::endl;
	cout << B::b << std::endl;

	Print();//上面单独展开了Print函数,所以使用Print函数是不需要在前面加上命名空间
    B::Print();


	return 0;
}

引用

语法规范:变量类型 & = 变量
举例:
int a = 10;
int& b = a;

注意:

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

指针和引用,赋值/初始化 的时候权限可以缩小,但是不能放大
举例:

	int a = 1int& b = a;
		

	//下面两行代码是错误的,c在定义的时候已经在前加上const,意思是只读的
	//但是这个时候给c取别名,那么就可以通过这个别名取修改c中的数据,所以这是不被允许的
	const int c = 2;
	int& d = c;
	
	//但是在给c取别名的时候也在前面加上const,就可以
	const int& d = c;

	//权限缩小,在定义变量的时候不加上cosnt,在取别名的时候加上cosnt,将权限缩小
	int a = 10;
	const int& b = a;

在函数的参数中使用引用,引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做

引用说白了就是给变量取一个外号,比如说,西游记中的孙悟空,他有许多的别名:孙行者,美猴王,齐天大圣,弼马温,不论以上哪个别名,都是孙悟空本人,打死了弼马温,就等同于打死了孙悟空。

举例:
在这里插入图片描述

引用在许多场景下可以发挥出强大的作用,比如作为函数的参数和返回值:


void Swap(int& a, int& b)//使用引用作为参数
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int x = 10;
	int y = 20;
	Swap(x,y);
	cout << "x = " << x << endl;
	cout << "y = " << y << endl;

	return 0;
}
int& Swap(int a, int b)//使用引用作为返回值
{
	static int c = a + b;
	return c;
}

int main()
{
	int x = 10;
	int y = 20;
	int& z = Swap(x,y);
	cout << "z = " << z << endl;

	return 0;
}

引用和指针的不同点:

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

函数重载

在C语言中,是不允许两个相同名字的函数出现的,但是在C++中可以,但是两个函数相同,参数不能相同,这就叫做函数重载,函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题
举例:

//参数不同的Add函数
void Add(int a,int b)
{
	cout << "int a ,int b" << endl;
}

void Add(double a, double b)
{
	cout << "double a,double b" << endl;
}
//参数顺序不同的Add函数
void Add(int a,double b)
{
	cout << "int a,double b" << endl;
}

void Add(double b,int a)
{
	cout << "double b,int a" << endl;
}
//参数个数不同的Add函数
void Add()
{
	cout << "无参数的Add函数" << endl;
}

void Add(int a)
{
	cout << "只有一个参数的Add函数" << endl;
}

在使用的时候根据参数的不同,参数的顺序不同,参数的个数不相同来调用对应的同名函数

缺省参数

在创建函数的时候可以给函数的参数赋一个默认值,如果使用者在调用此函数的时候不想传入对应的参数,那么在调用后函数就会使用默认值
举例:
在这里插入图片描述
半缺省:如果一个缺省函数有多个参数可以缺省,这个时候可以传一部分值,剩下的参数使用缺省参数
举例:
在这里插入图片描述

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现
  3. 缺省值必须是常量或者全局变量

auto自动识别类型

auto作为一个关键字,在C++中的作用是自动识别类型,比如说:
在这里插入图片描述
在创建一个变量接受另一个变量赋值的时候,不需要指定类型,而是交给auto去自动识别,C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

auto不能推导的场景

  1. auto不能作为函数的参数
  2. auto不能直接用来声明数组
  3. . 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

结合auto的范围for循环
语法:
for(auto 变量名:数组名)
{
代码;
}
在这里插入图片描述
注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。
范围for循环中自己定义的变量,例如上面的tmp,并不是数组中的元素本身,而是一个拷贝,所以在范围for循环中改变tmp 的值,并不会影响外面的数组,如果想象通过范围for循环改变数组本身,可以使用引用:
在这里插入图片描述

类和对象

在C语言中,要想自己定义一个类型,可以用到结构体:struct
在C++中想要定义一个类型,同样也可以使用struct,但是在C++中这个被称之为类
类是什么?
C语言是一个面向过程的语言,许多的细节都需要自己去把握,因此代码量也会大大的提示,而C++中增加了面向对象这个概念,什么是面向对象,什么是面向过程,举个例子:
如果我要去做饭,我首先得到菜市场买菜,然后回家,再然后我要洗菜切菜,起锅烧油,控制火候,放入调味品等一系列繁琐的工作,什么都得自己做,这就是面向过程
有一天,我不想做饭,于是我去了饭店,饭店把所以的事情都做了,而我只需要吃饭就行了,这就是面向对象

所以,通俗的来说,面向过程就是自己来做,面向对象就是让别人来做。

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:
之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,
会发现struct中也可以定义函数

在这里插入图片描述
类的定义:
class 类名
{
成员变量;
成员函数;
}

在这里插入图片描述

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分
号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者
成员函数。

访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选
择性的将其接口提供给外部的用户使用。
【访问限定符说明】

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束。
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)

对象的大小计算:
对象大小的计算方式和结构体类似,但是有一点不同,就是类中的函数,并不占用类的大小,类只保存成员变量,而成员函数保存在公共区域
6. 第一个成员在与结构体偏移量为0的地址处。
7. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8
8. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
9. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

构造函数:
在创建对象时,自动调用的函数,如果不自己定义,系统会默认生成一个,构造函数和类名相同,构造函数没有返回值,可以重载
在这里插入图片描述
我们可以通过构造函数来给成员中的成员对象赋一个初始值:
在这里插入图片描述
构造函数重载:

class Test
{
public:

	Test(int a ,int b ,int c )
	{
		cout << "有参构造函数被调用" << endl;
		_a = a;
		_b = b;
		_c = c;
	}

	Test()
	{
		cout << "无参构造函数被调用" << endl;
	}

	void Print()
	{
		cout << _a << endl;
		cout << _b << endl;
		cout << _c << endl;
	}

private:
	int _a;
	int _b;
	int _c;
};

int main()
{
	Test a;//调用无参构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
	
	//Test a(1,2,3);//调用有参构造函数
	//注意:如果有已经有了一个带有函数重载的构造函数,再去调用无参的构造函数
	//编译器将不能识别,会报错
	a.Print();
	return 0;
}

构造函数默认值:构造函数对内置类型(int,double,char等)不做处理,对自定义类型进行对应的初始化

析构函数:
构造函数是初始化用的,那么析构函数和构造函数截然相反,它的作用是销毁
析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理
析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
    函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
class Test
{
public:
	Test()
	{
		 p = (int*)malloc(40);
		if (p == NULL)
		{
			cout << "开辟空间失败" << endl;
			exit(-1);
		}
	}


	//但对象生命周期结束,自动销毁开辟的动态内存
	~Test()
	{
		free(p);
		p = nullptr;
		cout << "析构函数调用" << endl;
	}

private:
	int* p;
};


int main()
{
	Test a;
	return 0;
}

拷贝构造:
语法:对象1(对象2)
对象1 = 对象2;
使用一个对象初始化另外一个对象,就叫做拷贝构造
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
    2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
  2. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
    字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
    4 拷贝构造函数典型调用场景:
    使用已存在对象创建新对象
    函数参数类型为类类型对象
    函数返回值类型为类类型对象

在这里插入图片描述
注意:在有些场景下不能使用拷贝构造,如果是普通的变量,在拷贝的时候是按照字节去拷贝,但是如果是指针,就会出问题,如果成员变量重有指针,那么就会造成两个对象的指针指向同一块空间,在一个对象使用结束后,
调用了析构函数销毁空间,那么这个时候如果另外一个对象也使用结束,同样也会调用析构函数去销毁空间,这样就会出错

内置类型可以直接拷贝,如果是自定义类型就需要调用拷贝构造

第一个C++小程序

通过上面的介绍,我们来使用C++编写第一个小程序,计算日期,输入一个数字N,计算出N天后是多少年多少月多少日

#include<iostream>
#include<assert.h>
using namespace std;

class Time
{
public:
	//使用构造函数初始化日期
	Time(int year = 2023,int moth = 3,int day = 1)
	{
		_year = year;
		_moth = moth;
		_day = day;
	}

	//计算出闰年
	int getmothday(int year,int moth)
	{
		//判断月份是否正确
		assert(moth > 0 && moth < 13);
		//使用数组存储一年中所有的月份天数
		int arr[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

		//判断闰年
		if (moth == 2 && ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)))
		{
			return 29;
		}
		else
		{
			return arr[moth];
		}
	}

	//计算出N天后是N年N月N日
	void getyearmothday(int x)
	{
		//首先将要计算的天数加上当前的月份
		_day += x;

		//如果天数大于当前月份的天数,则减去当前月份的天数,然后月份+1
		while (_day > getmothday(_year, _moth))
		{
			//减去当前月份的天数
			_day -= getmothday(_year, _moth);
			//月份+1
			_moth++;
			//如果月份大于13,将年+1,月份置为1
			if (_moth == 13)
			{
				_year++;
				_moth = 1;
			}
		}

		//循环结束,天数不再大于当前月份的天数,大于输出
		cout << _year << "年" << _moth << "月" << _day << "日" << endl;
	}

private:
	int _year;//年
	int _moth;//月
	int _day; //日

};

int main()
{
	Time ti(2023,3,1);
	ti.getyearmothday(100);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值