C++ 新手学习手册!(持续更新中)

声明:笔者仍为在校学生,有错误之处恳请批评指正。
另本文基于《C Primer Plus》和《C Primer》,可以说是这两书以及作者个人理解的总结。如果在文中出现" "引用,无特别说明均出自这两本著作。
初期按部分整理相关内容(最基本的C语言语法不做介绍),更新情况见目录~
一、C++ 语言快餐式介绍
二、对象和类

  1. 什么是过程性编程?什么是面向对象?什么是面向对象编程?
  2. 啥叫类?
  3. 什么是封装?
  4. 返回引用和返回值有什么区别?
  5. 类作用域
  6. 运算符重载

三、输入和输出文件

  1. . 流和缓冲区
  2. 流、缓冲区和iostream文件
  3. 文件的输入输出
  4. 命令行处理
    (持续更新后续内容)
    —————————————目录线————————————————
    一、C++ 语言快餐式介绍
    C++通过一些特性改造了C语言,本身是C语言的超集。如果说C语言的精华是指针和内存管理,那么C++的精华之一就是其面向对象特性(OOP,Object-oriented Programming)。
    C++ 的OOP特性:封装、数据隐藏、数据抽象、多态和继承、代码重用。
    二、对象和类
    1.什么是过程性编程?什么是面向对象编程?
    过程性编程:在思考一个问题的解决方式时,首先考虑要遵循的步骤,按步骤考虑这些数据的实现。
    面向对象:面向对象是一种编程风格,它适用于各种编程语言中。就像数据结构也是通过C语言进行介绍,但是各种语言都离不开数据结构一样。C++提供了专门用来实现OOP方法的特性,因此用C++来介绍OOP更加明确。将数据表示和函数声明放在一个头文件中,这就是类声明。使数据表示成为私有,使数据只能被授权的函数访问。(比如说电脑希望用户双击“我的电脑”图标来打开这个页面,而不是有权限篡改浏览器的其他数据),数据的隐藏增加的数据的安全性。另外,将实现细节和接口设计分离出来有利于程序的维护(哪里不会点哪里,哪里不对改哪里)不会像写在一个文件里,改了一处程序出现了更多bug,这也就降低维护的工作量,大量节省了程序员的头发。

面向对象编程:首先考虑的不是步骤,而是要处理的数据。从用户的角度考虑对象,包括对象的所需数据和对象要进行的操作(接口)。模块化实现要进行的操作。
2.什么叫类?什么是接口?
类可以认为是一系列具有相同特征的数据及数据操作的集合。也就是类定义里定义了该类型的对象需要什么样的数据构成,以及这些对象可以实现什么样的操作(接口)。
类规范由两个部分组成:
类声明:数据成员 + 成员函数(接口),常写在 .h 文件中,也就是头文件内容。
类方法定义:类成员函数的具体实现,常写在 .cpp 文件中。

接口:对类我们说公共接口。接口在狭义上就是类中定义的数据所能进行的操作,也就是函数声明。用户在使用计算机时,不能直接告诉计算机自己想的是什么,而是通过程序提供的接口函数来进行交互。用户只需要知道这个函数是干什么的,怎么用就行了,而不用关心这个函数具体是怎么实现的。这样也达到了数据隐藏的目的。(Windows的桌面图标一定程度上都可以看做计算机提供的用户接口,我们知道双击来打开文件,或者打开浏览器,这就是我们会使用这个接口。但我们大多不知道这个接口是怎么实现的)。接口让程序员会使用类对象。要使用类,必须了解它的公共接口;要自己实现一个类,那么必须创建公共接口。
具体的实例实现可以参照即将更新的另一篇文章:《一道作业题:实例感受下类的代码重用》
3.什么是封装?
我们知道了类由声明 + 定义组成,也知道了共有接口的概念——我们说了用户不用关心这些接口的具体实现,但要编写类必须自己实现这些接口(其实就是编写函数操作,和C语言的函数有一点像,只是分开在不同的文件中,数据访问形式也有不同)。将共有接口和具体实现分开的数据存在形式就叫封装。将函数类定义和类声明放在不同的文件中也是封装的一个例子。
----------------------------------------------------------------------------2020.3.4首更
4.返回引用和返回对象到底有什么区别?
返回引用:可以节省空间和内存,加快程序执行的速度!
class money
{.
… … … … …
public:
const Money & Money::function(… … );//返回引用
}
返回引用意味着返回的是调用对象本身而不是其副本。
因为使用返回对象(值)时,系统需要制造出一个临时副本再进行数据复制。返回调用则直接操作调用对象。(显然返回引用的效率更高,因为省去了调用副本对象的构造函数、析构函数和复制的时间)。返回引用的实质就是返回地址。但并非所有的函数都可以有这种用法,对在函数中创建的临时对象则不能使用。
将函数参数声明为引用可以提高程序的效率。如果按值传递对象,代码功能是相同的,但是传递引用速度更快,使用内存更少。然而返回值不能返回指向局部变量或临时对象的引用。如果返回了这样的引用,意味着函数将返回临时创建对象的一个引用——但临时对象在函数结束时将会被删除,这样返回的引用是一个空指针。如果返回对象则大有改观,因为程序在删除临时对象之前会构造返回对象的副本,调用函数得到这个副本。
5.类作用域
在类中定义的名称作用域是整个类,而对其他的类控制访问权限。也就是说可以在不同的类中使用相同的变量或函数名。要调用共有的成员函数必须具体的对象。定义成员函数时也必须使用作用域解析运算符::。
class Money
{
private:
int num;
public:
void view_num(int n);
}
void Money::view_num(int n)
{
std::cout << num << endl;
}
int main()
{
Money * money = new Money;
Money a = Money(10); //使用直接成员运算符
a.view_num;
money->view_num; // 使用间接成员运算符
}

