C++深入理解 面向对象部分

一、C++流的控制

1.1 输入输出流相关的类

  输入输出重定向例子,

#include <iostream>
using namespace std;
int main(){int x, y;cin >> x >> y;freopen("test.txt", "w", stdout); // 从t.txt读数据if(y == 0)cerr << "error." << endl;elsecout << x/y;return ;
}
/*
t.txt
3.14 123
输出:
3.14 123
*/

  判断输入流结束。怎么知道 cin 读取文件数据什么时候结束了。用循环方式读入。从文件读 freopen(“some.txt”, “r”, stdin); 那么读到文件尾部,输入就算结束。
  istream 类的成员函数,

istream & getline(char * buf, int bufSize); // 读取 bufSize-1 字符,‘\n'结束
istream & getline(char * buf, int bufSize, char delim); // 遇到 delim 字符结束读入都会自动添加 ’\0',可以用 if(!cin.getline()) 判断是否结束
bool eof(); // 判断输入流是否结束
int peek(); // 返回下一个字符,但不从流中去掉
istream & putback(char c); // 将字符 ch 放回输入流
istream & ignore(int nCount = 1, int delim = EOF); // 从流中删掉最多 nCount 个字符,遇到 EOF 时结束。

  举个例子,

#include <iostream>
using namespace std;
int main(){int x, y;char buf[100];cin >> x;cin.getline(buf, 90);cout << buf << endl;return 0;
}/*
输入:
12 abcd 回车
输出:abcd // 注意是读入是字符输入:
12回车
程序立即结束,输出:(什么都没有) // 注意读入字符
*/

1.2 流操纵算子控制输出格式

  流操纵算子,设置数据输出格式,使用流操作算子需要 #include < iomanip >.比如,

int n = 10;
cout << n << endl;
cout << hex << n << "\n"<< dec << n << "\n"<< oct << n << endl; // 16进制,10进制,8进制输出。知道用另外一个流操作算子,那样作用才会消失

  控制浮点数精度的流操作算子,

precision, setprecision
-> precision 是成员函数, 调用方式为: cout.precision(5);
-> setprecision 是流操作算子,调用方式为: cout << setprecision(5); // 可以连续输出他们功能相同。
指定输出浮点数的有效位数(非定点方式输出时)
指定输出浮点数的小数点后的有效位数(定点方式输出时)
定点方式:小数点必须出现在个位数后面double x = 1234567.89, y = 12.34567;
int n = 1234567;
int m = 12;
// 浮点数输出最多 6 位有效数字
cout << setprecision(6) << x << endl<< y << endl << n << endl << m;输出为:
1.23457e+006
12.3457
1234567
12// 以小数点位置固定的方式输出
cout << setiosflags(ios::fixed) << setprecision(6) << x << endl<< y << endl << n << endl << m;
输出为:
1234567.890000
12.345670
1234567
12// 取消以小数点位置固定方式输出
cout << setiosflags(ios::fixed) << setprecision(6) << x << endlresetiosflags(ios::fixed) << x;
输出为:
1234567.890000
1.23457e+006

  设置域宽的流操纵算子
—> 设置域宽(setw, width)

int w = 4;
char string[10];
cin.width(5);
while(cin >> string){cout.width(w++);cout << string << endl;cin.width(5);
}
// 宽度设置有效是一次性的,在每次读入和输出之前都要设置宽度
// 缺省情况下,宽度不足,右侧补空格,满足宽度
// 设置域宽时候包含结尾的'\0'
输入:
1234567890
输出:
1234567890

  一个综合的例子,

#include <iostream>
#include <iomanip>using namespace std;
int main(){int n = 141;// 1) 分别以十六进制、十进制、八进制先后输出 ncout << "1)" << hex << n << " " << dec << n << " " << oct << n << endl;double x = 1234567.89, y = 12.34567;// 2) 保留 5 位有效数字cout << "2)" << setprecision(5) << x << " " << dec << y << " " << " " << endl;// 3) 保留小数点后面 5 位,定点方式输出cout << "3)" << fixed << x << setprecision(5) << x << " " << y << endl;// 4) 科学计数法输出,且保留小数点后面 5 位cout << "4)" << scientific << setprecision(5) << x << " " << y << endl;// 5) 非负数要显示正号,输出宽度为 12 字符,宽度不足则用 '*' 填补cout << "5)" << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;// 6) 非负数不显示正号,输出宽度为 12 字符,宽度不足则右边用填充字符填充cout << "6)" << noshowpos << setw(12) << left << 12.1 << endl;// 7) 输出宽度为 12 字符,宽度不足则左边用填充字符填充cout << "7)" << setw(12) << right << 12.1 << endl;// 8) 宽度不足时,负号和数值分列左右,中间用填充字符填充cout << "8)" << setw(12) << internal << -12.1 << endl;cout << "9)" << -12.1 << endl;return 0;
}

结果如下,

  用户自定义流操纵算子,

ostream &tab(ostream & output){return output << '\t';
}
// tab 是函数,当作流操纵算子
cout << "a" << tab << "b" << end;
输出:
aa  bb
为什么可以?
函数要满足,参数为 引用,输出也为 引用

因此,定义方式为,

iostream 里对 << 进行了重载(成员函数)ostream & operator
<< (ostream &(*p)(ostream &));该函数内部会调用 p 所指向的函数,且 *this 作为参数 hex, dec, oct 都是函数,且以 *this 作为参数

1.3 文件读写一

  可以将顺序文件看作一个有限字符构成的顺序字符流,然后像对 cin, cout 一样的读写。回顾一下输入输出流类的结构层次。
  包含头文件,#include < fstream >, 如下,

ofstream outFile("clients.dat", ios::out|ios::binary);
--"clients.dat": 要创建的文件名字
--ios::out:      文件的打开方式.ios:out    输出到文件,删除原有内容.ios::app   输出到文件,保留原有内容,总是在尾部添加
--ios::binary:   以二进制文件格式打开文件

  创建文件
------》也可以先创建 ofstream 对象,再用 open 函数打开
  ofstream fout;
  fout.open(“test.out”, ios::out | ios::binary);
------》判断打开是否成功:
  if(! out){
  cout << “File open error! << endl” }
------》文件名可以给出绝对路径,也可以给出相对路径。没有交代路径信息,就是在当前文件夹下找文件。
  文件名的绝对路径和相对路径,

绝对路径:
"c:\\tmp\\mydir\\some.txt"
相对路径:
"\\tmp\\mydir\\some.txt"  当前盘符的根目录下的 tmp\dir\some.txt
"..\\tmp\\mydir\\some.txt"  当前文件夹的父文件夹下面的tmp子文件夹里面的...
"..\\..\\tmp\\mydir\\some.txt"  当前文件夹的父文件夹的父文件下面的tmp子文件夹里面的...

  文件的读写指针
