5.1 从C到C++

5.1 从C到C++

这一章我会着重介绍C++的一些工具(注意是工具,不是基础知识),以及这些工具在算法中的一些运用。


5.1.1 c++版框架

首先我们之前在使用scanf和printf是引用stdio.h这个头文件。然而这在C++中不是最推荐的写法(可以,但不推荐)。C++一般推荐的头文件是:

#include<cstdio>

就是将c语言中很多.h的头文件,变为c~的形式。
具体发生了什么变化如下:
stdio.h和cstdio的区别

C++提供了一种新的输入输出的方式,这里要使用C++特有的头文件< iostream >:

#include<iostream>
using namespace std;
int main(){
	int a,b;
	cin>>a>>b;
	cout<<a<<" "<<b;
	return 0;
}

cin,表示标准输入的istream类对象。cin使我们可以从设备读入数据。>>a表示将数据放入a对象中,它的返回值为一个读取了a的流(注意是代表了一个读取了a的流,而不是a)
cout,表示标准输出的ostream类对象。cout使我们可以向设备输出或者写数据。<<a表示将a对象中存储的数据拿出。

这种输入输出方式比scanf和printf要慢,所以还是建议使用cstdio的输入和输出

那这里的using namespace std是什么意思呢?C++中有一个“名称空间”的概念(namespace),用来缓解复杂程序的组织问题。例如我们将多个不同的程序合并起来使用,然而对于程序A中的函数abc(),参与使用的程序B中也有一个函数名,参数类型完全相同的函数。于是不能进行函数重载(如果参数类型不同是可以的)。于是我们就可以将这两个abc函数分别放在他们各自的名称空间里(一位为X,一个为Y),然后像这样进行调用:

X:abc()
Y:abc()

头文件iostream和algorithm(一个很重要的头文件)里定义的内容都是放在std的名称空间里的,当我们代码里自己定义的内容和std里面的内容不重名。我们就可以用using namespace std的方法把std里所有的名字导入默认空间。这样就可以用cin去代替std::cin,cout代替std::cout。

事实上这种方式在实际的工程里面非常不推荐,可以看到很多程序员的代码里面还都是使用std::的。


5.1.2 引用

这里我们回忆一下在第四章介绍的swap函数,当时我们是用指针的方式进行编译的:

void swap(int* a,int* b){
	int t=*a;
	*a=*b;
	*b=*t;
}

在c++中我们可以:

void swapp(int &a,int &b){
	int t=a;
	a=b;
	b=t;
}

(这里用swapp命名的原因是因为algorithm头文件里以及提供了swap)
我们在参数名之前加一个&符号,就表示这个参数按照传引用的方式传递,而不是传值的方式传递。也就是说在函数内改变参数的值·,也会修改到函数的实参。
事实上传值的本质是:形参是实参的一份复制。传引用的本质是:形参和实参是同一个东西


5.1.3 字符串

当时我们在C语言中学习了字符数组,还有字符指针数组,然而实际操作中会感觉到非常的麻烦。C++提供了一个新的string的变量,用来代替C语言中的字符数组。

string 字符串变量名
输入格式:
cin>>string;//无空格
getline(cin,string);//有空格

这里要注意的是getline在循环输入的时候有一个蛮严重的bug (不知道新的编译器改了没有)

然后书本上还提供了一个头文件叫< sstream > (全称应该叫stringstream)。这个头文件提供了一种对于字符串流的读取和输出。

这是其中的一种用法

#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main(){
	string s,x;
	getline(cin,s);
	stringstream ss(s);//这一行表示创建了一个字符串流 
	while(ss>>x){ cout<<x<<endl;}
	return 0;
}

在这里插入图片描述
就是说我们将这个字符串按照空格分割成了三个字符串。
事实上这里的创建的字符串流是以上面一个含有空格的字符串建立的,这里自动将这一个字符串按照空格给分割开了。然后读取到x内进行输出。

这里我们再看一个例子:

#include<typeinfo>//调用typeid的头文件
#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main(){
	stringstream stream;
    string result;
	int i = 1000;
	stream<<i;
    stream>>result;
    cout<<result<<"这个变量的类型为:"<<typeid(result).name();
	return 0;
}

