Contents
  1. 1. 小知识
    1. 1.1. 1. 在控制台窗口内编译程序
    2. 1.2. 2. 输入输出流
    3. 1.3. 3.for循环和while循环的对比
    4. 1.4. 4.读取数据直到文件尾
    5. 1.5. 5.一旦选择了一种风格,就要坚持使用。
    6. 1.6. 6.能定义使用上像内置类型一样自然的类类型
    7. 1.7. 7.使用#include<> // “”
    8. 1.8. 8.使用文件重定向
    9. 1.9. 9.算术类型 && 空类型
    10. 1.10. 10.运算
    11. 1.11. 11.’ ‘ & “ “
    12. 1.12. 12.面向对象程序设计的思想
    13. 1.13. 13.初始化和赋值
    14. 1.14. 14.列表初始化
    15. 1.15. 15.默认初始化
    16. 1.16. 16.分离式编译
    17. 1.17. 17.标识符
    18. 1.18. 18.嵌套的作用域
    19. 1.19. 19.引用(&d,d是声明的变量)
    20. 1.20. 20.空指针
    21. 1.21. 21.赋值永远改变的是等号左侧的对象
    22. 1.22. 22.两指针比较
    23. 1.23. 23.void* 指针
    24. 1.24. 24.指向指针的引用
    25. 1.25. 25.const限定符
    26. 1.26. 26.const的引用
    27. 1.27. 27.指针和const
    28. 1.28. 28.非常量引用不能引用字面值常量
    29. 1.29. 29.顶层 && 底层
    30. 1.30. 30.constexpr和常量表达式
    31. 1.31. 31.定义类型别名
    32. 1.32. 32.auto类型说明符
    33. 1.33. 33.decltype类型指示符
    34. 1.34. 34.预处理器
    35. 1.35. 35.抽象数据类型库
    36. 1.36. 36.using
    37. 1.37. 37.#include
    38. 1.38. 38.getline
    39. 1.39. 39.处理string中的字符
    40. 1.40. 40.范围for语句
    41. 1.41. 41.标准库类型vector
    42. 1.42. 42.迭代器
    43. 1.43. 43.数组
    44. 1.44. 结构体
    45. 1.45. 函数的重载和覆盖
    46. 1.46. string
    47. 1.47.
    48. 1.48. 构造器与析构器
    49. 1.49. this
    50. 1.50. 继承
    51. 1.51. 关于endl和’\n’的区别
    52. 1.52. 访问控制
    53. 1.53. 和java有点像喔…
  2. 2. 一些easy_test
    1. 2.1. 读取文本
    2. 2.2. 复制文本
    3. 2.3. 字符串变大写!
    4. 2.4. 迭代器运算小🌰子
    5. 2.5. 构造器和析构器
    6. 2.6. 继承

在有c语言基础下学习《C++ Primer》的新知

小知识

okfine太繁杂了…我觉得写的太烂了…淦,后面二次学习的话就会精简一些了…吧…

  • 9.7更
    1
    using namespace std;

使用std::虽然较麻烦,但是在大型程序中,很有必要。后期讲命名空间解释。

1. 在控制台窗口内编译程序

假定main程序保存在文件prog1.cc中,用如下命令编译它:

1
$ CC prog1.cc

其中,CC为编译器程序的名字,$是系统提示符。

2. 输入输出流

1
cin >> 变量名;

cin >> 其中>>最初定义为右移操作符
体现了重载
理解为 从cin内部缓冲区中提取信息
<<同理

一些函数

1
2
3
4
5
6
7
8
9
10
11
12
cin.ignore
cin.getline
cin.peek
cin.get
cin.read
cin.gcount
cin.write
----------------------------------------------
cin.eof():如果到达文件(或输入)末尾,返回true
cin.fail():如果cin无法工作,返回true
cin.bad():如果cin因为比较严重的原因(例如内存不足)而无法工作,返回true
cin.good():以上情况都没有发生。返回true
1
2
3
cout << "字符串字面常量" << 变量运算 << endl;
| |
可有可无

一些函数

1
2
cout. precision
cout. width

想象一下,cin>>x
就是从输入流给到x
cout<<x
就是x给输出流
这样好记住用<<还是>>

