C++    2020-04-02 17:46:09    161    0    0
C++    2020-04-01 18:38:45    223    0    0

泛型函数:泛型函数的参数类型是我们事先不知道的——直到我们调用了这个函数我们才会得知。

0x01 模板函数

1.1 模板函数的定义

泛型函数的具体实现是通过模板函数的。

title

1.2 模板函数定义实例

原本的median函数:

title

这个函数计算的是 double 类型向量的中数,想通过改成模板函数使之「也可以取其他类型的向量的中值」:

title

1.3 另一个模板函数定义实例

title

0x02 迭代器

title

0x03 流迭代器

title
title

0x04 改进 split 函数

原本的 split 函数:

title

此函数的局限性:
上面这个split函数返回一个vector<string>类型的向量。但是这种做法具有局限性:用户可能不想要一个向量,相反他们可能希望得到一个list<string>类型的链表或者是其他种类的容器。而且split算法根本没有要求我们非要产生一个向量不可。

此函数的改进思路:
提高此函数的适应性。重写后的函数会有一个输出迭代器而不是返回一个值。我们将使用这个迭代器来输出split后的单词。我们的调用程序在调用前要把这个迭代器和放置函数的输出位置连接起来

改进的 split 函数:

title

  1. 通过模板头和模板参数的定义,改进后的split变成一个模板函数了。它有一个类型参数Out,这个参数的名称标明了它是一个输出迭代器。
  2. *os++ = string(i,j);这条语句输出了单个单词。子表达式*os指示了 os 被连接到的容器的当前位置,因此,我们把值string(i,j)赋给位于这个位置的元素。完成了赋值操作之后,我们对 os 加1,通过这样就满足了输出迭代器的要求,而且在下一个循环过程中函数会赋一个值给容器的下一个元素。
  3. split(s,ostream_iterator<string>(cout,"\n")); main 函数调用 split 来把输入行分割成独立的单词并把这些单词写到标准输出。我们通过传递一个 ostream_iterator 类型的迭代器给 split (传入到形参 os 的位置,os 本就应该是一个迭代器)从而把单词写到 cout —— 这里的迭代器会被连接到 cout。
C++    2020-03-31 18:12:27    258    0    0

0x01 支持高效查找的容器

之前提到的容器,vectorlist 都属于顺序容器。

  • vector:为了快速随机访问而被优化的,顺序访问的效率高。会自动扩缩容。但是如果要在向量内部删除或增加元素,就必须移动位于被插入或删除的元素后面的所有元素,这样效率很低。

  • list:可以让我们在容器的任何位置插入和删除元素。vector 支持索引,但 list 不支持索引,但 list 支持迭代器。

顺序容器对于元素顺序排列,不利于高效查找。而关联容器根据元素自身的值自动安排序列,更利于高效查找。

最常见的一种关联容器结构存储了「键-值」对。这样的一个数据结构被称为「关联数组」。关联数组是库的一部分。在C++中最常用的一种关联数组是map(映射表)。它定义在<map>头中。

可以联想到,Python中最常用的一种关联数组是字典。

title

0x02 计算单词数

title

迭代器类型

每一种标准容器,例如向量,都定义了两种相关的迭代器类型:

  1. container-type::const_iterator
  2. container-type::iterator

container-type指的是诸如vector<Student_info>这样的容器类型,它包括了容器元素的类型。如果我们想用一个迭代器来修改存储在容器中的值,使用iterator类型;如果我们仅仅需要读操作,那么使用const_iterator类型。

上面代码中的for循环中只是读取并顺序输出,无需修改map容器中的值,所以是const_iterator类型。

map容器

title

title
title

0x03 产生一个交叉引用表

现在想要编写程序来产生一个交叉引用表从而指示每一个单词在是在输入的哪些行出现的。

设计为:

  1. map<string,vector<int> >
  2. //注意两个 > 之间应该隔一个空格,不然编译器会误认为是 >> 符号
  • vector<int> 存储出现的那些行数。

程序设计

title

一个注意点

title

缺省参数

title

存入行数

title

完整代码

title
title
title
title

或者:

  1. #include <map>
  2. #include <iostream>
  3. #in
C++    2020-03-30 17:05:54    179    0    0

算法就是一些函数,<algorithm>头文件定义了很多算法。

泛型算法和迭代器适配器

title

title

优先级

title

back_inserter

title

find_if

title

小结

title

C++    2020-03-30 17:02:36    14    0    0

0x00 前言

C++的标准库提供了有用的数据结构和函数,还提供了很多库容器。

title

库容器的行为特征具有一致性。

本章就重点学习C++的库容器。

0x01 按类别来区分学生

需求升级:现在我们不仅希望算出学生的成绩,同时还想知道哪些学生不能通过这一门成绩。