------》对于输入文件,输出文件,输入输出文件,都有相应的指针,标识文件操作的当前位置,该指针在哪里,读写操作就在哪里进行。举个例子,

/*写指针例子*/
ofstream fout("a1.out", ios::app);  // 以添加方式打开
long location = fout.tellp();  // 取得写指针的位置
location = 10;  // 可以为 负
fout.seekp(location); // 将写指针移动到第 10 个字节处
fout.seekp(location, ios::beg); // 从头数 location
fout.seekp(location, ios::cur); // 从当前位置数 location
fout.seekp(location, ios::end); // 从尾部数 location/*读指针例子*/
ifstream fin("a1.in", ios::ate);  // 打开文件,定位文件指针到文件尾
long location = fout.tellg();  // 取得读指针的位置
location = 10L;  // 可以为 负
fout.seekg(location); // 将读指针移动到第 10 个字节处
fout.seekg(location, ios::beg); // 从头数 location
fout.seekg(location, ios::cur); // 从当前位置数 location
fout.seekg(location, ios::end); // 从尾部数 location

  字符文件读写
------》因为文件流也是流,所以流的成员函数和流操作算子也同样适用于文件流。举个例子,

写一个程序,将文件 in.txt 里面的整数排序后,输出到 out.txt
例如,
若 in.txt 的内容为:1 234 9 45 6 879
生成 out.txt 的内容为:1 6 9 45 234 879

程序如下,

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>using namespace std;int main(){vector<int> v; // 可变长数组,都是 int 类型ifstream srcFile("in.txt", ios::in);ofstream destFile("out.txt", ios::out);int x;while(srcFile >> x) v.push_back(x); // 添加进去sort(v.begin(), v.end());for(int i = 0; i < v.size(); i++)destFile << v[i] << " ";srcFile.close();  // 处理完成以后一定要关闭destFile.close();return 0;
}

结果如下,

1.4 文件读写二

  二进制文件读
------》ifstream 和 fstream 的成员函数,
------》istream & read(char *s, long n);
将文件读指针指向的地方的 n 个字节内容,读入到内存地址 s ,然后将文件读指针向后移动 n 字节 (以 ios::in 方式打开文件时,文件读指针开始指向文件开头)
  二进制文件读
------》ifstream 和 fstream 的成员函数,
------》istream & write(const char *s, long n);
将内存地址 s 处的 n 个字节内容,写入到文件中写指针指向的位置,然后将文件写指针向后移动 n 字节(以 ios::out 方式打开文件时,文件写指针开始指向文件开头,以 ios::app 方式打开文件时,文件写指针开始指向文件尾部)。
  具体例子:在文件中写入和读取一个整数

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>using namespace std;int main(){// 二进制文件ofstream fout("some.dat", ios::out | ios::binary);int x = 120;fout.write((const char *)(&x), sizeof(int));fout.close();ifstream fin("some.dat", ios::in | ios::binary);int y;fin.read((char *) &y, sizeof(int));fin.close();cout << y << endl;return 0;
}

  具体例子:从键盘输入几个学生的名字的成绩,并以二进制文件形式保存

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>using namespace std;struct Student{char name[20];int score;
};
int main(){Student s;ofstream OutFile("D:\\GraduateLevelFile\\Program\\C++\\Advance\\Deep\\students.dat", ios::out | ios::binary);while(cin >> s.name >> s.score)OutFile.write((char * ) &s, sizeof(s));OutFile.close();return 0;
}
输入:
Tom 60
Jack 80
Jane 40
输出:
形成的students.dat为72字节,用记事本打开,呈现的是乱码接着读取,显示出来,
// 接着再读取出来,程序如下
int main(){Student s;ifstream inFile("Students.dat", ios::in | ios::binary);if(!inFile){cout << "error" << endl;return 0;}while(inFile.read((char*) &s, sizeof(s))){int readedBytes = inFile.gcount(); // 看读了多少个字节cout << s.name << " " << s.score << endl;}inFile.close();return 0;
}
输出:
Tom 60
Jack 80
Jane 40

  具体例子:二进制文件读写,将students.dat文件的Jane改成Mike

// 将students.dat文件的Jane改成Mike
int main(){Student s;fstream ioFile("D:\\GraduateLevelFile\\Program\\C++\\Advance\\Deep\\students.dat", ios::in | ios::out | ios::binary);if(!ioFile){cout << "error" << endl;return 0;}ioFile.seekp(2*sizeof(s), ios::beg); // 定位写指针到第三个记录ioFile.write("Mike", strlen("Mike")+1); // 结尾 '\0' 写进去ioFile.seekg(0, ios::beg); // 定位读指针到开头while(ioFile.read((char*) &s, sizeof(s))){cout << s.name << " " << s.score << endl;}ioFile.close();return 0;
}
输出:
Tom 60
Jack 80
Mike 40

  总的来说,为什么存为二进制文件,因为节省空间。因为二进制方式存储,字节数固定查找的比较快,比直接存储方式方便很多

  具体例子:文件拷贝程序 mycopy 示例

/* mycopy src.dat dest.dat即将 src.dat 拷贝到 dest.dat 如果 dest.dat 原来就有,则原来的文件就会被覆盖
*/
#include <iostream>
#include <fstream>using namespace std;int main(int argc, char *argv[]){if(argc != 3){cout << "File name missing!" << endl;return 0;}// src.datifstream inFile(argv[1], ios::binary | ios::in); // 打开文件用于读if(! inFile){cout << "Source file open error." << endl;return 0;}// dest.datofstream outFile(argv[2], ios::binary | ios::out); // 打开文件用于写if(!outFile){cout << "New file open error." << endl;inFile.close(); // 打开的文件一定要关闭return 0;}char c;while(inFile.get(c)); // 每次读取一个字符outFile.put(c);  // 每次写入一个字符outFile.close();inFile.close();return 0;
}

  二进制文件和文本文件的区别,

Linux, Unix 下的换行符号:'\n' (ASCII码: 0x0a)
Windows 下的换行符号:'\r\n' (ASCII码: 0x0d0a) endl 就是 '\n'
Mac OS 下的换行符号:'\r' (ASCII码: 0x0d)
导致 Linux, Mac OS 文本文件在 Windows 记事本中打开时不换行在 Unix/Linux 下打开文件,用不用 ios::binary 没区别
Windows 下打开文件,如果不用 ios::binary, 则:读取文件时,所有的'\r\n'会被当作一个字符'\n'处理,即少读一个字符'\r'写入文件时,写入单独的'\n'时,系统自动在前面加一个'\r',即多谢一个'\r'所以用二进制方式读写,会造成很大的麻烦

