C++ Primer Plus学习笔记:第二章 开始学习C++

1.cin.get()与getchar()与cin的区别

  • cin.get()与getchar():get读入的一个字符,但是cin.get()读入的是一个流,毕竟他是一个istream类的cin对象的一个方法,所以他继承了ostream的流的特性
  • cin.get()与cin的区别:基本一样,但是cin.get() 是保留回车在输入流队列中的,输入数据类型也不一样
程序实验1.1:

#include 
#include 
#include 
using namespace std;

int main() {
     
	string t, m;
	cin >> t;
	cin >> m;
	cout << t << "!!" << m;
}

input 1

abc
cdf

output 1

abc!!def

input 2

abc cdf

output 2

abc!!def

#include 
#include 
#include 
using namespace std;

int main() {
     
	string t, m;
	cin.get(t);// >> t;
	cin.get(m);// >> m;
	cout << t << "!!" << m;
}

Debug:

“std::basic_istream> &std::basic_istream>::get(std::basic_streambuf> &,_Elem)”: 无法将参数 1 从“std::string”转换为“_Elem &”

说明了cin可以读入string,但是cin.get()不能

程序实验1.2:
#include 
#include 
#include 
using namespace std;

int main() {
     
	char t[10], m[20];
	cin >> t;
	cin >> m;
	cout << t << "!!" << m;
}

input 1:

abc
abc

output 1:

abc!!abc

说明回车在cin会被认为结束
input 2:

abc abc

output 2:

abc!!abc

说明cin会认为空格是分割符不会直接读入一个空格到char数组中,如果不想留0数组一定是scanf输入

input 3:

abcabcabca abc

output 3:

---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Debug Error!

Program: E:\Test\ccpp\ts\Project1\Debug\Project1.exe
Module: E:\Test\ccpp\ts\Project1\Debug\Project1.exe
File: 

Run-Time Check Failure #2 - Stack around the variable 't' was corrupted.

(Press Retry to debug the application)

---------------------------
中止(A)   重试(R)   忽略(I)   
---------------------------

注意,这里的cin读入不是像scanf那样大小是10就可以访问十个元素,这是一种“字符数组”,和char数组是有区别的,他要预留一个位置给\0

QQQ1问问题:不是说例如int a[10]的可访问a[0]-a[9],a[10]存在,就是\0
但是用VS调试不是这样的,但是访问*(&a+10)返回的是0,但是实际上为0的概率太小了,所以不知道为什么要预留\0,还有就是如何在控制台输入ASCII0
程序实验1.3:

cin.get(&a[i],size)

#include 
#include 
#include 
using namespace std;

int main() {
     
	char t[10], m[20];
	cin.get(t,10);
	cin.get(m,15);
	cout << t << "!!" << m;
}

input 1:

123 456

output 1:

123 456!!

说明输入时忽略空格空格符的,认为是字符串的一个组成,他是记录回车了刘输入,所以只有回车为分隔符(\n&\r),但是,他是解析流输入以后,回车被弹出,到时认为m的输入为一个但回车,size可以比实际大,甚至可以比数组大,由于是流输入,他相当于有一个缓冲区,所以不会报错,但是如果你的输入超过size,他会截取前size的字符赋值,如果输入超过数组内存,Visual Studio 2019会报错,但是Dev-C++不报错,输入了多少就输出多少,可能是用了流输入缓冲区

QQQ2问问题:why?

2int main()是函数头(但是不经限于main)

顺便说一下work()叫做函数头,里面的是形参列表


3.C&C++语句和分号作用变化


main():

  • C-的默认理解->自动补int为int main()
  • C++ 不能这样