先写一个简单的判断一个学生是否不及格的函数:

  1. //判断学生是否不及格
  2. bool fgrade(const Student_info& s) //读入 Student_info 结构的引用,不改变其值
  3. {
  4. return grade(s) < 60;
  5. }

解决思路:逐个检查所有学生的记录,然后在两个向量(通过&非通过)中选择一个来存放它。这两个向量中的一个是用来存储成绩及格的学生记录的,另一个则为成绩不及格的学生记录而设。

  1. //第一次尝试,把及格和不及格的学生记录分离开来
  2. vector<Student_info> extract_fails(vector<Student_info>& students)
  3. {
  4. vector<Student_info> pass, fail;
  5. for (vector<Student_info>::size_type i = 0; i != students.size(); ++i)
  6. {
  7. if (fgrade(students[i]))
  8. fail.push_back(students[i]);
  9. else
  10. pass.push_back(students[i]);
  11. }
  12. students = pass;
  13. return fail;
  14. }

代码思路:

title

就地删除元素

title

erase(students.begin()+i)

title

在循环中不用给 i 加一:

title

0x02 迭代器

title

迭代器类型

title

一个迭代器实例

title

title

迭代器操作

从迭代器取元素

title

间接引用*和->

title

0x03 用迭代器来代替索引

原索引:

  1. vector<Student_info> extract_fails(vector<Stu
2020-03-28 15:59:26    158    0    0

0x01 定位域控

通过KDC服务确定域内机器是否为域控

Kerberos 协议中主要有三个角色:
1. 访问服务的 Client
2. 提供服务的 Server
3. KDC(Key Distribution Center)密钥分发中心
KDC 默认安装在域控中,而 Client 和 Server 为域内的用户或者服务,如 web 应用、数据库服务器和邮件服务器等。Client 是否有权限访问 Server 端的服务由 KDC发放的票据来决定。

自然想到,确定一台机器是域控的方法之一:
通过 KDC 服务。

下面这台就是一个域控:

在 CS Beacon Shell 里:

  1. shell net start //查看服务

title

下面这台就不是域控:

title

没有 Kerberos Key Distribution Center 服务。虽然有个 DNS 服务,但是是 client 客户端不是 server 服务端。


通过 DNS Server 确定域控

AD 很大程度上依赖 DNS 这类解析服务,所以域内内成员机器指向的 DNS 都是域控。

下面这台就是一个域控:

title

DNS 指向自身:本机 IP 地址是192.168.3.142,它的 DNS 也是192.168.3.142。

下面这台就不是域控:

title

本机IP : 100.100.100.104

域控/DNS 服务器 IP:100.100.100.100


通过时间服务器确定域控

域中的默认时间服务器是 DC。

title

那么 192.168.3.142 就是域控。

一个实例

先通过 net time 命令获取域控服务器的 FQDN

title

然后通过 ping 域控服务器的 FQDN 获知域控 IP:

title


0x02 横向移动

IPC+计划任务

进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或者信号的一些技术或方法。

title

2020-03-27 11:03:28    188    0    0

0x01 const常量

const常量的特点

  • 必须初始化
  • 不能被修改(其实是可以通过指针间接修改)
  • 对于常量是在编译时刻做的检查

const常量与宏的区别

  • 宏没有类型,不占内存空间
  • const 常量有类型,并且会做类型检查

修改const常量

利用指针实现修改 const 常量:

  1. const int nNum = 100;
  2. int *p = (int *)&nNum;
  3. *p = 10;
  4. cout << nNum << endl; //但是最终输出的还是100,为什么?

分析:编译器在处理const常量时,一旦const常量的值确定了,就会进行优化,认为所有用到const常量的地方的值都是不会变化的,在编译阶段就会把cout << nNum << endl这种语句给优化了。

  1. const int nNum = argc; //让编译器在编译阶段不知道 const 常量的值,就无法优化了
  2. int *p = (int *)&nNum;
  3. *p = 10;
  4. cout << nNum << endl; //可以实现修改

title

常量和指针

常量和指针的结合:

  • const Type * :指针可以修改,但是指向的内容不能被修改
  • Type const * :与上面的等价
  • Type * const :指针不可修改,但指向的内容可以被修改
  • const Type const * :指针和指向的内容都不能被修改
    常见用途:限定某些内容不能被修改
    char *strcpy( char *strDestination, const char *strSource ); //限定strSource是常量
    常量指针的转换:常量指针不能转换为普通指针
    char* p1 = NULL;
    const char* p2 = p1; //可以
    p1 = p2; //报错:“=”: 无法从“const char ”转换为“char

0x02 引用

  • 引用:引用是变量的别名
    引用必须初始化
    引用初始化时给变量名,不能是字面值常量(不能是int& n=1;
2020-03-26 21:13:43    272    0    0

0x01 输出

cout是一个输出流对象,可以连着写,会自动识别类型,并且不会自动换行:

  1. cout << "Hello" << "World";

如果想换行可以使用endl(是一个函数),该函数的定义如下:

  1. inline _CRTIMP ostream& __cdecl endl(ostream& _outs) { return _outs << '\n' << flush; }

相当于 << '\n' << flush

  1. cout << "Hello" << "World" << '\n' << flush; //flush也是函数(不是fflush)

0x02 cout 的格式控制

进制:输出八进制、十六进制数据

  1. int nNum = 16;
  2. cout << hex << nNum << endl;
  3. cout << oct << nNum << endl;
  4. cout << dec << nNum << endl;

title

手动设置标志和取消标志

  1. int nNum = 16;
  2. cout << nNum << endl; //默认是按照十进制输出的
  3. cout.setf(ios::hex, ios::basefield); //cout.setf()设置标志,设置为16进制
  4. cout << nNum << endl;
  5. cout.unsetf(ios::hex); //cout.unsetf()取消标志
  6. cout << nNum << endl;

title

  • std 是标准的命名空间,系统提供的,可以直接使用。cincout 之类的都在里面。

    setf函数就跟setprecision(设置精度)、setw(设置宽度)类似,只不过它是用于设置当前使用的进制的。


显示进制前缀
  • 如十六进制显示0x,八进制显示0
  1. cout.setf(ios::showbase);
  2. cout << hex << nNum << endl;
  3. cout << oct << nNum << endl;

title

科学计数法、显示正负号

  1. flo
2020-03-26 19:28:54    204    1    0

什么是 MSDN 以及为什么需要 MSDN?

MSDN 相当于一个字典,遇到什么不懂的函数可以查一查。

里面都有函数怎么使用的、很多细节,不懂的就查查,是最权威的。

MSDN 的安装

版本的选择上,最好装 2001 离线版的。因为现在高版本的库函数与早期的都有区别了,尤其是最近几个版本的 VS 都使用模板泛型编程,导致很难看懂了。

2001 离线版从此链接下载安装:

https://www.cctry.com/thread-5422-1-1.html

MSDN 的使用

打开之后直接搜索相关函数就行:

title

比如:

title

搜索结果可能出现多条,选择自己需要的那个。

比如我想搜索 cin.getline,不应该看这个:

title

因为这是泛型编程的。而我要查找的 cin.getline 是 istream 中的,于是就定位到 istream 中查找,如下图:

title

最终就找到了我想要的:

title

C++    2020-03-26 11:47:50    190    0    0

0x01 面向对象语言的特点

面向对象语言的特点:

  • 封装:数据和行为绑到一起
  • 继承:描述事物与事物之间的关系
  • 多态:不同的对象对同一件事物有不同的操作,不是编译时决定的是运行时决定的
  • 实例化:声明对象的过程叫实例化
  • 设计模式降低耦合性:把不变的代码和变化的代码分开。多态是设计模式的基础

C++ 设计初衷是面向对象的,弥补C的不足。

0x02 断言宏assert()

  • assert():执行到该行代码处,断言一定成立,如果不成立则弹错。
  • 注意:assert只是在 Debug 模式下起作用,并且不能替换检查语句。(意思是 debug 版编译的程序
    编译为 release 版它就不起作用了)
  • 作用:用于快速定位错误位置。

title

如果输入a时输入的小于10则会弹窗:

title

0x03 布尔类型

布尔类型:是C++特有的(C只有_Bool),只有0和1(非零)。

  • 小布尔:bool nRet = true; //false(这种是通常所说的小布尔)
  • 大布尔:
  1. #define BOOL int
  2. #define TRUE 1
  3. #define FALSE 0

0x04 命名空间

C++的三种作用域:

  • 局部作用域:函数作用域、块作用域
  • 名字空间作用域:命名空间(namespace)、全局作用域
  • 类域

命名空间的作用:

避免不经意的名字定义冲突,以及使用库中相同的名字导致的冲突。

命名空间的定义:

  1. namespace CR26
  2. {
  3. int testNum = 3;
  4. int m = 4;
  5. int k = 5;
  6. }

注:声明时候要放在函数的外面或者放到.h文件中,否则会报错:

title

命名空间的三种使用方式:

  1. using CR26::m; //方式一:using CR26::m;(使用命名空间中的m,相当于声明了m,下面可以使用m)
  2. printf("%d\r\n", m);
  3. printf("%d\r\n", CR26::k); //方式二:CR26::k(直接使用命名空间中的k)
  4. using namespace CR26; //方式三:using namespace CR26;(可以使用命名空间中的