二、函数模板和类模板

2.1 函数模板

  比如交换两个整型变量的值,写一个 Swap 函数,对于不同类型数据的交换,都要写一个相应的函数,这样就很麻烦,因此引入函数模板。解决方法如下,

template <class 类型参数1, class 类型参数2, ......>
返回值类型 模板名(形参表){函数体
};template <class T>
void Swap(T & x, T & y){T temp = x;x = y;y = temp;
}函数模板中可以有不止一个类型参数,
tempate <class T1, class T2>
T2 print(T1 arg1, T2 arg2){cout << arg1 << " " << arg2 << endl;return arg2;
}

  举个例子,求数组最大值的函数,

template <class T>
T maxElement(T a[], int size) // size 是数组元素个数
{T tempMax = a[0];for(int i = 1; i < size; ++i){if(tempMax < a[i])tempMax = a[i];}return tempMax;
}// 再例如,不通过参数实例化函数模板
T Inc(T n){return 1 + n;
}
int main(){cout << Inc<double>(4)/2; // 输出 2.5return 0;
}

  同时,函数模板是可以重载的,只是他们的形参表或类型参数表不同即可。**函数模板和函数的次序:**先找完全匹配普通函数,再找参数完全匹配的模板函数,最后再找经过自动类型转换后能够匹配的普通函数。举个例子,

template <class T>
T Max(T a, T b){cout << "TemplateMax" << endl; return 0;
}
template <class T, class T2>
T Max(T a, T2 b){cout << "TemplateMax2" << endl; return 0;
}
double Max(double a, double b){cout << "Mymax" << endl; return 0;
}int main(){int i = 4, j = 5;Max(1.2, 3.4); // 输出 MymaxMax(i, j); // 输出 TemplateMaxMax(1.2, 3); // 输出 TemplateMax2return 0;
}

  匹配函数模板时,不进行类型自动转换。再举个例子,

#include <iostream>
using namespace std;template <class T, class Pred>
void Map(T s, T e, T x, Pred op){for(; s != e; ++s, ++x){*x = op(*s);}
}
int Cube(int x){return x * x * x;}
double Square(double x){return x * x;}int a[5] = {1, 2, 3, 4, 5}, b[5];
double d[5] = {1.1, 2.1, 3.1, 4.1, 5.1}, c[5];
int main(){Map(a, a+5, b, Square);/* 实例化以下函数// double (*op)(double) 是函数指针void Map(int * s, int * e, int *x, double (*op)(double)){for(; s != e; ++s, ++x) *x = op(*s);}*/for(int i = 0; i < 5; ++i)cout << b[i] << ",";cout << endl;Map(a, a+5, b, Cube);/* 实例化以下函数// double (*op)(double) 是函数指针void Map(int * s, int * e, int *x, double (*op)(double)){for(; s != e; ++s, ++x) *x = op(*s);}*/for(int i = 0; i < 5; ++i)cout << b[i] << ",";cout << endl;Map(d, d+5, c, Square);/* 实例化以下函数// double (*op)(double) 是函数指针void Map(int * s, int * e, int *x, double (*op)(double)){for(; s != e; ++s, ++x) *x = op(*s);}*/for(int i = 0; i < 5; ++i)cout << c[i] << ",";cout << endl;return 0;
}

  结果如下,

2.2 类模板

  为了多快好省地定义出一批相似的类,可以定义类模板,然后由类模板生成不同的类。一个类的元素和函数,使用模板方式实例化以后,可以自动生成各种类型的元素和函数。定义如下,

template <typename 类型参数1, typename 类型参数2, ......>
// 类型参数表
class 类模板名{成员函数和成员变量
};// 类模板成员函数写法
template <class 类型参数1, class 类型参数2, ......> // 类型参数表
返回值类型 类模板名 <类型参数名列表>::成员函数名(参数表){......
}// 用类模板定义对象的写法:
类模板名 <真实类型参数表> 对象名(构造函数实参表);

  举个例子,

#include <iostream>
using namespace std;template <class T1, class T2>
class Pair{public:T1 key;T2 value;Pair(T1 k, T2 v):key(k), value(v){};// 符号 < 重载bool operator < (const Pair <T1, T2> & p) const;
};
template <class T1, class T2>
// Pair 的成员函数 operator <
// Pair <T1, T2> 类的名字
bool Pair <T1, T2>::operator <(const Pair <T1, T2> & p) const{return key < p.key; // 谁的 key 小谁算小
}int main(){Pair <string, int> student("Tom", 19);// 实例化出一个类 Pair <string, int>cout << student.key << " " << student.value;return 0;
}

  结果如下,

  编译器由类模板生成类的过程叫做类模板的实例化。有类模板实例化得到的类,叫做模板类。同一个类模板的两个模板类是不兼容的,比如

Pair <string, int> *p;
Pair <string, double> a;
p = & a; // 会出错,不兼容

  函数模板可以作为类模板成员

#include <iostream>
using namespace std;template <class T> // 没用到也不影响
class A{public:template <class T2>void Func(T2 t){cout << t;}  // 成员函数模板
};
int main(){A <int> a;a.Func('K'); // 成员函数模板 Func 被实例化 chara.Func("hello"); // 成员函数模板 Func 再此被实例化 char *return 0;// 输出为: Khello
}

  类模板与非类模板
  类模板的 “<类型参数表>” 中可以出现非类型参数:

// int size 是非类型参数
template <class T, int size>
class Array{T array[size];public:void Print(){for(int i = 0; i < size; ++i){cout << array[i] << endl;}}
};
Array <double, 40> a1;
Array <double, 70> a2;  // a1 和 a2 属于不同的类
Array <int, 50> a3;  // a2 和 a3 属于不同的类

2.3 类模板与派生、友元

  类模板与继承

// 类模板从类模板派生
// 类模板从模板类派生
// 类模板从普通类派生
// 普通类从模板类派生// 1. 类模板从类模板派生
template <class T1, class T2>
class A{T1 v1; T2 v2;
};
template <class T1, class T2>
class B: public A<T2, T1>{T1 v3; T2 v4;
};
template <class T>
class C: public B<T, T>{T v5;
};int main(){B<int, double> obj1;C<int> obj2;return 0;
}// 2. 类模板从模板类派生
template <class T1, class T2>
class A{T1 v1; T2 v2;
};
template <class T>
class B: public A <int, double>{T v;
};
int main(){B <char> obj1; // 自动生成两个模板类: A <int, double> 和 B <char>return 0;
}// 3. 类模板从普通类派生
class A{int v1;
};
template <class T>
class B: public A{  // 所有从 B 实例化得到的类,都以 A 为基类T v;
};
int main(){B <char> obj1;return 0;
}// 4. 普通类从模板类派生
template <class T>
class A{T v1; int n;
};
class B: public A<int>{double v;
};
int main(){B obj1;return 0;
}

  类模板与友元