int main()

  • C-的默认理解->对是否接受参数保持沉默
  • C++ 不能这样:等效int main(void){

注意: main()必须返回0
但是在main()中不写会默认返回0(只有在main()中)


4.为什么非要主函数而且必须叫main()

注意大小写不能任意修改
可以不叫main(),在c-开发的专用程序中可以不需要main()但是编译器会调用一个叫做_tmain()的函数(就像默认构造函数和构造函数的关系)或者在Windows下写dll模块不需要写main()


5.预处理命令

例如#include 这句话意思是在编译阶段将iostream文件的内容替换这里的#include ,一个简单地复制粘贴的过程,所以说,如果你不小心修改了iostream文件的内容,编译结果会改变,但是如果把.cpp或者.o文件(.o文件可以理解为编译器的"直译文件",没有进行连接等操作,.o文件经过连接就是exe文件了)复制到别的电脑上仍然可以正确编译

p.s.iostream文件:由istream和ostream组成,顾名思义就是输入流和输出流,他的兄弟(准确的说是应该是孩子)还有fstream(file-stream)是iostream的一个子类,他的对象继承使用istream/ostream的方法进行基本的操作,但实际上大部分继承的方法都有自己的独有方法,例如在穿参时对于ifstream对象可以选择istream/ifstream,但是在两个都存在的时候会选择ifstream

void test(istream &ost){
     
...
}
void test(ifstream &ost){
     				//<-----choose it
...
}
main(){
     
	...
	ifstream ff;
	ff.open()
	test(ff)
	...
}

6.头文件

头文件类型 约定 实例 说明
C++旧式风格 以.h结尾 iostream.h C++可以使用
C旧式风格 以.h结尾 math.h c/c++都可以使用
C++新式风格 没有扩展名 iostream C++可以使用,但是要加上namespace std
转换后的C 去.h前缀家c cmath C++可以使用,忽略c的特性,要加上namespace std

唯一要注意Visual Studio 2019在gcc/g++8.2.0-5下cstring string.h string 三个头文件各不相同

实验

#include 
using namespace std;

int main() {
     
	int t = 1;
	cout << t;
}

编译成功

#include 
//using namespace std;

int main() {
     
	int t = 1;
	cout << t;
}

编译失败,找不到标识符cout

#include 
//using namespace std;

int main() {
     
	int t = 1;
	cout << t;
}

编译成功(有可能编译失败了,那么是缺少头文件iostream.h手动补进来就可以了,代码在下面)

/***
*iostream.h - definitions/declarations for iostream classes
*
* Copyright (c) 1990-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* This file defines the classes, values, macros, and functions
* used by the iostream classes.
* [AT&T C++]
*
* [Public]
*
****/
#if _MSC_VER > 1000
#pragma once
#endif
#ifdef __cplusplus
#ifndef _INC_IOSTREAM
#define _INC_IOSTREAM
#if !defined(_WIN32) && !defined(_MAC)
#error ERROR: Only Mac or Win32 targets supported!
#endif
#ifdef _MSC_VER
// Currently, all MS C compilers for Win32 platforms default to 8 byte
// alignment.
#pragma pack(push,8)
#include 
#endif // _MSC_VER
/* Define _CRTIMP */
#ifndef _CRTIMP
#ifdef _DLL
#define _CRTIMP __declspec(dllimport)
#else /* ndef _DLL */
#define _CRTIMP
#endif /* _DLL */
#endif /* _CRTIMP */
typedef long streamoff, streampos;
#include  // Define ios.
#include  // Define streambuf.
#include  // Define istream.
#include  // Define ostream.
#ifdef _MSC_VER
// C4514: "unreferenced inline function has been removed"
#pragma warning(disable:4514) // disable C4514 warning
// #pragma warning(default:4514) // use this to reenable, if desired
#endif // _MSC_VER
class _CRTIMP iostream : public istream, public ostream {
     
public:
iostream(streambuf*);
virtual ~iostream();
protected:
iostream();
iostream(const iostream&);
inline iostream& operator=(streambuf*);
inline iostream& operator=(iostream&);
private:
iostream(ios&);
iostream(istream&);
iostream(ostream&);
};
inline iostream& iostream::operator=(streambuf* _sb) {
      istream::operator=(_sb); ostream::operator=(_sb); return *this; }
inline iostream& iostream::operator=(iostream& _strm) {
      return operator=(_strm.rdbuf()); }
class _CRTIMP Iostream_init {
     
public:
Iostream_init();
Iostream_init(ios &, int =0); // treat as private
~Iostream_init();
};
// used internally
// static Iostream_init __iostreaminit; // initializes cin/cout/cerr/clog
#ifdef _MSC_VER
// Restore previous packing
#pragma pack(pop)
#endif // _MSC_VER
#endif // _INC_IOSTREAM
#endif /* __cplusplus */

说明没有扩展名的必须namespace但是有.h的不需要(实际上iostream.h是叫非标准输入输出)


7.namespace

假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。
同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。

using namepace std;

这里的using是编译指令,要using的不是单单一个关键字,而是一个名字空间,所以写上namespace,之后知道他是一个namespace了,我们指明我们要使用的名字空间是std名字空间

但是有一个问题,比如我要用std,还要用一个我自己写的namespace np,结果np和std都有endl这个名字,就比较尴尬了,所以更妥当的方式是这样写

std::cout<<"hellow word";

我们之后说为什么他会有两个冒号
但是这种方法未免太麻烦了
于是人们又想了一个办法
告诉编译器我要使用using预编译但是不是整个名字空间而是名字空间里的一个元素

using std::cout;

这样以后就不用写了,这样的using我们叫他using声明,而using namespace std我们叫他using编译,不仅仅是因为他们声明的范围不同,更重要的是using声明可以限定在局部使用
下面说::符号
::是一种作用于解析运算符,就我而言我喜欢把他翻译成"下的",就像->.我喜欢把他们翻译成“的”一样
std::cout,std名字空间下的cout
m::work(),写类的外联函数用到的,m类下的work()函数
特殊用法:全局作用域,有的时候写了全局函数但是找不到,这个时候就需要这样调用

::work();

8.cin&cout

一个简单地输出

cout<<"pich"<<endl;

1.<<:这不是左移运算符吗,但是他在这里不是这个意思,这是通过重载的技术让<<同时具有了两种含义,就像汉语里的多音字一样,我们可以判断他是那个读音,计算机通过"上下文"判断这个<<不是左移,他的意思是把这个要输出的对象发送给cout,我们叫他插入运算符
2.endl:这是一种控制符,如果没有他,cout在输入一行后是不会自动换行的
3.\n:这是一种换行符,但是和endl还是有区别的,在早期的编译器中cout<<"\n"是不允许的,还有就是,我习惯只在最后一个地方写endl,虽然在中间写endl也是合法的,最后一个比较formal:endl确定可以在程序继续运行之前刷新,但是\n不做保证
对于这样的转义字符,如果要想在控制台上输出’\n’需要写’\n’才可以,在C++ 11里面还有一种方法是raw字符串
4.Raw字符串

	string s = R"1234(现在,我们连"(和)"都可以显示了)1234";
	cout << s << endl;

这样写我们在控制台上就可以得到1234(现在,我们连"(和)"都可以显示了)1234


9. 变量

int a=15;

首先告诉电脑我要一个int类型的变量,快点开空间
然后告诉这个内存单元叫做a
然后存储
这里C++11还提供了一种赋值方法

int a=b=c=d=e=1223;

赋值操作时候从右往左进行的

QQQ问题:好像还有一个什么东西也是自右向左的,但是忘了qwq

新的赋值方法:

#include 
using namespace std;

struct tt {
     
	int a, b;
	double c, d;
	int e;
};

int main() {
     
	int a {
      123 };
	tt m {
      1,2,1.2,1.3,4 };
	m{
      1,2,1.2 };
}

我们使用{}对他进行赋值,没有=他的特别之处就是可以对结构体进行赋值,按照写结构体的时候变量的顺序赋值,对于{}内数目少于总变量数的时候,从前往后填充,不够的填0
更多细节留到后边

**新的变量定义:**如果不想判断是什么类型,可以使用C++11的auto

auto a=123;
auto ss="qwqyinyinyin";

好像看不出什么优势
那看看下面这个

set<string> s;
set<string>::iterator it=nams.begin()

可以写成

set<string> s;
auto it=nams.begin()

可以偷懒了qwq
但是如果你用一个很复杂的结构体去auto,编译器会提示

在直接列表初始化上下文中,类型“auto”只能从单个初始值设定项表达式推导出
无法推导“auto”的类型(依据“initializer list”)

不能判断,尤其是有时候丧心病狂的不把{}里的内容写全
从报错提示我们可以知道,auto使用条件

  • 单个
  • 初始值设定项 (不能先定义,后声明,曾经我幼稚的想先auto,然后再cin进来数据,让他自动选择用long long int还是int)
  • 数据类型存在于“initializer list”

一个小的注意事项:
对于char:char t="a";是不合法的,必须是char t="a";而且print("%d",t);输出t的ASCII
对于string:string t="a";是不合法的,必须是string t="a";而且print("%d",t);输出不是t的ASCII


10.cout

print和cout的对比:
1.cout用起来更方便,不需要考虑变量类型,比如多个版本的C++标准对于long long int的占位符不同,用cout就不需要考虑这个问题
2.cout体现了C++的OOP特性通过重载<<实现了输出,所以可以继续通过重载<<的方式实现对结构体的整体输出
3.print设计不够精密,所以有些问题不会报错
4.print在实现特别复杂细致的格式的时候比较方便,但是print可以实现的cout都可以实现,甚至更多
5.print输出由于不保证在下一步运行时完成输出,所以速度快,cout用ios::sync_with_stdio(false);关闭同步单数速度还是差一点,但是注意关闭同步时候cout和printf

上次忘了说的:cin.get(),和getchar()一样,但是回车敏感,所以要暂停就要写两个,一个吃上一次输入的回车,一个等待输入


11.函数

work()叫做函数调用
work函数为被调用函数
函数原型:在编译的时候,编译器需要知道函数的返回值,所以需要函数原型,但是如果函数本身写在函数调用之前就不需要了
正常情况下写法就是吧实际的函数头复制下来,去掉{ ,去掉变量名,结尾加;
但是也有不一样的时候
比如我的work函数默认三个变量,但是有一个变量是可选的,不写则按默认值算,那么函数头应该写成

int work(int a,int b,int c=122)

函数原型是

int work(int a,int b,int c)
int work(int a,int b,int c)

函数原型是

int work(int a,int b,int c=122)

两种写法都可以
但是注意C++无法识别你想默认的是哪一个变量,只是从前往后填,哪里不够,哪里就按默认值走,所以,默认值必须写在最后
函数原型食欲函数就像变量声明之于变量,函数原型只是描述了函数的接口,也就是说描述了发送给函数和函数返回的信息

关于main函数的返回值:
Q:返回给谁?
A:可以吧操作系统看成调用程序,因此,main()的返回值是返回给系统了
Q:为什么return 0;
A:通常约定返回0代表程序正常结束,非零代表有问题
exQ:main是关键字吗
exA:不是,所以你完全可以写一个变量叫main(),但是不要写一个结构体,然后重载(),然后起名字叫main,然后main(),会有很多奇奇怪怪的错误

你可能感兴趣的:(#,C++)