5.2 作用域为类的常量
枚举:如果在一个类的声明中需要用到一个常量,你可能这样写:
class sell
{
Private:
Int Months = 12;
Sell_money[Months];

}
这样的写法是错的,因为在声明时系统并不会给常量分配内存空间。要实现这样的用法只需要一个简单的枚举:
class sell
{
Private:
enum {Months = 12};
Sell_money[Months];

}
这样你就可以在类定义中用Months了——但是仅仅在类定义中。类成员函数中却不会包含这样的枚举。也就是说这样的枚举不会创建类数据成员。
另外,C++提供了另一种创建静态类成员的方法:
class sell
{
Private:
Static const int Months = 12;
}
这样创建的枚举量,和其他静态的变量是存在一起的,而没有在对象中。静态类成员以后介绍。

如果两个枚举中出现相同的常量名称怎么办?
假定有这样的两个枚举:
enum apple{num, price, source};
Enum banana{num, price, source};
系统会否认这样冲突的枚举。这个时候需要把枚举的作用域定义为类:
enum class apple{num, price,source}
enum class banana{num, price, place};
这样就可以避免冲突,在使用枚举量时需要标明枚举名限定:
apple one = apple::num;
另外,常规的枚举一般会默认为,或者自动转换为整型,但加了如上类的枚举,则不能自动转换类型。需要时,可以显式类型转换:
int a = int (apple::price);
5.3抽象数据类型
----------------------------------------------------------------------------2020.3.5二更
(更新中)
6. 什么是运算符重载?它有什么作用?
实际上,在类定义时,我们用不同的形参来区分定义的几个默认构造函数,实质上是函数名的重载。运算符重载将允许C++标准将加减运算用于类对象。运算符重载是一种C++的多态。
C++运算符重载允许扩展到用户定义的类型——比如定义 + 符号为两个整型数组相加返回一个他们的和值。对照 * ,本身也是一种运算符重载,可以表示相乘,也可以标识指针。我们注意到在程序中没有特别说明,编译器也知道我们是把它用作乘号还是标识指针。重载其它运算符,他们的使用也类似。只能重载有效的C++运算符,运算符之外的符号不被允许重载。回到数组相加的例子,用户只需要用加号就可以,而不需要知道它是怎么实现的。隐藏内部机理而强调实质,达到封装和代码重用的目的,这是C++的另一个优势。
下面具体介绍怎么用运算符重载:
需使用一个特殊的函数形式:operatorop( argument - list );
op 指有效的运算符(+ - [] … …);后面是形参列表。

三、文件的输入和输出

  1. 流和缓冲区
    正如C语言实现了一个标准函数库一样,C++自带了一个标准类库来实现输入和输出。在C++中,程序的输入和输出被看做字节流(输入流和输出流)。输入时程序从输入流中获取字节;输出时,程序将字节插入到输出流中。这种把信息看做数据流的方式使得C++可以用相同的方式来对待文件或终端(键盘)的输入输出——C++只是检查字节流而不关心字节流来自何方。相似的,C++只关心如何将字节插入到输出流中,而不关西心这些信息去往何处。因此管理输入分两步:
    (1)将流和程序关联起来
    (2)将流和文件关联起来
    什么是缓冲区?
    缓冲区(buffer)是用来暂存信息的工具。因为通常情况下,硬盘上的信息都是一大块一大块被读取的——磁盘驱动器以512以上的字节读取数据;而程序每次只能处理很少字节的信息。如果每次都要从磁盘中读取信息将会耗费大量时间——从内存直接读取需要的时间相当”可观“。如果把从内存读出来的一大块数据放入缓冲区,而程序每次从缓冲区中读取字节,显而易见可以提升很多效率。也就是说缓冲区帮助匹配两种不同的数据转换速率。同理输出数时,程序将字节插入到输出流中存入缓冲区,缓冲区满再将整块的数据穿给内存,并清空缓冲区。这一过程就叫刷新缓冲区(flushing the buffer )。
    缓冲区的益处不仅在提升速率上。如果我们在输入终端输入一个个字符,这些字符并没有在刚输入进去的时候就被处理,而是等待我们输入完毕按下回车键才进入缓冲区。这使得用户在数据被程序处理之前可以及时更正。也就是说C++程序通常在用户按下enter键时刷新输入缓冲区。
  2. 流、缓冲区、iostream文件
    在iostream文件中专门提供了用来管理流和缓冲区的类:
    streambuf类提供了管理缓冲区内存等方法;
    ios_base类用于表示流的一般特征,数据表示方式等;
    ios类继承ios_base,包括了一个指向streambuf对象的指针成员:
    ostream:输出方法;
    istream:输入方法;
    iostream:继承了上两者的输出输入方法。
    使用这些工具需要创建合适的类。创建这样的对象将自动打开一个流,自动创建缓冲区,并将其与流关联。
  3. 文件的输入输出
    · 什么是文件?
    文件是存储在某种设备上的一系列字节。