// 函数、类、类的成员函数作为类模板的友元
// 函数模板作为类模板的友元
// 函数模板作为类的友元
// 类模板作为类模板的友元// 1. 函数、类、类的成员函数作为类模板的友元
void Func1(){}
class A{};
class B{public:void Func(){}
};
template <class T>
class Temp1{friend void Func1();friend class A;friend class B::Func();
}; // 任何从 Temp1 实例化来的类,都有以上三个友元// 2. 函数模板作为类模板的友元
#include <iostream>using namespace std;template <class T1, class T2>
class Pair{private:T1 key;  // 关键字T2 value;  // 值public:Pair(T1 k, T2 v):key(k), value(v){};bool operator < (const Pair <T1, T2> & p)const;template <class T3, class T4> // 新的模板friend ostream & operator << (ostream & o,const Pair <T3, T4> &p);
};
template <class T1, class T2>
bool Pair <T1, T2>::operator < (const Pair <T1, T2> & p)const{// “小” 的意思就是关键字小return key < p.key;
}
template <class T1, class T2> // 与之前模板一样
ostream & operator << (ostream & o,const Pair <T1, T2> & p){o << "(" << p.key << "," << p.value << ")";return o;
}int main(){Pair <string, int> student("Tom", 29);Pair <int, double> obj(12, 3.14);cout << student << " " << obj;return 0;
}
输出为:
(Tom,29) (12,3.14)注意:任意从 template <class T1, class T2>
ostream & operator << (ostream & o,const Pair <T1, T2> & p)
生成的函数,都是任意 Pair 模板类的友元// 3. 函数模板作为类的友元
#include <iostream>using namespace std;
class A{int v;public:A(int n):v(n){}template <class T>friend void Print(const T & p);
};
template <class T>
void Print(const T & p){cout << p.v;
}
int main(){A a(4);Print(a);return 0;
}
输出:
4
注意:所有从 template <class T>
void Print(const T & p) 生成的函数,都成为 A 的友元
但是自己写的函数
void Print(int a){}
不会成为 A 的友元// 4. 普通类从模板类派生
#include <iostream>using namespace std;
template <class T>
class B{T v;public:B(T n):v(n){}template <class T2>friend class A;
};
template <class T>
class A{public:void Func(){B <int> o(10);cout << o.v << endl;}
};int main(){A <double> a;a.Func();return 0;
}
输出:
10
A <double> 类,成为 B <int> 类的友元。
任何从 A 模板实例化出来的类,都是任何 B 实例化出来的类的友元

2.4 类模板与静态成员变量

  类模板与 static 成员
  类模板中可以定义静态成员,那么从该类模板实例化得到的所有类,都包含同样的静态成员。比如,

#include <iostream>using namespace std;template <class T>
class A{private:static int count;public:A(){count ++;}~A(){count --;}A(A &){count ++;}static void printCount(){cout << count << endl;}
};
// 在 dev c++ 要写 template<>
template<> int A<int>::count = 0;
template<> int A<double>::count = 0;
int main(){A <int> ia;A <double> da;ia.printCount();da.printCount();return 0;
}
输出:
1
1

三、string类

  string 类是模板类,使用 “string” 类要包含头文件 < string >,string 对象初始化:

// 正确初始化方法
string s1("Hello");
string month = "March";
string s2(8, 'x');// 错误初始化方法
string s1 = 'c';
string s2('u');
string s3 = 22;
string s4(8);// 但是可以将字符赋值给 string 对象
string s
s = 'n';

  string 类支持

// 常用
1. string 对象的长度用函数 length() 读取
2. string 支持流读取运算符
3. string 支持 getline 函数 // string s; getline(cin, s);
4. string 对象可以用 = 赋值
5. string 用 assign 成员函数复制 // string s1("cat"), s3; s3.assign(s1);
6. string 用 assign 成员函数部分复制 // string s1("catpig"), s3; s3.assign(s1, 1, 3);
7. string 单个字符复制 // s2[5] = s1[3] = 'a';
8. string 逐个访问 string 对象中的字符 // string s; s.at(i)... at 会越界检查
9. string 用 + 运算符连接字符串
10. string 用成员函数 append 连接字符串 // string s1, s2; s1.append(s2, 1, s2.size())
11. string 可以比较大小,返回值为 bool 类型
12. string 用成员函数 compare 比较 string 大小 // string s1, s2; s1.compare(s2, 1, 3)
13. string 成员函数 substr, 取字串 // string s1, s2; s2 = s1.substr(3, 4)
14. string 成员函数 swap, 交换两个字符串 // s1.swap(s2)
15. string 成员函数 find, 查找一个字符串位置,返回首部下标,找不到返回string::npos也可以选择从某一个位置开始找 // string s; s.find("abc", 1);
16. string 成员函数 rfind, 从后往前找,也是返回首部下标,找不到返回string::npos// 不常用
17. string find_first_of,查找字符串中任何一个字符第一次出现位置,找不到返回string::npos
18. string find_last_of,查找字符串中任何一个字符最后一次出现位置,找不到返回string::npos
19. string find_first_not_of
20. string find_last_not_of

  对字符串进行操作的函数

/*注意字符下标是从 1 开始,不是从 0 开始*/
// 删除 string 中的字符
erase() // s1.erase(5) 删除下标 5 及以之后的字符// 替换 string 中的字符
replace // s1.replace(2, 3, "haha") 将 2-3 下标字符替换成 "haha"// s1.replace(2, 3, "haha", 1, 2) 将 2-3 下标字符替换成 "haha" 中 1-2 字符// 在 string 插入字符
insert // s1.insert(5, s2) 从 5 开始插入 s2// 转换成 C 语言 char* 字符串
c_str()   data()
string s1("hello world");
printf("%s\n", s1.c_str); // 转化为 const char * 类型字符串,以 '\0' 结尾

  字符串流处理,除了标准流和文件流输入输出外,还可以从 string 进行输入输出;类似 istream 和 ostream 进行标准流输入输出,我们用 istring 和 ostream 进行标准流输入输出,我们用 istringstream 和 ostringstream 进行字符串的输入和输出,也叫做内存输入输出。要包含头文件 #include < sstream >,举个例子,

#include <iostream>
#include <sstream>using namespace std;int main(){string input("Input test 123 4.7 A");istringstream inputString(input);  // 类实例化string str1, str2;int i; double d; char c;inputString >> str1 >> str2 >> i >> d >> c;cout << str1 << endl << str2 << endl;cout << i << endl << d << endl << c << endl;long L;if(inputString >> L)cout << "long\n";else cout << "empty\n";
}