3.for循环和while循环的对比

在循环次数已知的情况下,for循环的形式更简洁。
循环次数无法预知时,用while循环实现更适合。
用特定条件控制循环是否执行,循环体中执行的语句可能导致循环判定条件发生变化。

4.读取数据直到文件尾

1
while(cin >> value)

直接对输入流cin进行检测
其中键入时,windows系统中,输入文件结束符的方法是Ctrl+Z,然后按Enter或Return
在unix系统和Mac OS X系统中,文件结束符的输入是Ctrl+D

5.一旦选择了一种风格,就要坚持使用。

really???嘻嘻,觉得想换就可以改进嘛

6.能定义使用上像内置类型一样自然的类类型

给的Sales_item.h头文件 网址==http://www.informit.com/store/c-plus-plus-primer-9780321714114,是卖书的。。。
没搞明白呢?先打个❓


  • 9.8更

7.使用#include<> // “”

包含来自标准库的头文件时,应该用< >包围头文件名。对于不属于标准库的头文件,则使用" "包围
(在学习c语言的时候,没有太注意<>和””的区别)

8.使用文件重定向

测试程序时,允许我们将标准输入和标准输出与命名文件关联起来:

1
2
3
4
$ addItems <infile >outfile
| | | |
| 可执行文件 | |
系统提示符 从infile中读取将输出结果写入outfile

9.算术类型 && 空类型

算术类型分为两类:整型(包括字符布尔类型在内)和浮点型

10.运算

c语言没有注意过,但是这是个漏洞点哟
当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成无符号数

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;

int main()
{
unsigned u = 10;
int i = -42;
cout << i + i << endl;
cout << u + i << endl;

return 0;
}

运行结果为

1
2
-84
4294967264

相加前先把整数-42转换成无符号数,


  • 9.10更

今天觉得自己c语言学的真粗糙,嘿嘿

11.’ ‘ & “ “

例如:字面值’A’表示的是单独的字母A,而字符串”A”则代表了一个字符的数组,该数组包含两个字符:一个是字母A,另一个是空字符(’\0’)

12.面向对象程序设计的思想

通常情况下,对象是指一块能存储数据并具有某种类型的内存空间。
更加细致可以参考:面向对象与面向过程的本质的区别

13.初始化和赋值

在C++语言中,初始化和赋值是两个完全不同的操作。然而在很多编程语言中二者的区别几乎可以忽略不计,即使在C++中有时这种区别也无关紧要,但是需要强调的是,这个概念至关重要,后面会经常提到。
初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。

14.列表初始化

要想定义一个名为units_sold的int变量并初始化为0,可以使用一下语句:

1
2
3
4
int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);

15.默认初始化

如果内置类型的变量未被显式初始化,则:
1.定义于任何函数体之外的变量被初始化为0。
2.定义在函数体内部的内置类型变量将不被初始化,此时试图拷贝或以其他形式访问此类值将引发错误。
3.绝大多数类都支持无需显式初始化而定义对象,如string类规定如果没有指定处值则生成一个空串。
4.一些类要求每个对象都显式初始化,此时如果创建了一个该类的对象而没有明确初始化操作,则引发错误。

16.分离式编译

c++支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。

1
2
3
extern int i;    //声明而非定义
int j; //声明并定义
extern double pi = 3.14159; //定义

在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。
如果要在多个文件中使用同一个变量,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

17.标识符

必须以字母或下划线开头
用户自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字母开头
定义在函数体外的标识符不能以下划线开头

18.嵌套的作用域

使用作用域运算符::
在本例中表示显式地访问全局变量
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
int reused = 42;

int main()
{
int unique = 0;
std::cout << reused << " " << unique << std::endl;
int reused = 0;
//使用局部变量reused,此时变量in scope
std::cout << reused << " " << unique << std::endl;
//显式访问全局变量reused
std::cout << ::reused << " " << unique << std::endl;
return 0;
}
1
2
3
42 0
0 0
42 0

  • 9.11更

19.引用(&d,d是声明的变量)

定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另一个对象,因此引用必须初始化。


  • 9.12放假划水了一天…
  • 9.13更

20.空指针