· 打开文件?
要操作文件就要定义相应的类:
要写入文件要定义一个ofstream 类(通常命名为fout),写入文件语句格式:fout << … … ;
之前提到了C++处理输出的时候只是把字节插入输出流而不关心字节的去向;这里字节的去向就是写入文件。
从文件中读数据要定义一个ifstream类(相似的命名为fin):
打开文件:fout.open(“filename”);
这就是打开一个文件的操作。

· 是否打开了想要的文件?——流状态检查
判断文件是否打开有很多方法:
if (!fin);
if (fin.fail());
if (fin.good());
if (fin.is_open()); //推荐这种方式,因为可以检测更细微的错误

· 写文件?
如果以这样的方式定义类:
ofstream fout(“filename”);
那么在写入文件的时候,如果没有发现目标文件的存在将会自动创建。如果已有文件,则会清空文件中的内容。如果是使用这种方法创建从文件中读数据: ifstream fin(“filename”); 那么只会在没有发现目标时创建新文件,而不会在发现目标文件时清空其内容。
但是写入文件时清除它之前的内容,这怎么行呢?如果我们不想清空文件中的内容,而是要向文件中添加内容,需要使用这种方法:
ofstream fout (“filename”, ios_base.out | ios_base.app);(c++)
FILE *fp;
fp = fopen(“filedemo.txt”,“w”) C )

· 特殊:关闭到文件的链接
输入和输出都是被缓冲的。创建ifstream对象(fin)时,将创建一个由fin管理的输入缓冲区。和之前缓冲区的介绍一样,缓冲区中写入的数据会被成块写入文件。当输入、输出流对象过期(程序终止)时,缓冲到文件的链接会自动关闭。另外也可以显性地关闭这种链接:
fin.close();
fout.close();
这种操作只是关闭了对象管理的缓冲区到文件的链接,并没有删除缓冲区——就像我们猜测的,可以再把流重新连接到另一个文件上。这样保留对象和缓冲区有什么好处呢?可以猜到,我们可以用他们来处理下一个文件——只需要更改对象和文件之间的链接就可以了,而不必再创建对象(以及它管理的缓冲区)。

  1. 命令行处理
    命令行参数:用户在输入命令时,在命令行中输入的参数。
    C++的访问命令行参数的机制:在main中:
    int main( int argc, char *argv[ ] );
    argc : 命令行中的参数个数,包括命令本身。 char * argv[ ]:指针数组,指针指向命令行参数
    例如有下面的命令:
    wc report1 report2 report3
    那么argc = 4 ,argv[0] = wc, argv[1] = report1 …

· 一个有关实现文件操作的应用栗子
#include
#include
#include
#include //for exit();
int main()
{
using namespace std;
char ch;
//const char * file = “gueats.txt”;
//ifstream fin(“guests.txt”, ios::out | ios::app);
ifstream fin;
fin.open(“guests.txt”);
/如果不存在这样的文件将创建一个空的新文件;
若用这种方式写文件:fout.open(filename)那么
有目标文件则会清空原来的内容
/

//保留源文件内容应该这么做:
 
if(!fin.is_open())
{
	cerr << "Can't open guests.txt!\n";
	exit(EXIT_FAILURE);
}
else
{
	cout << "Here is the current contents of the file:\n";
	while(fin.get(ch))
	cout << ch;
}
fin.close();

ofstream fout("guests.txt", ios_base::out | ios_base::app);
//不会清空原来文件的内容 
if(!fout.is_open())
{
	cout << "Can't open guests.txt!\n";
	exit(EXIT_FAILURE);
}
else
{
	string name;
	cout << "Enter the new guest's names(enter a blank line to quit)!\n";
	while(getline(cin,name) && name.size() > 0 )
	{
		fout << name << endl;
	}
	fout.close();
}
return 0;

}
(更新中)
原创文章,有纰漏之处联系修改。
另外可以关注作者的其他文章,一只软件工程在校生的学习历程全记录~
持续更新中~欢迎指正拍砖 ~
作者的其它文章:
第一道OOP课程题目
https://blog.csdn.net/weixin_45612325/article/details/104660088

你可能感兴趣的:(笔记)