结果如下

输出到屏幕方式如下,

ostringstream outputString;
int a = 10;
outputString << "This" << a << "ok" << endl;
cout << outputString.str();输出为:
This 10ok

四、标准模板库 STL

  C++ 语言核心优势之一就是便于软件的重用
  C++ 中有两个方面体现重用:1. 面向对象的思想:继承和多态,标准类库;2. 泛型程序设计(generic programming) 的思想:模板机制,以及标准模板库 STL。
  泛型程序设计,将常用数据结构和算法写成模板,不论数据和数据结构放什么对象,算法针对什么对象,都不必实现数据结构,重新编写算法。
  标准模板库(Standard Template Library) 就是一些常用 数据结构和算法模板的集合。并且有非常高的性能。

4.1 简述

  基本概念

容器可以用来存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模板
分为:
1) 顺序容器
vector 动态数组  deque 双向队列  list 双向链表
2) 关联容器
set multiset  map multimap // 都是排序的
2) 容器适配器
stack 栈 queque 队列 priority_queque 优先级队列

  对象被插入容器时,被插入的是对象的一个复制品。对容器的对象所属的类,往往还要重载 == 和 < 运算符等。

4.2 迭代器

  迭代器的基本属性,
----》用于指向顺序容器和关联容器中的元素
----》迭代器用法和指针类似
----》有 const 和非 const 两种
----》通过迭代器可以读取它指向的元素
----》通过非 const 迭代器还能修改其指向的元素
  定义一个容器类的迭代器方法可以是,

容器类名::iterator 变量名;
或者
容器类名::const::iterator 变量名;访问一个迭代器指向的元素:
* 迭代器变量名

  举个例子,

#include <vector>
#include <iostream>using namespace std;int main(){vector <int> v; // 一个存放 int 元素的数组,一开始里面没有元素v.push_back(1);  v.push_back(2); v.push_back(3); v.push_back(4);// 常量迭代器不能对元素操作vector <int> :: const_iterator i; // 常量迭代器for( i = v.begin(); i != v.end(); ++i){cout << *i << ","; // 迭代器看作指针}cout << endl;vector <int> :: reverse_iterator r; // 反向迭代器// 反向遍历for( r = v.rbegin(); r != v.rend(); ++r){cout << *r << ","; // 迭代器看作指针}cout << endl;// 非常量迭代器可以对元素进行操作vector <int> :: iterator j; // 非常量迭代器for( j = v.begin(); j != v.end(); ++j){*j = 100; // 迭代器看作指针}for( j = v.begin(); j != v.end(); ++j){cout << *j << ","; // 迭代器看作指针}cout << endl;
}

结果如下,

双向迭代器
  若 p 和 p1 都是双向迭代器,则可对 p 和 p1 进行操作,

++p, p++; --p p--; *p; p = p1; p == p1 p != p1

随机访问迭代器
  若 p 和 p1 都是随机访问迭代器,则可对 p 和 p1 进行操作,

p += i; p -= i; p + i;
p + i; //指向 p 后面的第 i 个元素的迭代器
p - i; // 指向 p 前面的第 i 个元素的迭代器
p[i]; // p 后面的第 i 个元素的引用
p < p1, p <= p1, p > p1, p >= p1

不同容器迭代器类型

容器             容器上的迭代器类别
vector          随机访问
deque           随机访问
list            双向
set/multiset    双向stack           不支持迭代器
queue           不支持迭代器
priority_queue  不支持迭代器

有的算法,例如 sort, binary_search 需要通过随机访问迭代器来访问容器中的元素,那么 list 以及关联容器就不支持该算法。

vector(deque 也一样) 的迭代器是随机访问迭代器,遍历方法为

vector <int> v(100);
int i;
for(int i = 0; i < v.size(); i++)cout << v[i]; // 根据下标随机访问vector <int>::const_iterator ii;
for(ii = v.begin(); ii != v.end(); ii++)cout << *ii;
for(ii = v.begin(); ii < v.end(); ii++)cout << *ii; // 间隔输出
ii = v.begin();
while(ii < v.end())cout << *ii; ii += 2;

list 的迭代器是双向迭代器,遍历方法为

list <int> v;
list <int>::const_iterator ii;
for(ii = v.begin(); ii != v.end(); ii++)cout << *ii;// 错误方法
// 因为双向迭代器不支持 <, list 没有成员函数 []
for(ii = v.begin(); ii < v.end(); ii++)cout << *ii;
for(ii = v.begin(); ii != v.end(); ii++)cout << v[i];

4.3 算法

  算法就是一个个函数模板,大多数在 < algorithm > 中定义
-----》STL 中提供了各种容器中通用的算法,比如查找,排序;
-----》算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器。比如排序和查找;
-----》有的算法返回一个迭代器。比如 find() ,返回一个指向该元素的迭代器;
-----》算法可以处理容器,也可以处理数组。
  一些例子,

template <class Init, class T>
find(Init first, Init last, const & T); // 在[first, last)区间查找 T,返回迭代器

  在 STL 中关联容器内部的元素是 从小到大 排列的。使用 STL 时,在缺省情况下,这三种等价:x比y小,表达式"x<y"为真,y比x大。举个例子,STL中“相等”概念演示,

#include <iostream>
#include <algorithm>
using namespace std;class A{int v;public:A(int n):v(n){}bool operator < (const A & a2)const{cout << v << "<" << a2.v << "?" << endl;return false;}bool operator == (const A & a2)const{cout << v << "==" << a2.v << "?" << endl;return v == a2.v;}
};
int main(){A a[] = {A(1), A(2), A(3), A(4)};cout << binary_search(a, a + 4, A(9)); // 折半查找return 0;
}

结果如下,

为什么最后结果是 1 ,因为与 STL 判断机制有关。比如 a > b || a < b 不成立,那么就认为 a = b。

4.4 vector, deque 和 list

vector 示例

#include <iostream>
#include <vector>using namespace std;template <class T>
void printVector(T s, T e){// 从头遍历到尾for(; s != e; ++s)cout << * s << " ";cout << endl;
}int main(){int a[5] = {1, 2, 3, 4, 5};vector <int> v(a, a + 5); // 将数组 a 装入容器cout << "1)" << v.end() - v.begin() << endl;// 两个随机迭代器可以相减cout << "2)"; printVector(v.begin(), v.end());v.insert(v.begin() + 2, 13); // 在数组 2 位置插入 13cout << "3)"; printVector(v.begin(), v.end());v.erase(v.begin() + 2); // 删除位于 begin() + 2 的元素cout << "4)"; printVector(v.begin(), v.end());vector <int> v2(4, 100); // v2 有 4 个元素,都是 100v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3);// 将 v 的一段插入 v2 开头cout << "5) v2:"; printVector(v2.begin(), v2.end());v.erase(v.begin() + 1, v.begin() + 3);// 删除 v 上的一个区间,即 2, 3cout << "6)"; printVector(v.begin(), v.end());return 0;
}