几种生成空指针的方法

1
2
3
4
int *p1 = nullptr;
int *p2 = 0;
//需要首先#include cstdlib
int *p3 = NULL;

NULL是一个预处理变量,在头文件cstdlib中定义。预处理变量不属于命名空间std。
在C++11新标准下,最好使用nullptr,同时尽量避免使用NULL。
把int变量直接赋给指针是错误的操作。

21.赋值永远改变的是等号左侧的对象

尤其是对于指针这种,很容易迷惑
eg:

1
2
3
int *pi = 0;  //pi被初始化为0
pi = &ival; //pi指向ival
*pi = 0; //(*pi即pi指向的对象)ival发生改变

22.两指针比较

类型相同的指针比较结果为布尔型。
两个指针存放的地址值相同有三种可能:
1.它们都为空
2.都指向同一个对象
3.一个指针指向某对象,同时另一个指针指向另外对象的下一地址

23.void* 指针

void*能存放任意类型对象的地址。他能做的就是:和其他指针比较、作为函数输入输出、赋给另外一个void*指针。不能直接操作

24.指向指针的引用

好复杂… 昏

1
2
3
4
5
6
int i = 42;
int *p;
int *&r = p; //r是一个对指针p的引用

r = &i; //r引用了一个指针,给r赋值&i就是令p指向i
*r = 0; //解引用r得到i,也就是p指向的对象,将i的值改为0

(从右向左阅读有助于弄清复杂指针或引用的声明语句的真实含义)

25.const限定符

1
const int bufSize = 512;

const对象一旦创建后,任何试图为bufSize赋值的行为都将引发错误。所以const对象必须初始化。初始值可以是任意复杂的表达式。

const学的非常不好…可以回头再多看看

26.const的引用

与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。

1
2
3
4
5
6
const int ci = 1024;
const int &r1 = ci;

r1 = 42; //错误,r1是对常量的引用
const int &r = r1; //合法,但是不允许通过r修改r1
int &r2 = ci; //错误,试图让一个非常量引用指向一个常量对象==>尤其容易搞错

假设第四句是合法的,则可以通过r2来改变它引用对象的值,这显然不正确。

27.指针和const

指针的类型必须与其所指对象的类型一致,但有两个意外。
1.允许一个指向常量的指针指向一个非常量对象。
2.

引用的类型必须与其所引用对象的类型一致,但有两个例外。
1.在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。
2.

28.非常量引用不能引用字面值常量

29.顶层 && 底层

顶层const:表示指针本身是一个常量。可以表示任意的对象是常量,对任何数据类型都适用。
底层const:表示指针所指的对象是一个常量。与指针和引用等复合类型的基本类型部分有关。

30.constexpr和常量表达式

1
2
3
4
const int max_files = 20;    //是常量表达式
const int limit = max_files + 1; //是
int staff_size = 27; //不是
const int sz = get_size(); //不是

其中staff_size的初始值是一个字面值常量,但是它只是一个普通的int,而非const int,所以不属于常量表达式。
sz本身是一个常量,但是它的具体值直到运行时才能获取到,所以不是常量表达式。
C++11规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。尽管不能使用普通的函数作为constexpr变量的初始值,但允许用constexpr函数(足够简单以使得编译时就可以计算其结果)去初始化constexpr变量。
而声明constexpr时用到的类型称为“字面值类型”
一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。

31.定义类型别名

1.使用关键字typedef
2.使用别名声明

1
using SI = Sales_item;    //SI是Sales_item的同义词

  • 9.14更(今天又出去玩了…)

    p61需要再研究

32.auto类型说明符

能让编译器替我们去分析表达式所属的类型。auto让编译器通过初始值来推算变量的类型。所以,auto定义的变量必须有初始值。
如果使用auto在一条语句中声明多个变量,则要保证该语句中所有变量的初始基本数据类型都一样。

33.decltype类型指示符

作用:选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
eg:

1
decltype(f()) sum = x;  //sum的类型就是函数f()的返回类型

如果decltype使用的表达式是一个变量,则返回该变量的类型(包括顶层const和引用在内)。
如果decltype使用的变量加上了一层或多层括号,编译器就会把他当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。
eg:

1
2
decltype((i)) d;  //错误,d是int&,必须初始化
decltype (i) d; //正确,d是int

decltype((variable))的结果永远是引用,而decltype(variable)的结果只有当var本身是引用时才是引用。


  • 9.25更

从第五空间线下回来了…好菜…又刷了点pwn题,继续哈~

34.预处理器

从c语言继承而来,确保头文件多次包含仍能安全工作的常用技术是预处理器
预处理功能
1.#include:用指定的头文件的内容代替#include
2.头文件保护符:依赖于预处理变量。#define把一个名字设定为预处理变量,#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查为真,则执行后续操作直至遇到#endif为止。

程序员一般习惯性的加上头文件保护符,不太在乎程序是否需要

35.抽象数据类型库

string和vector是两种最重要的标准库类型,前者支持可变长字符串,后者表示可变长的集合。还有一种标准库类型是迭代器,它是string和vector的配套类型,常被用于访问string中的字符或vector中的元素。

36.using

我们用到的库函数基本都属于命名空间std,同时使用作用域操作符(::)
简单途径就是 使用using声明使用到命名空间的成员

1
using namespace::name;

用到的每个名字都必须有自己的声明语句,分号结束。

头文件不应包含using声明

37.#include

可以定义很多种初始化string对象的方法=.=【使用等号(=)执行拷贝初始化,否则为直接初始化

1
2
3
4
5
6
string s1;    //默认初始化,s1是一个空字符串
string s2(s1); //s2是s1的副本
string s2 = s1; //等价于s2(s1)
string s3("value"); //s3是字面值“value”的副本,除了字面值最后的那个空字符串
string s3 = "value"; //等价于s3("value"),s3是字面值“value”的副本
string s4(n, 'c'); //把s4初始化为由连续n个字符c组成的串

string对象上的操作

1
2
3
4
5
os<<s;    //将s写到输出流os当中,返回os
is>>s; //从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s); //从is中读取一行赋给s,返回is
s.empty(); //s为空返回true,否则返回false
s.size(); //返回s中字符的个数

string对象会自动忽略开头的空白(即空格符、换行符、制表符等),并从第一个真正的字符开始读起,直到遇见下一处空白为止。


  • 9.26更

    38.getline

getline函数从给定输入流中读入内容,直到遇到换行符为止(换行符也被读进来了),如果输入的一开始就是换行符,那么所得的结果是个空string。

1
2
3
4
string line;
//每次读入一整行,直至到达文件末尾
while(getline(cin, line))
cout << line << endl;

empty函数:根据string对象是否为空返回一个对应的布尔值。

1
2
3
4
//每次读入一行,遇到空行直接跳过
while(getline(cin, line))
if(!line.empty())
cout << line << endl;

size函数:返回string对象的长度

1
2
3
while(getline(cin, line))
if(line.size() > 80)
cout << line << endl;

size函数返回的是一个string::size_type类型。它是一个无符号类型的值而且能够存放下任何string对象的大小。所有用于存放string类的size函数返回值的变量,都应该是string::size_type类型。

C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型:

1
auto len = line. size();    //len的类型是string::size_type

假设n是一个负值int,则表达式s. size() < n的判断结果几乎肯定是true。这是因为负值n会自动地转换成一个比较大的无符号值。

tips:如果一条表达式中已经有了size()函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题。

string对象的比较依照字典顺序。

1
2
3
string str = "hello";     //3
string phrase = "hello world"; //2
string slang = "hiya"; //1

两个string对象相加使用(+)(+=)直接串接而成。

字面值和string对象相加:必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string。

1
2
3
strjng s1 = "hello";
string s = s1 + "," + " world";
//正确

39.处理string中的字符

cctype头文件中的函数:过多… 用到时查一下,记住常用的好了。

使用c++版本的c标准库头文件:c语言头文件形如name.h,c++将这些文件命名为cname,c表示这是一个属于c语言的头文件。

40.范围for语句

for(declaration : expression)
       statement

其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值

访问string对象中的单个字符有两种方式,一种是使用下标运算符([ ]),另一种是使用迭代器。
注意下标的设置,因为c++标准并不要求标准库检测下标是否合法。


  • 9.28更