这里就是将整型i通过字符串的流读取,然后按照字符串输入了出来。(这里的typeid不是一个函数,我只是为了告诉大家这个1000被转化为了字符串而已)

同理,这个方法可以将任意的类型互相转化。但是在同一个流中进行多次转化需要加入这个操作:

stream.clear()

这里需要注意的是,clear清除的是标志位,但是没有释放stream占用的内存。
我们上面两个用法合并,可以得到一种非常厉害的用法(对我来说厉害):

#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main(){
	string a;
	getline(cin,a);
	stringstream ss(a);
	int x;
	while (ss>>x){
		cout<<x<<" "; 
	}
	return 0;
}

我们先将一个带有空格的字符串分段读取,然后再将这些段可以转化为数字的字符串输出。

这个用法我是在别人的博客里学的,然而这个用法在找数字的时候还不错,但是如果找char类型的时候,可能会把一个分段中的第一个首字母和后面的分段给割裂开,所以慎用这个方法

不过据说,string和sstream都很慢hhhh


5.1.4 再谈结构体

C++中支持类class(python入门的同学泪目)。事实上struct除了可以用成员变量外,还可以使用成员函数(struct和class默认访问权限和继承方式也不同)。在工程中,一般用struct定义纯数据的类型。

首先我们按照书本上的方式定义一个结构体:

struct Point{
	int x,y;
	Point(int x=0,int y=0):x(x),y(y){ }
};

这里我们在Point中定义了一个函数,函数名为Point,但事实上这个函数什么都没有做。这样的函数我们称作构造函数ctor(一般函数名即为结构体或类的名字,关于构造函数)

python类里面的_init_的作用和它即为相似
这个函数在我们的对象(变量)被创建时,会自动调用里面的内容(所以python的课程里有一章节叫做重写_init_方法)。

例如我们声明Point a,b(1,2),分别调用了Point()和Point(1,2)。(注意这里我们定义函数的时候x和y后面都加了0,表示x和y的初始值为0)
因此Point的意思即为Point(0,0)

x(x),y(y)为一个简单的写法,表示把成员变量x初始化为参数x,成员函数y初始化为参数y。(就是赋值啦)

接下来:

//这里函数的参数前加上const进行修饰,是为了保护指针,防止意外的被修改
Point operator + (const Point& A,const Point& B){
	return Point(A.x+B.x,A.y+B.y); 
}

书上这里介绍的非常粗糙(毕竟是算法书不是原理书)

事实上operator是c++的一个关键字,他和运算符一起使用,表示一个运算符重载函数,可以理解为扩展了原本这个运算符的功能。(就是按照这个运算符本来的格式定义了一种新的函数)

这里的const的用法也需要了解一下:关于const

(我问了一下老师好像说是这样子引用既安全又快捷)

具体的内容可参考:
operator介绍关于operator的使用重载运算符和重载函数

这里代码的意思就是对结构体Point我们给它定义了加法。

接下来定义流输出的方式(注意<<只能输出的一般类型):

ostream& operator << (ostream &out,const Point& p){
	return out<<"("<<p.x<<","<<p.y<<")";
}

接下来我们就可以通过cout<<p的方式输出一个结构体p了。


5.1.5 模板

首先要知道C++是一门强类型语言,也就是说无法做到像动态语言一样处理变量。但是对于一些函数而言,我们想让他突破类型的限制,在任意类型下都可以进行编译该怎么办呢?

template<typename T>

我们在函数的上面添加一行这个代码,然后把这个函数原本参量前的类型名改成T即可。(注意T*还是可以代表T的指针的)

那如果是结构体和类呢?

template<typename T>
struct Point{
	T x,y;
	Point(T x=0,T y=0):x(x),y(y){ }
};

但如果函数有返回值,且返回类型也需要突破类型的限制呢?

template<typename T>
Point<T> operator + (const Point<T>& A,const Point<T>& B){
	return Point<T>(A.x+B.x,A.y+B.y); 
}

书上是这么处理的,当然还有别的处理方式:
详细的可以参考:模板详解


这一章节的学习真的涉及了非常多新的知识呢(疲倦.jpg)


版权声明:本文为weixin_55835175原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。