vector 实现二维数组

#include <iostream>
#include <vector>using namespace std;int main(){vector < vector <int> > v(3); // v 有 3 个元素,每个元素都是 vector <int> 容器for(int i = 0; i < v.size(); ++i)for(int j = 0; j < 4; ++j)v[i].push_back(j);for(int i = 0; i < v.size(); ++i){for(int j = 0; j < v[i].size(); ++j)cout << v[i][j] << " ";cout << endl;}return 0;
}
结果如下:
0 1 2 3
0 1 2 3
0 1 2 3

**deque **
  所有 vector 都适用于 deque 操作。deque 还有 push_front(将元素插入到前面) 和 pop_front (删除最前面的元素)操作,复杂度是 o(1).

**list 实例 **
  在任何位置插入删除都是常数时间,不支持随机存取。
  除了具有所有顺序容器都有的成员函数外,还有 8 个成员函数,

push_front: 在前面插入
pop_front:  删除前面元素
sort:       排序(list不支持STL的算法 sort)
remove:     删除和指定值相等的所有元素
unique:     删除所有和前一个元素相同的元素(要做到元素不重复,要先sort)
merge:      合并两个链表,并清除合并那个
reverse:    颠倒链表
splice:     在指定位置前面插入另一个链表中的一个或多个元素,并在另一链表中删除被插入元素

举个例子,

#include <iostream>
#include <list>
#include <algorithm>using namespace std;class A{private:int n;public:A(int n_){n = n_; }friend bool operator < (const A & a1, const A & a2);friend bool operator == (const A & a1, const A & a2);friend ostream & operator << (ostream & o, const A & a);
};
bool operator < (const A & a1, const A & a2){return a1.n < a2.n;
}
bool operator == (const A & a1, const A & a2){return a1.n == a2.n;
}
ostream & operator << (ostream & o, const A & a){o << a.n;return o;
}template <class T>
void printList(const list<T> & lst){// 不推荐的写法,还是用两个迭代器作为参数更好typename list<T>::const_iterator i;for(i = lst.begin(); i != lst.end(); i++) cout << * i << ",";
} // typename 用来说明 list<T>::const_iterator 是个类型
// 在 vs 中那个不屑int main(){list<A> lst1, lst2;lst1.push_back(1); lst1.push_back(2);lst1.push_back(3); lst1.push_back(4);lst1.push_back(2);lst2.push_back(10); lst2.push_back(20);lst2.push_back(30); lst2.push_back(30);lst2.push_back(30); lst2.push_back(40);lst2.push_back(10);cout << "1)"; printList(lst1); cout << endl;cout << "2)"; printList(lst2); cout << endl;lst2.sort();cout << "3)"; printList(lst2); cout << endl;lst2.pop_front();cout << "4)"; printList(lst2); cout << endl;lst1.remove(2);cout << "5)"; printList(lst1); cout << endl;lst2.unique();cout << "6)"; printList(lst2); cout << endl;lst1.merge(lst2);cout << "7)"; printList(lst1); cout << endl;cout << "8)"; printList(lst2); cout << endl;lst1.reverse();cout << "9)"; printList(lst1); cout << endl;lst2.push_back(100); lst2.push_back(200);lst2.push_back(300); lst2.push_back(400);list <A>::iterator p1, p2, p3;p1 = find(lst1.begin(), lst1.end(), 3);p2 = find(lst2.begin(), lst2.end(), 200);p3 = find(lst2.begin(), lst2.end(), 400);lst1.splice(p1, lst2, p2, p3);// 将 [p2, p3) 插入 p1 之前,并从 lst2 删除 [p2, p3)cout << "10)"; printList(lst1); cout << endl;cout << "11)"; printList(lst2); cout << endl;return 0;
}

输出结果为,

4.5 set 和 multiset

  关联容器
-----》内部元素有序排列,新元素插入的位置取决于它的值,查找速度快;
-----》除了各容器都有的函数外,还支持以下成员函数:

find:查找等于某个值(x 大于 y 和 y 小于 x 同时不成立立即为相等)
lower_bound:查找某个下界
upper_bound:查找某个上界
equal_bound:同时查找上界和下界
count:计算等于某个值的元素个数(~)
insert:用以插入一个元素或一个区间

  Pair 模板

template <class _T1, class _T2>
struct pair{typedef _T1 first_type;typedef _T2 second_type;_T1 first;_T2 second;pair():first(), second(){}pair(const _T1 & _a, const _T2 & _b):first(_a), second(_b){}template<class _U1, class _U2>pair(const pair <_U1, _U2> & _p):first(_p.first), second(_p.second){}
}map/multimap 容器里放着的都是 pair 模板类的对象,且按 first 从小到大排列第三个构造函数用法实例:
pair <int, int>
p(pair <double, double>(5.5, 4.6))
// p.first = 5, p.second = 4

  multiset 情况

template <class Key, class Pred = less < Key >,class A = allocator <Key> >
class multiset{......};
---> Pred 类型的变量决定 multiset 中元素,定义怎么比大小的。op(x, y) 返回值 true, 则 x 比 y 小. 缺省类型为 less < Key >
---> less 模板的定义:
template <class T>
struct less: public binary_function <T, T, bool>
{bool operator()(const T & x, const T & y){return x < y;}const;}
// less 模板靠 < 来比较大小

  multiset 的成员函数

// 在容器中查找值为 val 的元素,返回迭代器。找不到,返回 end()
iterator find(const T & val);// 将 val 插入到容器中并返回迭代器
iterator insert(const T & val);// 将区间 [first, last)插入容器
iterator insert(iterator first, iterator last);// 统计有多少个元素的值和 val 相等
int count(const T & val);// 查找一个最大的位置 it, 使得 [begin(), it) 中所有的元素都比 val 小
iterator lower_bound(const T & val);// 查找一个最小的位置 it, 使得 [it, end()) 中所有的元素都比 val 大
iterator upper_bound(const T & val);// 同时求得 lower_bound 和 upper_bound
pair <iterator, iterator> equal_range(const T & val);// 删除 it 指向的元素,返回其后面的元素的迭代器(VS 这样的,dev c++ 不是)
iterator erase(iterator it);

  multiset 实际例子1