41.标准库类型vector

vector表示对象的集合,其中所有对象的类型都相同。它也常被称为容器
要使用vector必须包含适当的头文件:

1
2
#include<vector>
using std::vector;

C++语言既有类模板,也有函数模板。其中vector是一个类模板。编译器根据模板创建类或函数的过程称为实例化,当使用模板时,需要指出编译器应把类或函数实例化成何种类型。

1
2
vector<int> ivec;    //ivec保存int类型的对象
vector<vector<string>> files; //该向量的元素是vector对象

vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。
早期版本C++标准:vector<vector > //======>外层添加一个空格
C++11标准:vector<vector>
初始化vector对象的方法
|||
-|-|
vector v1| v1是一个空vector,它潜在的元素是T类型的,执行默认初始化|
vector v2(v1) | v2中包含v1所有元素的副本 |
vector v2 = v1 | 等价于上一条 |
vector v3(n, val) | v3包含了n个重复的元素,每个元素的值都是val |
vector v4(n) | v4包含了n个重复地执行了值初始化的对象|
vector v5{a, b, c … } | v5包含了初始值个数的元素,每个元素被赋予相应的初始值|
vector v5 = {a, b, c … } | 等价于上一条—>列表初始化|
可以选择初始化为空,或者进行拷贝。 如果vector对象中的元素的类型不支持默认初始化,我们就必须提供初始化的元素值。

可以通过花括号({ })和圆括号(( ))来区别是列表初始化还是元素数量:

1
2
3
4
vector<int> v1(10);    //v1有10个元素,每个元素的值都是0
vector<int> v2{10}; //v2有1个元素,值为10
vector<int> v3(10, 1); //v3有10个元素,每个的值都是1
vector<int> v4{10, 1}; //v4有两个元素,值分别是10和1

但如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了:

1
2
3
4
vector<string> v5("hi");    //列表初始化,v5有一个元素
vector<string> v6{"hi"}; //错误:不能使用字符串字面值构建vector对象
vector<string> v7{10}; //v7有10个默认初始化的元素
vector<string> v8{10, ”hi“}; //v8有10个值为“hi”的元素

vector的成员函数push_back:负责把一个值当成vector对象的尾元素“压到(push)”vector对象的“尾端(back)”。

1
2
3
4
5
string word;
vector<string> text;
while(cin >> word){
text.push_back(word); //将word添加到text后面
}

范围for语句体内不应改变其所遍历序列的大小。

除了push_back外,vector还提供了几种其他操作,大多与string类似。

vector对象及string对象的下标运算可用于访问已存在的元素,而不能用于添加元素。对于vector对象,正确的方法是使用push_back。

关于下标必须明确的一点是:只能对确知已存在的元素执行下标操作。


  • 9.30更

    42.迭代器

    有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置;其他所有情况都属于无效。

和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。这些类型都拥有名为beginend的成员。
如果对象是常量,begin和end返回const_iterator,否则,返回interator

1
2
3
4
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1类型是vector<int>::iterator
auto it2 = cv.begin(); //it2类型是vector<int>::const_iterator

C++11新引入了cbegin,cend,为了确保不管怎样返回类型都为const_iterator

1
2
3
//b表示v的第一个元素,e表示v尾元素的下一位置
auto b = v. begin(), e = v.end();
auto it = v.cbegin(); //it的类型是vector<int>::const_iterator

尾后迭代器(尾迭代器)没有什么实际含义,仅是个标记而已,表示我们已经处理完了容器中的所有元素,不能对其进行递增或解引用的操作。
如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。
标准容器迭代器的运算符与C类似,见到就能懂是干嘛的。

C++程序员习惯性使用!=或==,是因为所有标准库容器都定义了他们,而大多数没有定义<运算符。


  • 11.24更,我好能鸽

迭代器精确类型无须知道。
const_iterator能读取但是不能修改

1
(*it).empty() 相当于 it->empty()		//解引用it,然后调用结果对象的empty成员,检查it所指字符串是否为空

试图在循环体中对迭代器所属容器添加元素,或任何一种可能改变vector对象容量的操作,如push_back,都会使迭代器失效
迭代器运算:主要是位置关系的运算,不是简单的在自身上加减