#include <iostream>
#include <set>
#include <algorithm>using namespace std;class A{};int main(){// 等价于 multiset <A, less <A>> a;//   插入元素时,multiset 插入元素会和已有元素做比较。//   由于 less 模板是用 < 进行比较的,这样,要求 A 的//   对象能用 < 比较,即适当重载了 < .multiset <A> a;a.insert(A()); // error
}

  multiset 实际例子2

#include <iostream>
#include <set>
#include <algorithm>using namespace std;template <class T>
void Print(T first, T last){for(; first != last; ++first)cout << *first << " ";
}
class A{private:int n;public:A(int n_){n = n_;}friend bool operator < (const A & a1, const A & a2){return a1.n < a2.n;}friend ostream & operator << (ostream & o, const A & a2){o << a2.n; return o;}friend class myLess;
};
struct myLess{bool operator()(const A & a1, const A & a2){// 按照个位数比大小return (a1.n % 10) < (a2.n % 10);}
};
typedef multiset <A> MSET1; // 用 < 号比大小
typedef multiset <A, myLess> MSET2; // 用myLess::operator()方式比大小
int main(){const int SIZE = 6;A a[SIZE] = {4, 22, 19, 8, 33, 40};MSET1 m1;m1.insert(a, a+SIZE);m1.insert(22);cout << "1)" << m1.count(22) << endl;cout << "2)"; Print(m1.begin(), m1.end());MSET1::iterator pp = m1.find(19);if(pp != m1.end()) // 右开区间cout << "found" << endl;// lower_bound 查找一个最大位置 it, 使得[begin, it)中所有元素都比 val 小cout << "3)"; cout << * m1.lower_bound(22) << ","<< *m1.upper_bound(22) << endl;cout << "4)"; Print(m1.lower_bound(22), m1.upper_bound(22));cout << endl;cout << "5)"; cout << *pp << endl;MSET2 m2;m2.insert(a, a + SIZE);cout << "6)"; Print(m2.begin(), m2.end());return 0;
}

结果如下

  set 模板

template <class Key, class Pred = less <Key>,class A = allocator<Key>>class set{...}
插入 set 已有元素时,忽略插入

  set 例子

#include <iostream>
#include <set>
#include <algorithm>using namespace std;int main(){typedef set <int>::iterator IT;int a[5] = {3, 4, 6, 1, 2};set <int> st(a, a+5); // 这里是 1 2 3 4 6pair<IT, bool> result;result = st.insert(5); // st 变成 1 2 3 4 5 6if(result.second) // 插入成功则输出被插入元素cout << *result.first << "inserted" << endl; // 输出 5 insertedif(st.insert(5).second)cout << * result.first << endl;elsecout << *result.first << "already exists" << endl; // 输出 5 already existspair <IT, IT> bounds = st.equal_range(4);cout << *bounds.first << "," << *bounds.second; // 输出 4, 5return 0;
}

结果如下,

Pair部分理解还不是很清楚。

4.6 map 和 multimap

  Pair 模板

template <class _T1, class _T2>
struct pair{typedef _T1 first_type;typedef _T2 second_type;_T1 first;_T2 second;pair():first(), second(){}pair(const _T1 & _a, const _T2 & _b):first(_a), second(_b){}template<class _U1, class _U2>pair(const pair <_U1, _U2> & _p):first(_p.first), second(_p.second){}
}map/multimap 容器里放着的都是 pair 模板类的对象,且按 first 从小到大排列第三个构造函数用法实例:
pair <int, int>
p(pair <double, double>(5.5, 4.6))
// p.first = 5, p.second = 4

  multimap模板

template <class Key, class T, class Pred = less <Key>,class A = allocator<T> >
class multimap{......typedef pair <const Key, T> value_type;
}; // Key 代表关键字类型
// 1. multimap 中的元素由 <关键字, 值> 组成,每个元素是一个 pair 对象,关键字就是 first 成员变量,类型是 Key
// 2. multimap 中允许多个元素的关键字相同。元素按照 first 成员变量从小到大排列缺省情况下用 less<Key>定义关键字小于关系

  multimap实例

#include <iostream>
#include <map>
#include <algorithm>using namespace std;int main(){typedef multimap <int, double, less<int> > mmid;mmid pairs;cout << "1)" << pairs.count(15) << endl;// typedef pair <const Key, T>value_type;pairs.insert(mmid::value_type(15, 2.7));pairs.insert(mmid::value_type(15, 99.3));// 求关键字等于某个值的元素个数cout << "2)" << pairs.count(15) << endl;pairs.insert(mmid::value_type(30, 111.11));pairs.insert(mmid::value_type(10, 22.22));pairs.insert(mmid::value_type(25, 33.333));pairs.insert(mmid::value_type(20, 9.3));for(mmid::const_iterator i = pairs.begin(); i != pairs.end(); i++){cout << "(" << i->first << "," << i->second << ")" << ",";}
}

输出结果为,

  map模板

template <class Key, class T, class Pred = less <Key>,class A = allocator<T> >
class multimap{......typedef pair <const Key, T> value_type;
};
// map 中的元素都是 pair 模板类对象。关键字 (first 成员变量) 各不相同。元素按照
// 关键字从小到大排列,缺省情况下用 less<Key>, 即“<”定义小于。

  map的[ ]成员函数

若 pairs 为 map 模板类的对象,
pairs[key]
返回关键字等于 key 的元素的值(second成员变量的引用)。若没有关键字为 key 的元素会往 pairs 里面插入
一个关键字为 key 的元素,其值用无参构造函数初始化,并返回其值引用。
如:
map <int, double> pairs;
pairs[50] = 5;

  map的示例

#include <iostream>
#include <map>
#include <algorithm>using namespace std;template <class Key, class Value>
ostream & operator <<(ostream & o, const pair<Key, Value> & p){o << "(" << p.first << "," << p.second << ")";return o;
}int main(){typedef map <int, double, less <int> > mmid;mmid pairs;cout << "1)" << pairs.count(15) << endl;pairs.insert(mmid::value_type(15, 27));pairs.insert(make_pair(15, 99.3)); // make_pair生成一个 pair 对象cout << "2)" << pairs.count(15) << endl;pairs.insert(mmid::value_type(20, 9.3));mmid::iterator i;cout << "3)";for(i = pairs.begin(); i != pairs.end(); i++)cout << *i << ",";cout << endl;cout << "4)";int n = pairs[40]; // 如果没有关键字 40 的元素,则插入一个for(i = pairs.begin(); i != pairs.end(); i++)cout << *i << ",";cout << endl;cout << "5)";pairs[15] = 6.28; // 把关键字 15 的元素变为 6.28for(i = pairs.begin(); i != pairs.end(); i++)cout << *i << ",";return 0;
}

输出结果为,

4.7 容器适配器

  容器适配器中没有迭代器。下面开始介绍,
  stack相关例子

stack 是后进先出的数据结构,只能插入,删除,访问栈顶的元素。
可以进行以下操作:
push   插入元素
pop    弹出元素
top    返回栈顶元素引用,可以修改

  queue相关例子

queue 是先进先出的数据结构
可以进行以下操作:
push   插入元素,发生在对尾
pop    弹出元素,发生在对头
top    返回对头元素引用,可以修改,发生在对头
back   返回对尾元素的引用,也可以修改

  priority_queue相关例子

与 queue 类似,确保优先级最高的在对头,即保证最大元素在最前面,用堆排序技术实现
可以进行以下操作:
push   插入元素,发生在对尾 O(logn)
pop    删除的是最大的元素 O(logn)
top    返回最大元素引用 O(1)
back   返回对尾元素的引用,也可以修改

  举个例子,

#include <iostream>
#include <queue>
#include <algorithm>using namespace std;int main(){priority_queue <double> pq1;pq1.push(3.2); pq1.push(9.8); pq1.push(9.8); pq1.push(5.4);while( ! pq1.empty() ){cout << pq1.top() << " ";pq1.pop();} // 输出为:9.8 9.8 5.4 3.2cout << endl;priority_queue <double, vector<double>, greater<double> > pq2;pq2.push(3.2); pq2.push(9.8); pq2.push(9.8); pq2.push(5.4);while( ! pq2.empty() ){cout << pq2.top() << " ";pq2.pop();} // 输出为:3.2 5.4 9.8 9.8cout << endl;return 0;
}

stack, queue, priority_queue 都有以下

empty()   成员函数用于判断适配器是否为空
size()    成员函数返回适配器中元素个数

五、总结

  还有一些数据结构部分没整理完,最近把这篇博客补充完整,但是 C++ 知识学习完成了。最近事突然多了起来,而且现在工作情况发生了一些细小的转变,因为自己做过了三个控制类型项目,一些 HR 给我了一些机会,叫我先努力学习,这些以后再做记录。现在对算法,计算机网络,操作系统进行系统学习了,博客也会记录。C++ 学习部分完成,以后会更深入。

4.C++深入理解 面向对象部分3相关推荐

  1. 深入理解面向对象 -- 基于 JavaScript 实现

    我们在学习编程时,避免不了会接触一个概念,叫:面向对象编程(Object-oriented programming,缩写:oop) (不是搞对象那个对象哈),其实我们的编程方式,不止有面向对象,还有 ...

  2. 从C++到Java --理解面向对象是关键所在

    从C++到Java --理解面向对象是关键所在 本文将提供一个对这些概念的简明的解释,而不是提供一些深入的或者如何使用的问题.记住,这只是依据我对Java的经验带而提出的一些主要的差异. Java在虚 ...

  3. 3.C++深入理解 面向对象部分2

    C++深入理解 面向对象部分 一.面向对象深入部分   在前几天 360 笔试做下来,感觉自己会的难度还行,还没学过的就乱选了,算法题部分做出来运行结果也是对的,但是两个解答题一点都没见过,直接就跳过 ...

  4. 2.C++深入理解 面向对象部分1

    C++深入理解 面向对象部分 一.补充知识   现在开始刷 北京大学 程序实际与算法三 视频面向对象部分学习,记录自己不懂的知识,这里以后打算学习 C++ 了,把这门语言吃透再学习新的,这里使用的运行 ...

  5. (自己收藏)全面理解面向对象的 JavaScript

    全面理解面向对象的 JavaScript 前天 by 资深编辑 WnouM 评论(3) 有2727人浏览 收藏 javascript 面向对象 对象 类 原型 < >猎头职位: 上海:Ju ...

  6. 深度理解面向对象的基础-抽象(一)

    前言: 面向对象这个词对于我们程序开发人员来说,应该都不陌生,我们总说开发要面向对象,但实际上在编写代码的过程中,很多人虽然实现了封装.继承.多态但却不是面向对象的程序设计,而是面向过程的实现逻辑,徒 ...

  7. 如何理解面向对象的封装、继承、多态

    如何理解面向对象的封装.继承.多态 面向对象可以说是一种对现实是事物的抽象,将一类事物抽象成一个类,类里面包含了这类事物具有的公共部分,以及我们对这些部分的操作,也就是对应的数据和过程. 面向对象思想 ...

  8. Java SE 008 理解面向对象程序设计 (Inside Object Oriented Programming)

    Java SE 008 理解面向对象程序设计 (Inside Object Oriented Programming) 前言:此笔记为圣思园张龙老师讲述的java视频课程笔记,自己看视频学习时记录的, ...

  9. 从哲学角度理解面向对象的思想

    "双语播放器"已在app store上架,欢迎大家前去下载(主要用于看电影,学英语,程序员一定要学好英语!) 这里是链接: https://itunes.apple.com/cn/ ...

最新文章

  1. Win7下的修改grub工具bcdedit
  2. 使用juery在iframe内部访问父页面元素
  3. pycharm 如何导入、导出设置?
  4. 国内互联网广告生态现状【计算广告】
  5. SEO中HTML标签权重
  6. java字符串的运用代码_java – 如何使用mockito模拟一个字符串?
  7. ubuntu 如何在任意终端不填加./就可以执行文件类似ls cd cp
  8. 一位像素艺术家用39张动图,将大自然的唯美尽收眼底…
  9. Abbirb120型工业机器人_ABB IRB 120工业机器人.pdf
  10. Aruba7010 默认密码_钟祥人注意!手机这个密码必须设,否则危险!
  11. 利用PlayerPrefs存储数据
  12. android monkey测试步骤,android Monkey 测试技巧
  13. linux内存源码分析 - 伙伴系统(释放页框)
  14. 1.2 数列和收敛数列
  15. laypage 独立控件使用 laypage 不出总页数
  16. chrome插件开发(Demo案例)
  17. 比较横截面与时间序列的因子模型
  18. 还在花钱抢票?12306 已屏蔽60款抢票软件!
  19. 007. RPX服务端和设计端说明
  20. 简单总结几种思维模式---助你编程,思绪如飞

热门文章

  1. win10 安装tensorflow-gpu
  2. Linux下创建Django项目并访问
  3. django-orm的表操作.
  4. web 前端 html
  5. linux 配置多IP
  6. 逻辑分析题汇总(一)
  7. Ising模型(伊辛模型)
  8. 【利用存储过程和三层架构完成新闻发布】
  9. 简单易懂的PHP的命名空间以及配合use的使用
  10. 安全漏洞问题6:SQL注入