关于指针迭代器
这里引用Alinshans在知乎上的回答
如果只讨论 STL container 类的 iterator,它们其实都是一种泛型指针。C风格指针是属于 iterator 的一种的。iterator 根据功能做了更细的划分,STL 中的 iterator 分成了五类。我觉得它们的区别:
1.在范围上,pointer 属于 iterator 的一种(random access iterator)
2.在功能上,iterator 有着比 pointer 更细的划分并对应能力不同的功能(重载不同的运算符)
3.在行为上,iterator 比 pointer 更统一和良好的用法(更轻易使用 begin()、end()且不用担心越界)等
我觉得:额…但是似乎并不是这么简单的涵盖问题,迭代器贴近一层抽象,进行封装。数组,链表十分不利于代码重用
另外一位答者Khellendros的回答我觉得很好
迭代器实际上是对“遍历容器”这一操作进行了封装。在编程中我们往往会用到各种各样的容器,但由于这些容器的底层实现各不相同,所以对他们进行遍历的方法也是不同的。例如,数组使用指针算数就可以遍历,但链表就要在不同节点直接进行跳转。这是非常不利于代码重用的。例如你有一个简单的查找容器中最小值的函数findMin,如果没有迭代器,那么你就必须定义适用于数组版本的findMin和适用于链表版本的findMin,如果以后有更多容器需要使用findMin,那就只好继续添加重载……而如果每个容器又需要更多的函数例如findMax,sort,那简直就是重载地狱……我们的救星就是迭代器啦!如果我们将这些遍历容器的操作都封装成迭代器,那么诸如findMin一类的算法就都可以针对迭代器编程而不是针对具体容器编程,工作量一下子就少了很多!至于指针,由于指针也可以用来遍历容器(数组),所以指针也可是算是迭代器的一种。但是指针还有其他功能,并不只局限于遍历数组。因为使用指针变量数组的操作太深入人心,c++stl中的迭代器就是刻意仿照指针来设计接口的

  • 11.27

    43.数组

    与vector相似,数组的元素应为对象,不存在引用的数组。但数组的大小固定,对某些特殊的应用来说程序的运行时性能较好,但是灵活性差。
    定义数组不允许用auto关键字。
  • 字符数组特殊性【这点同C语言】
    1
    2
    3
    4
    char a1[] = {'C', '+', '+'};
    char a2[] = {'C', '+', '+', '\0'};
    char a3[] = "C++";
    char a4[6] = "Daniel"; //错误,因为使用符串字面值初始化会自动添加空字符\0

为了更加明显的理解【遗憾的是,c和c++并没有定义直接返回数组长度的函数】

1
2
3
4
5
6
7
8
9
10
11
int main() {
char a1[] = { 'C', '+', '+' };
char a2[] = { 'C', '+', '+', '\0' };
char a3[] = "C++";
char a4[6] = "Danil";
cout << "a1长度为:" << sizeof(a1) / sizeof(a1[0]) << "\n";
cout << "a2长度为:" << sizeof(a2) / sizeof(a2[0]) << "\n";
cout << "a3长度为:" << sizeof(a3) / sizeof(a3[0]) << "\n";
cout << "a4长度为:" << sizeof(a4) / sizeof(a4[0]) << "\n";
return 0;
}

输出

1
2
3
4
a1长度为:3
a2长度为:4
a3长度为:4
a4长度为:6

数组不允许直接拷贝和赋值。【一些编译器支持,这就是编译器扩展,但是可移植性不强…】
理解数组声明的意义,最好从数组的名字开始按照由内到外顺序阅读。

数组下标定义为size_t类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream>
#include<fstream>

using namespace std;

int main() {
int a[] = { 0, 1, 2, 3, 4, 5 };
int* pa = a;
int* pa2 = &a[0];

auto pa3(a); //整型指针,指向a的第一个元素
//pa3 = 12; //pa3是整型指针,不能用int赋值。

decltype(a) a_ = { 1, 3, 5, 7, 9 };

string b[] = { "a", "bb", "ccc" };
//int* pb = b; //报错
string* pb2 = b;

cout << "pa = " << pa << endl;
cout << "pa2 = " << pa2 << endl;
cout << "pa3 = " << pa3 << endl;
cout << "a_ = " << a_ << endl;
cout << "pb2 = " << pb2 << endl;

return 0;
}
1
2
3
4
5
pa = 004FFC88
pa2 = 004FFC88
pa3 = 004FFC88
a_ = 004FFC44
pb2 = 004FFBE8

vector string的迭代器支持的运算,数组的指针全部支持。
数组头地址的获取上面有示例,数组尾么有接触过,是这么得到尾元素后那个并不存在的元素的地址:

1
int *e = &arr[10];

不能对尾后指针执行解引用或递增的操作。

okok更安全简单的获取指针的方法来啦。begin & end,这两个函数是不是看起来很熟悉?他们在容器中也出现了。
用法:

1
2
3
int a[] = {0, 1, 2, 3, 4, 5};
int *beg = begin(a);
int *last = end(a);
1
2
auto n = end(arr) - begin(arr);
//结果类型为ptrdiff_t
  • 11.28

结构体

指针的类型必须与指向的地址的变量的类型一致。

指针用箭头->来指;
解引用后用.来指。

1
2
3
Stu *pStu = &Stu1;
(*pStu).name = "Wang";
pStu->name = "Kun";

函数的重载和覆盖

string

  • 提取子字符串
  • 比较字符串
  • 添加字符串
  • 搜索字符串

最近被boss限制了代码风格,要求用对应大括号而不是尾后括号,所以改一下风格…

可以在声明某个类的同时立刻创建一些该类的对象:

1
2
3
4
class Car
{
···
}car1, car2;

但是!!!应该避免这种做法

构造器与析构器

  • 构造器名称必须与它所在的类的名字一样
  • 系统在创建某个类的实例时,会第一时间自动调用这个类的构造器
  • 构造器永远不会返回任何值
  • 构造器不写也会自动生成
  • 析构器不带参数,也不会返回任何值
    一般来说,构造器用来完成事先初始化和准备工作(申请分配内存)
    析构器用来完成事后所必须的清理工作(清理内存)【java会自动清理,c++没有】

eg:

1
2
3
4
5
class Hello
{
Hello(void); //构造器
~Hello(); //析构器
}

this

解决二义性隐患

继承

  • 基类(父类、超类)
  • 子类

继承机制中的构造器与析构器稍复杂

关于endl和’\n’的区别

https://blog.csdn.net/u011675745/article/details/51939094

访问控制

  • public ==>允许任何代码访问
  • protected ==>只有这个类本身和它的子类可以访问
  • private ==>只有这个类本身可以访问

和java有点像喔…

跟着上面的练习更…

一些easy_test

读取文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<fstream>

using namespace std;

int main() {
ifstream in; //文件**输入流**的类
in.open("C:\\Users\\jy\\Desktop\\flag.txt");
//这两句可替换为:ifstream in("C:\\Users\\jy\\Desktop\\flag.txt");
if (!in) {
cerr << "打开文件夹失败!" << endl;
return 0;
}
char x;
while (in >> x) { //in每一次流一个字符到x
cout << x; //x再流到cout输出
}
cout << endl;
in.close();

return 0;
}
//会出现中文乱码,空格和回车没有流

flag.txt中是带空格的,可是没有打印出来哦~
ifstream 还可以接受不止一个参数

1
ifstream in(char* filename, int open_mode)

ios::in —— 打开一个可读取文件
ios::out —— 打开一个可写入文件
ios::binary —— 以二进制的形式打开一个文件
ios::app —— 写入的所有数据将被追加到文件的末尾【append】
ios::beg —— 使得文件指针指向文件头
ios::end —— 使得文件指针指向文件尾
ios::trunk —— 删除文件原来已存在的内容
ios::nocreate —— 如果要打开的文件不存在,那么以此参数调用open函数将无法进行
ios::noreplace —— 如果要打开的文件已存在,试图用open函数打开时将返回一个错误

可使用|来选择多种open_mode

复制文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>
#include<fstream>

using namespace std;

int main(int argc, char* argv[]) {
char sourcename[80], destname[80], buffer[256];
int n;
cout << "请输入要复制的文件名:" << endl;
cin >> sourcename;
cout << "请输入复制后的文件名:" << endl;
cin >> destname;
ifstream in;
ofstream out;
in.open(sourcename, ios::in | ios::binary);
out.open(destname, ios::out | ios::binary);
if (!in | !out) {
cerr << "打开失败!";
in.close();
out.close();
exit(1);
}
out << in.rdbuf();
out.close();
in.close();
cout << "复制完毕!" << endl;
return 0;
}

copyfile.png

第二种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
#include<fstream>

using namespace std;

int main(int argc, char* argv[]) {
if (argc != 3) {
cout << "输入形式: copyfile 源文件名 目标文件名" << endl;
exit(0);
}
ifstream in;
ofstream out;
in.open(argv[1], ios::in|ios::binary);
out.open(argv[2], ios::out|ios::binary);
if (!in | !out) {
cerr << "打开失败!";
exit(1);
}
out << in.rdbuf();
out.close();
in.close();
cout << "复制成功!" << endl;
return 0;
}

argc 加上本身一共传入的参数个数。CVE20190841终端运行
输入格式 提示
argv[] 每个指针指向命令行的一个字符串

字符串变大写!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
#include<fstream>

using namespace std;

int main() {
string s = "some thing";
for (auto i = s.begin(); i != s.end() /*&& !isspace(*i)*/; i++){
* i = toupper(*i);
cout << *i;
}

return 0;
}

输出

1
SOME THING

迭代器运算小🌰子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
#include<fstream>

using namespace std;

int main() {
string str1 = "abcdefg";
string str2 = "123456789";

auto mid = str1.begin() + str1.size() / 2;
cout << "mid = " << *mid << endl;

auto x = str1.begin();
x += 2; //我想直接使用 str1.begin() += 2 ,失败。因为str1.begin()是指针啊....【感谢delort】
cout << "str1_begin = " << *x << endl;

auto n = str2.begin() - str2.end(); //返回类型difference_type
cout << "str2头到尾距离 = " << n << endl;

return 0;
}

输出

1
2
3
mid = d
str1_begin = c
str2头到尾距离 = -9

//使用迭代器实现二分查找法也是不错的

构造器和析构器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<iostream>
#include<string>
#include<fstream>

class StoreFlag
{
public:
std::string flag, speaker;
std::ofstream fileOutput;

StoreFlag();
~StoreFlag();

void inputFlag();
void inputSpeaker();
bool write();
};

StoreFlag::StoreFlag()
{
fileOutput.open("flag.txt", std::ios::app); //append追加的形式打开,如果目录下没有flag.txt就新建
}

StoreFlag::~StoreFlag()
{
fileOutput.close();
}

void StoreFlag::inputFlag()
{
std::getline(std::cin, flag);
}

void StoreFlag::inputSpeaker()
{
std::getline(std::cin, speaker);
}

bool StoreFlag::write()
{
if (fileOutput.is_open())
{
fileOutput << flag << "——" << speaker << "\n";
return true;
}
else
return false;
}

int main()
{
StoreFlag flag;
std::cout << "请输入flag:\n";
flag.inputFlag();

std::cout << "请输入队伍名:\n";
flag.inputSpeaker();

if (flag.write())
std::cout << "写入成功!\n";
else
std::cout << "写入失败!\n";

return 0;
}



继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<iostream>
#include<string>

class Animal
{
public:
std::string mouth;

void eat();
void sleep();
void drool();
};

class Pig : public Animal
{
public:
void climb();
};

class Turtle : public Animal
{
public:
void swim();
};

void Animal::eat()
{
std::cout << "I am eating!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I am sleeping!" << std::endl;
}
void Animal::drool()
{
std::cout << "I am drooling!" << std::endl;
}
void Pig::climb()
{
std::cout << "A pig can climb" << std::endl;
}
void Turtle::swim()
{
std::cout << "A turtle can swim~" << std::endl;
}


int main()
{
Pig pig;
Turtle turtle;
pig.eat();
pig.climb();
turtle.sleep();
turtle.swim();

return 0;
}

继承.png

参考:小甲鱼c++入门学习视频