个人C++学习笔记

2020-07-02

个人C++学习笔记,有需要的可以拿走(可能做的不详细)。

  1 /**********************************************************************************************************************************************/
  2                                                                       封装
  3 /**********************************************************************************************************************************************/
  4 
  5     为什么使用堆内存:
  6     1.使用比较大的内存时,可能超过栈内存限制
  7     2.使用手动控制内存的释放,跨函数使用内存
  8     3.可以根据程序实际需要来分配内存大小
  9     
 10     new delete与malloc(),free()的区别:
 11     1new delete 是操作符,而malloc() free()是函数,在申请内存时会创建函数栈,所以new delete效率要高。
 12     2、new会自动调用构造函数、delete会自动调用析构函数,而malloc和free没有这些。
 13 
 14 引用的定义(&):int &ref = num (ref为num的引用,绑定后不可更改)
 15          注:int *p = &num (这里的&是取地址符)
 16 
 17        ->C++没有提供读取引用地址的方式,访问引用的地址,实际上就是去访问了变量的地址。<-
 18 
 19          引用的类型必须和变量的类型是一样的(error: int num; long &ref = num;)。
 20              正确: int num = 10;
 21                    int &ref = num;
 22          引用常量的引用必须是常引用(error: int &ref = 10;)
 23              正确: const int &ref = 10;
 24 引用和指针使用上的区别:1、引用在声明时必须初始化,而指针可以是野指针
 25                    2、引用的绑定关系不可更改,而指针可以改变指针指向
 26                    3、有void的指针 但是没有void的引用,有二级指针,没有二级引用
 27                    4、有指针引用,没有引用指针
 28                    5、有数组指针,也有数组引用
 29                    引用的本质其实是指针常量,一样的不可更改绑定关系,但C++编译器在处理引用时会自动在指针常量前加上“*”号,而处理指针常量时则依旧时地址
 30 
 31 引用在C++中的使用:1、能作参数
 32                 2、调用简单又能得到地址传递
 33                 3、效率高
 34                 既有指针做参数的效率,又比指针方便安全
 35 
 36                 引用作返回值注意:千万不能返回一个局部变量的引用
 37 
 38 C++新增的函数特性:
 39                 1、内联函数(inline):在函数名前面加个inline就是内联函数
 40                 一般函数在调用的时候,都会创建函数栈和销毁函数栈,对于一下比较简短的函数,会比较浪费时间
 41                 而使用内联函数的话。在调用的时候会将函数内的逻辑语句替换到函数被调用的位置,这样就不会有
 42                 创建和销毁函数栈的动作。相当于一个带参宏替换,但是带参宏的话不会检查参数类型是否正确,不
 43                 安全,因此就有了内联函数,既会对参数类型进行检查,而且在调用的时候会将函数内的逻辑语句替
 44                 换到函数被调用的位置。
 45                 注意:内联函数中逻辑语句不能超过5行,否则编译器会识别为一般函数处理。
 46                      内联函数不能有循环体和switch语句,否则也会当作一般函数处理。
 47                      内联函数中不能抛出异常,例如(throw),否则也会当作一般函数处理。
 48                      内联函数中不能递归,否则也会当作一般函数处理。
 49 
 50                 2、函数重载(overload):允许在同一范围内(同一个类,同一文件),函数名相同,参数不同(参数个数不同/参数类型不同),
 51                         ->与返回值类型无关。
 52                           调用时会根据实参的个数和类型来调用函数。
 53                         ->注意不要定义容易产生二义性的函数,函数要具有唯一性。
 54 
 55                 3、函数缺省值:定义的时候必须从右往左,因为函数参数的使用顺序时从右往左。
 56                 如果函数参数右边的参数没有设置缺省值,则当前函数也不能设置缺省值,只有右边参数定义了缺省值
 57                 左边的才能定义。
 58                 ->注意防止二义性
 59 
 60 
 61  *面向对象编程:
 62  * 封装、继承、多态
 63  * 封装成类
 64  * 类的继承
 65  * 成员函数的多态
 66  *
 67  * class 与 struct比较,在创建类时只有默认的权限不一样,class创建类时默认权限是private(私有的),
 68     而struct在创建类时默认权限是Public(公共的)
 69  *
 70  * 创建对象:
 71  * 1、一般对象创建(栈内存):类名+对象名
 72  * 2、对象指针 (堆内存):类名+对象指针名 = new + 类名   delete + 对象指针名
 73  * 3、对象数组 (堆内存):类名+对象指针名 = new + 类名[ ] delete[ ] + 对象指针名
 74              (栈内存):类名+对象名[i];
 75               
 76  * 4、使用构造函数创建对象(栈内存)
 77  * 5、匿名对象
 78  *
 79  * 特殊对象:
 80  *       常对象(const + 类名 + 对象名),常对象只能调用
 81  常函数
 82  *       一般对象既能调用一般函数,又能调用常函数
 83  
 84  * 常函数:(类型+函数名()+const)例:void show()const{}
 85  *       常函数中不能修改对象的值
 86  
 87  * 特殊的重载:常函数重载(例:void show(){}   和void show()const {}
 88  *                      这两个函数虽然函数名相同,参数列表相同,但是一样构成重载)
 89                操作符重载
 90  *
 91  * 类中的特殊数据成员:例如static int studentname; 
 92  *       静态数据成员:不能在类内初始化只能在类内声明,只能在类外初始化,并且初始化时不需要带static,但要加上类域
 93  *      一般数据成员必须通过对象才能访问,而静态数据对象可以直接通过类名直接访问。例如:cout <<student::studentname<<endl;,也可以通过对象调用。
 94 
 95         不管有多少个对象,如果只创建了一个静态成员变量,那就永远只会有一个,通过任意对象去调用这个变量,都会是那一个变量。
 96         一般数据成员对于不同的对象会有不同的成员。
 97 
 98          静态成员函数:静态成员函数中不能访问一般成员变量,同时也不能使用this指针,可以使用类名直接调用,也可以使用对象调用。
 99          只能访问静态成员变量,或者静态成员函数。
100          静态成员函数中不能访问一般成员变量,一般成员函数,同时也不能使用this指针的原因是因为静态成员函数不需要对象就可以使用,而一般函数、一般成员变量、this都需要先创建对象才可以使用。
101 
102    用静态成员函数作单例模式:
103          单例模式:保证在同一时刻只有一个对象存在(常用在文件管理对象)(线程池的管理对象)
104          将构造函数、析构函数私有化(private),创建一个静态成员函数来获取一个对象和一个析构函数来销毁对象
105 
106 /***********************************************************************/
107 访问权限:
108                         本类             子类             类外
109             默认:          可以             不可以          不可以
110             private:   可以            不可以            不可以
111             public:        可以            可以                  可以
112             protected:    可以            可以                不可以
113 /************************************************************************/
114 
115 
116 -->20200422
117 构造函数:函数名与类名相同,并且没有任何返回值
118           构造函数重载时一样需要避免二义性。
119         class student
120         {
121         public:
122             string name;
123             int age;
124             static int num;
125         public:
126             student();            <--默认构造函数
127             student(string name,int age):name(name),age(age)<--带参构造函数
128             {
129                 num++;
130             }
131             void SetName(string name);
132             void show()const;
133             static int StudentAge();
134             ~student();
135         };
136 
137         student stu;                            //调用无参的构造函数
138         student stu("zhangsan",10);//调用带参的构造函数
139         //student stu();                        //没有调用构造函数,调用的是()系统自动创建操作符重载
140         student *stu = new student;//调用默认的构造函数
141 
142 没有调用构造函数的情况:
143         student stu4("lisi",23);
144         student stu5 = stu4;                //对象stu5的创建不是调用的构造函数,而是调用的拷贝构造函数
145 
146         void  showstudent(student   stu)        //把实参值拷贝给形参时不会调用构造函数
147         {    
148                 stu.show();
149         }
150 
151 
152 写类时需要先把默认构造函数写了
153 默认构造函数:
154             在类里没有写任何构造函数时,在创建对象时会自动创建一个默认构造函数,但值会是个随机值。
155             但是如果类里面一旦写了一个带参的构造函数,那么就不会自动生成默认构造函数。
156             (带参的构造函数会把系统创建的默认构造函数给屏蔽掉,所以默认构造函数必须要显示定义)
157 
158 
159 构造函数特殊其情况:
160           ->类内有成员变量为指针时:
161             浅拷贝:只是拷贝地址,这样有可能产生问题,如果地址指向的内存被销毁,那么指针将会变成野指针
162             深拷贝:重新为指针分配内存并把值拷贝过来(相当于原封不动的Copy)。
163 
164 对象初始化的方式:
165             构造函数初始化:
166             初始化列表:在形参列表右边加上“:”再将参数赋值给成员变量。
167             例如:student(string name,int age):name(name),age(age)
168             {                                        ^           ^                            
169             }
170             "name(name),age(age)",括号外面的name和age是参数,括号内的是成员变量。
171 
172             ->初始化列表是在给对象分配内存的时候就调用的,在构造函数之前调用
173             ->常量数据成员初始化必须在初始化列表中初始化
174             ->引用数据成员初始化时必须在初始化列表中初始化
175             ->其他类的对象作本类的数据成员时必须在初始化列表里初始化,不能放在构造函数内部初始化,放在内部初始化会多创建一个对象
176             ->父类构造一般也在初始化列表中初始化,指定调用父类的哪种构造函数
177             
178             class Body
179             {
180             public:
181                 Body()
182                 {
183                     cout << "Body()" << endl;
184                     this->year = 0;
185                     this->month = 0;
186                     this->day = 0;
187                 }
188                 Body(int year,int month,int day)
189                 {
190                     cout << "Body(int year,int month,int day)" << endl;
191                     this->year = year;
192                     this->month = month;
193                     this->day = day;
194                 }
195                 void showData()
196                 {
197                     cout << this->year << "-" << this->month << "-" << this->day << endl;
198                 }
199             private:
200                 int year;
201                 int month;
202                 int day;
203             };
204 
205             class student
206             {
207             public:
208                 student()
209                 {
210 
211                 }
212                 student (string name,int age,int year,int month ,int day):name(name),age(age),Bd(year,month,day)
213                 {
214 
215                 }
216                 void show()
217                 {
218                     cout << this->name << "," << this->age << ",";
219                     this->Bd.showData();
220                     cout << endl;
221                 }
222             private:
223                 string name;
224                 int age;
225                 Body Bd;
226             };
227 
228             int main(int argc, char *argv[])
229             {
230                 student stu("zhangsan",21,1998,11,18);
231                 stu.show();
232                 return 0;
233             }
234             
235 初始化列表执行顺序:
236             1、先执行初始化列表,再执行构造函数
237           ->2、类中有多个其他类对象作数据成员时,初始化列表执行的顺势是按照成员声明顺序来执行的,与初始化列表书写顺序无关。
238 
239 析构函数定义和执行:
240             ->析构函数:函数名与类名相同,前面有"~",且不能有参数,所以析构函数不能重载,在一个类里只有一个析构函数。
241             ->析构函数跟构造函数一样也是自动调用,不过是在销毁对象或作用域结束时自动调用
242             ->析构函数执行得顺序与构造函数完全相反,先创建的对象最后析构,后创建的对象最先析构
243 
244 类内有成员为指针时析构:
245             
246 
247 STL(标准库)的string类:
248         C++中的string跟C中字符串的区别;
249                 C的字符串是 const char *str = "hello";必须以结尾;
250                 C++中的字符串是typedef basic_string<char> string;
251                 C++中的string是只能装char的容器,不再是、0作结束;
252                 
253                 string str1 = "高弟"254                 cout << sizeof(str1) << endl;//24
255                 cout << str1.size() << endl; //字节数
256                 cout << str1.length() << endl; //字符个数
257                 cout << str1.capacity() << endl; //容量
258                 cout << str1.max_size() << endl; //最大容量
259 
260                 str1.append("好嗨哟");        //追加
261                 str1.resever();
262                 str1.resize();'
263                 
264                 C语音字符串转C++字符串-> string data = "hello";
265                 C++ 字符串转C语言字符串->const char * = data.c_str/data.data;
266 
267 string的增删改查:
268         访问:
269             1、通过下标访问
270             for(int i = 0;i<str.size();i++)
271             {
272                 cout << str1[i] << endl;            //这种方法不会查找是否越界
273                 cout << str1.at(i) << endl;            //这种方法会查找是否越界(推荐使用)
274             }
275             
276             2、迭代子访问:
277             //string::iterator it;                   //迭代子的本质是指针
278             for(string::iterator it = str.begin();it != str.end();it++)
279             {
280                 cout << *it << endl;
281             }
282             
283             可以通过一般迭代子去修改字符值
284             it = str.begin();
285             *it = "H";
286             cout << str1 << endl;
287 
288             3、常迭代子:const_iterator
289             for(string::const_iterator it = str.cbegin();it != str.cend();it++)
290             {
291                 cout << *it << endl;
292             }
293             不能用常迭代子改变值
294             
295             4、反向迭代子:reverse_iterator
296             for(string::reverse_iterator it = str.rbegin();it != str.rend();it++)
297             {
298                 cout << *it << endl;
299             }
300     /**************************************************************************************************************************************/
301   ->增:
302             string src = "hello abc world abc linux abc";
303             string str1 = "hello";
304             string str2 = "world";
305           ->/*append    只能往string类的末尾添加字符串*/
306             //str1.append("abc");                        //往str1后面增加字符串abc
307             //str1.append(str2.begin()+2,str2.end());    //将str2从中的“rld”增加到str1后面
308             //str1.append(5,'6');                        //在Str1后面增加5个字符6
309             //str1.append("hello world",1,4);            //将字符串从下标1到下标5加到str1后面
310             
311           ->/*insert    可以往string类的任意位置添加*/
312             //str1.insert(str1.begin() + 1,'H');        //往str1的下标1处插入字符H
313             //str1.insert(1,3,'H');                        //往str1的下标1处插入三个字符H
314             //str1.insert(1,"hello linux");                //往str1的下标1处插入字符串“hello linux”
315             //str1.insert(0," hello world ",5);            //往str1的下标0处插入字符串的前五个字符“hello”
316             //str1.insert(0,"hello world",0,5);            //往str1的下标0处插入字符串“hello world”第0个下标到第5个下标的字符,既“hello”
317     /**************************************************************************************************************************************/
318   ->删:
319     erse
320     clear  ->可用empty来判断是否为空
321     pop
322     /**************************************************************************************************************************************/
323   ->改:(用'='可以直接全部改0变)
324     assign
325     replace
326       /**************************************************************************************************************************************/
327   ->查:
328     ->find(从左往右查)返回的是查找到的字符的下标,没找到返回-1
329             string src = "hello abc world abc linux abc";
330             string str1 = "hello";
331             string str2 = "world";
332     ->rfind(从右往左查)返回的是查找到的下标,没找到报错
333     
334     ->find_first_of(从左往右)查找子串中任意一个字符出现的位置
335     
336     ->find_first_not_of(从左往右)查找不是子串中任意一个字符出现的位置
337      
338      find_last_of
339      
340      find_last_not_of
341       /****************************** ********************************************************************************************************/
342   ->截取:
343         substr
344             
345             
346 只有把已初始化对象赋值给为初始化对象的时候才会调用拷贝构造函数
347 把实参对象转换成形参对象时会
348 
349                                                                                                                          
350 无名对象:
351         创建对象时没有对象名,用完后就销毁,更节省内存,而用一般对象则会一直知道主函数结束才会销毁,占用内存比较高。
352 
353 临时对象:
354         由函数返回的对象,如果有对象去接收这个对象,那么它的作用域将会得到延申
355 
356 友元类:
357         在一个类中用friend声明的函数(友元函数)
358         1、把一个类的成员函数作为另一个类的友元函数
359         2、把一个普通函数声明为类的友元
360         3、声明为友元类后,则这个类的所有函数都能直接访问另一个类的所有数据。
361 
362         友元是为了突破类的数据访问限制(privateprotected363         
364         友元会破坏类的封装性,所以尽量不要用友元突破私有限制。
365 
366 内部类:
367         在一个类中声明的类,相对于内部类的外部(外部类)
368         内部类的一般函数要用两层类域才能在外部类外面实现
369 
370 内部类的访问特性:
371         内部类可以直接访问外部类的所有静态数据,但是外部类不能直接访问内部类的成员。内部类相当于外部类的一个成员。
372 
373 局部类:定义在函数中的类    常用在接口设置
374         作用范围只能在这个函数
375         void Test()
376         {
377             class Locale        //这个类的作用域仅在这个函数里
378             {
379                 public:
380                     .
381                     .
382                     .
383                 private:
384                     int data;
385                     //static int staticdata    //error  局部类中不能定义静态数据成员,因为一般函数需要对象调用
386             
387             };
388             //函数调用
389             locale l(10);
390             l.show();
391         }
392 
393 
394 /**********************************************************************************************************************************************/
395                                                                       继承
396 /**********************************************************************************************************************************************/
397 用“:”来继承
398 格式:子类 : 权限 父类名
399 
400 继承以后:
401         ->创建子类对象的时候,需要先生父类对象再生子类对象(辈分高的最先创建),可以在初始化列表中指定使用父类哪个构造函数构造父类对象    格式:子类构造函数名 + ; +父类构造函数名
402         ->析构的时候,先析构子类再析构父类
403         ->子类继承父类的数据成员、成员函数(代码重用,体系(创建子类时只需要注意子类比父类多的属性、动作))
404         ->父类私有数据成员,私有成员函数既不能被调用也不能被继承,构造函数、析构函数、拷贝构造函数、运算符重载不能被继承但是可以被子类调用
405 
406 继承权限:
407         public:继承过来后,原来是public的还是public,protected的还是protected(父类的访问权限在子类中保持不变)
408         private:继承过来后,继承过来的公共/保护父类成员函数,公共/保护数据成员权限全部变成private
409         protected:继承过来后,继承过来的公共父类成员函数,公共数据成员权限变成protected,保护的成员权限不变,私有的还是私有
410         默认: private
411 
412 多继承:
413         格式:子类 : 权限 父类1 , 权限 父类2...
414         多继承时的构造和析构顺序:
415                         构造的时候,按照继承顺序构造父类,再按声明顺序构造子类中其他类对象或数据成员,再构造子类对象。析构的顺序与构造顺序相反。
416 
417                     #include <iostream>
418                     #include <string>
419 
420                     using namespace std;
421 
422                     class A
423                     {
424                     public:
425                         int public_data;
426                     private:
427                         int private_data;
428                     protected:
429                         int protected_data;
430 
431                     public:
432                         A()
433                         {
434                             cout << "A()" << endl;
435                             this->public_data = 0;
436                             this->private_data = 0;
437                             this->protected_data = 0;
438                         }
439                         A(int i,int j,int k)
440                         {
441                             cout << "A(int i,int j,int k)" << endl;
442                             this->public_data = i;
443                             this->private_data = j;
444                             this->protected_data = k;
445                         }
446                         ~A()
447                         {
448                             cout << "~A()" << endl;
449                         }
450                         void showA()
451                         {
452                             cout << this->public_data << "," << this->private_data << "," << this->protected_data << endl;
453                         }
454                     };
455                     
456                     class B: public A
457                     {
458                     private:
459                         string name;
460                     public:
461                         B()
462                         {
463                             cout << "B()" << endl;
464                         }
465                         B(string name,int i,int j,int k):A(i,j,k)
466                         {
467                             cout << "B(string name ,int i,int j, int k)" << endl;
468                             this->name =  name;
469                         }
470                         ~B()
471                         {
472                             cout << "~B()" << endl;
473                         }
474                         void showB()
475                         {
476                             cout << this->name << endl;
477                         }
478                     };
479 
480                     class  C: public B
481                     {
482                     public:
483                         C()
484                         {
485                             cout << "C()" << endl;
486                         }
487                         ~C()
488                         {
489                             cout << "~C()" << endl;
490                         }
491                         void showC()
492                         {
493                             cout << "showC()" << endl;
494                             cout << this->public_data << endl;
495                             cout << this->protected_data << endl;
496                         }
497                     };
498 
499                     int main(int argc, char *argv[])
500                     {
501                         B b("zhangsan",1,2,3);
502                         b.public_data = 3;
503                         b.showA();
504                         C().showC();
505                         cout << "main over" << endl;
506                         return 0;
507                     }
508 
509 
510 静态联编(早期联编):根据指针或引用的类型来判断调用哪个类的函数,而不是根据对象类型来决定调用哪个类的
511             (子类指针不能指向父类对象,子类引用不能引用父类对象)
512 动态联编(晚期联编):在运行时确定函数的调用,根据指针或引用实际指向的对象来决定调用哪个类的函数
513         要实现动态连编:
514                     1、在父类的同名成员函数前面加virtual(虚函数),子类可加可不加
515                     2、父类的同名函数加了virtual,不一定是动态联编
516 
517 virtual的本质:加了virtual后在本类中会增加一个指针,这个指针指向了底层的虚函数表,虚函数表里放的就是子类的同名函数。
518                把父类指针或引用作参数,子类的指针或引用都能都当参数传递进去
519 
520 
521 动态联编从另外一种角度来讲,其实就相当于重写
522 重写:
523             在父子类,子类的函数名与父类的函数名相同,参数列表相同,父类的同名函数必须有vritual
524             重写一定是动态联编,由引用和指针指向的对象来决定调用哪个类的函数
525             
526 多态(重写)(重载):
527             调用同名函数时,由于参数不同或由于所调用的对象不同造成调用结果不同
528             同名函数参数不同,就是重载
529             同名同参数,但调用对象不同,就是重写
530 
531 重载和重写的区别:
532             重载是在同一个类,而重写是在不同的类
533             重载要求函数名相同,但参数不同,而重写两者都要相同
534             virtual要求不同
535             重载是静态联编,重写是动态联编
536             
537 重写容易发生的错误:
538             1、父类函数缺少virtual关键字,只能发送静态联编
539             2、参数不同,此时不管父类有没有vritual关键字,都会屏蔽子类的同名函数,发生静态联编
540 
541 父/子类的构造/析构函数问题:
542           ->父类的默认构造函数不能省,否则子类对象会无法创建
543           ->使用父类指针指向子类对象,并通过父类指针去删除子类对象时,无法调用子类的析构函数,需要将
544             父类的析构函数定义成虚析构(虚析构是能够被子类继承的)
545 
546 抽象类:有纯虚函数的类就是抽象类;
547         抽象类不能创建对象,只能声明指针或引用
548         抽象类能做框架,接口
549         抽象类的对象必须由它的子类去实现,子类要创建抽象类的对象,必须将父类中的纯虚函数全部实现
550         
551 纯虚函数:在虚函数的形参列表右边写上=0   例:vritual void fun() = 0;
552                             
553 纯虚析构函数:(必须在抽象类内部声明,在类外实现)
554         格式: virtual ~类名() = 0555             实现:类域::类名(){};
556 
557 一般纯虚函数可以在抽象类中实现,也可以不实现。
558 
559 
560 多继承问题:
561         1、菱形继承时会出现二义性,需要利用虚继承来解决二义性
562         
563                 class A
564                 {
565                 private:
566                     string name;
567                 public:
568                     void show()
569                     {
570                         cout << "A" << endl;
571                     }
572                 };
573 
574                 class B : virtual public A
575                 {
576 
577                 };
578 
579                 class C : virtual public A
580                 {
581 
582                 };
583 
584                 class D : public B,public C
585                 {
586 
587                 };
588 
589                 int main(int argc, char *argv[])
590                 {
591                     D d;
592                     d.show();
593 
594                     cout << "Hello World!" << endl;
595                     return 0;
596                 }
597 
598 2、V形继承  : 使用类域指定使用哪个类的成员;
599 
600 
601 操作符重载:
602 1、类内重载:把操作符重载为函数,当使用这个我操作符时会调用这个函数
603             格式:返回类型 operator 重载的操作符(形参列表)、
604             参数个数由操作符的操作数决定,单目运算符一般不需要参数a++,操作this
605                                           双目运算符 例:a + b ;则需要1个参数,this是左操作数
606 2、类外重载:
607             类外重载需要把操作符的所有操作数都卸载参数列表里。
608 
609 操作符重载时要注意的问题:
610             1、重载不能改变操作符的本义
611             2、不要改变操作符的返回值
612             3、下面的操作符不能重载: :: .  ->*  .*   ?:  标准类型转换(static_cast ,const_cast,reinterpret_cast,dynamic_cast),sizeof
613             4、=操作符必须在类内重载不能在类外重载
614 
615 输入输出流的重载(iostream):
616 
617             typedef struct student
618             {
619                 string name;
620                 int age;
621             }stu_t;
622                 
623             ostream & operator <<(ostream &out,const stu_t &val)  //重载一个stu_t类型的输出流
624             {
625                 out<<val.name<<" "<<val.age<<endl;
626                 return out;
627             }
628 
629             istream & operator >> (istream &in,stu_t &val)        //重载一个stu_t类型的输入流
630             {
631                 cout<<"input name:";
632                 in>>val.name;
633                 cout<<"input age:";
634                 in>>val.age;
635                 return in;
636             }
637 
638 
639 指向类内成员指针:数据成员、成员函数
640 
641 .*
642 ->*
643                 class Student
644                 {
645                 public:
646                     string name;
647                     int age;
648                 public:
649                     Student()
650                     {
651 
652                     }
653                     Student(string name ,int age)
654                     {
655                         this->name = name;
656                         this->age = age;
657                     }
658                     ~Student()
659                     {
660 
661                     }
662                     void show()
663                     {
664                         cout << "show()" << endl;
665                         cout << this->name << "," << this->age << endl;
666                     }
667                 };
668 
669                 int main(int argc, char *argv[])
670                 {
671                 #if 0
672                     int Student::*p;                            //创建一个指向数据成员的指针
673                     Student stu("zhangsan",20);
674                     p = &Student::age;                            //将数据成员地址传递给指针,但应注意这里需要使用类域
675                     cout << stu.*p << endl;                        //创建的对象stu为普通对象,所以调用的时候用.*
676 
677                     Student *pfun = new Student("lisi",21);        //这里创建的对象为指针,因此去调用之创建的指向数据成员的指针时需要用->*
678                     cout << pfun->*p << endl;
679                     delete pfun;
680                 #endif
681 
682                 #if 1
683                     void (Student::*pfun)();                    //创建一个指向成员函数的指针
684                     pfun = Student::show;                        //将show函数的地址传递给pfun
685                     Student stu1("zhaosi",50);                    //创建一般对象
686                     (stu1.*pfun)();                                //一般对象使用.*调用之前创建的pfun指针
687 
688                     Student *stu2 = new Student("lisi",30);        这里创建的对象为指针,因此去调用之创建的指向成员函数的指针时需要用->*
689                     (stu2->*pfun)();
690                     delete stu2;
691                 #endif
692                     return 0;
693 
694 
695 因为重载或重写时,虽然降低了调用者的工作量,但并没有降低编写者的工作量,所以有;1模板                                                                                                                                                                            
696 
697 模板的本质:将类型做参数    把类型做形参传递,实际的参数类型在调用的时候才确定
698 
699 模板函数:只适用于函数体相同,参数个数相同,类型不同的情况
700 模板类:
701 
702 模板函数例子:
703         template <typename T>                    //定义了一个模板类型名,也可以用template <class T>来定义
704         void add(T a,T b)                        //实现的模板函数
705         {
706             cout << "add()" << endl;
707             cout << a + b << endl;
708         }
709 
710         int main()
711         {
712             float f1 = 1.6f,f2 = 1.7f;
713             add(f1,f2);                            //如果参数列表里的类型不一样,需要在调用前先声明参数类型
714             add<int>(10, 10);                    //如果参数列表里的类型都一样,可以用这种方法调用
715             return 0;
716         }
717 
718 模板类例子:
719         template<typename T1,typename T2>                //声明模板类型
720         class Student
721         {
722         private:
723             T1 name;                                    //将数据成员定义成模板类型
724             T2 age;
725         public:
726             Student()
727             {
728 
729             }
730             Student(T1 name ,T2 age);                    //参数需要定义成模板类型
731             ~Student()
732             {
733 
734             }
735             void show();
736         };
737 
738         template<typename T1,typename T2>                //在类外实现时需要将模板类型在函数前声明
739         Student<T1,T2>::Student(T1 name ,T2 age)        //实现时需要在类域后面说明一下类型的个数和类型
740         {
741             this->name = name;
742             this->age = age;
743         }
744 
745         template<typename T1,typename T2>
746         void Student<T1,T2>::show()
747         {
748             cout << "show()" << endl;
749             cout << this->name << "," << this->age << endl;
750         }
751 
752         int main()
753         {
754             Student<string,int> stu("zhangsan",20);        //在创建对象时调用构造函数,需要在函数名后面说明类型模板里的类型
755             stu.show();
756             return 0;
757         }
758 
759 
760 模板类是实现容器得技术基础
761 
762 标准容器库STL:(standard  template  library)容器所存储得类型是未知的
763 容器:顺序 (线性)容器
764 关联容器:键值对key-value
765 
766 vector:
767 使用push_back()往容器尾部添加元素
768 可以使用下标去遍历容器,也可以使用迭代子去便利容器
769 //迭代子的本质是指针
770     
771     特点:底层是数组实现的
772           查找效率高
773           无序容器
774           允许有重复值
775           
776           
777 vevtor例子:
778         #include <iostream>
779         #include <vector>    //导入头文件
780 
781         using namespoace std;
782 
783         template<typename T1,typename T2>                //声明模板类型
784                 class Student
785                 {
786                 ...
787                 };
788                 ...
789                 
790         int main()
791         {
792            vector<string>  V;                            //定义一个string类型的容器V
793            V.push_back("zhangsan");                        //往容器类放入数据
794            V.push_back("lisi");
795 
796            Student<string ,int> stu0("wangwu",20);
797            vector<Student<string,int>> stu;
798            stu.push_back(stu0);
799 
800            for(vector<string>::iterator it = V.begin(); it != V.end(); it++)   //使用迭代子的方式去遍历容器
801            {
802                 cout << *it << endl;
803            }
804             return 0;
805         }
806 
807 
808 
809 
810 通用算法遍历for_each();
811 
812 vector查找:find(自动调用==操作符重载比较(类外重载)),
813             find_if(_Predicate:一个类中的()操纵符重载)
814             find_end();从右往左找连续子集,_BinaryPredicate
815             find_if_not()查找不是
816             
817             
818 
819 
820         一般类型容器可以用==操作符判断两个容器是否相等,而自定义类型容器可以用equal进行比较,但需要重载==操作符。
821 
822         使用count和count_if对成员进行数量统计
823         
824         普通排序sort
825         堆排序sort_heap
826 
827         去重unique:只能去除连续重复,它属于通用算法,不能改变容器大小
828         在去重前需要先排序,再统计容器大小,再去重,再改变容器大小。
829         
830         使用pop_back删除容器最后一个数据,使用clear清空容器,使用erase删除指定位置的数据
831         
832         去重的两种方法:
833         
834                         vector<Student> stu1;
835                         stu1.push_back(Student("wangwu",20));
836                         stu1.push_back(Student("zhangsan",21));
837                         stu1.push_back(Student("lisi",32));
838                         stu1.push_back(Student("zhaoliu",89));
839                         stu1.push_back(Student("gaodi",18));
840                         stu1.push_back(Student("lisi",32));
841                         stu1.push_back(Student("lisi",32));
842                         
843                         #if 0       //去重,不改变原有顺序
844                         bool flag = true;
845                         vector<Student>::iterator it = stu1.begin();
846                         while((it = find_if(it ,stu1.end(),Find_lisi())) != stu1.end())
847                         {
848                             if(flag)
849                             {
850                                 it++;
851                                 flag = false;
852                             }
853                             else
854                             {
855                                 stu1.erase(it);
856                             }
857                         }
858                     #endif
859 
860                     #if 1   //使用unique去重,但是会改变原有顺序
861                         sort(stu1.begin(),stu1.end(),cmpstr);                           //排序
862                         int n = count(stu1.begin(),stu1.end(),Student("lisi",32));      //统计个数
863                         unique(stu1.begin(),stu1.end());                                //去重
864                         stu1.reserve(stu1.size()-n+1);                                  //改变容器大小
865                     #endif
866          
867 
868 双端队列deque:
869         
870 单端队列queue:没有at函数,不能通过下标去遍历 不能用迭代子遍历 不能用通用函数遍历
871                 只能边删边遍历
872 
873 
874 
875 
876 set的容器特性:(在存数据的时候会自动排序)
877         1、底层实现(二叉树,链表),增删效率低,查询非常高
878         2、有序的容器(自动排序)
879         3、值不允许有重复
880         4、有自己的find()按值查找 erase()按值删除。不需要sort(),unique()
881         
882         
883 map:一个数据包含两部分:key value(键值对存在)
884 
885 默认升序排序
886 less显式升序排序
887 greator显式降序排序
888 
889 
890 map 允许value重复,不允许key重复.     
891 
892 map数据的操作:查改删,只能通过key去操作value
893 
894 一次存两个数据key value 以pair存在,key和value一一对应,通过key操作value
895 有序容器,对key排序
896 不允许key重复,允许value重复
897 增效率低,查询效率高
898 
899 
900 int main()
901 {
902     map<string ,student> stumap;
903 }
904 
905 
906 
907 
908 随机
909 有种随机:每次运行都随机 在运行时确定
910           制种  有效范围,在函数内有效
911 srand(time(NULL));
912 int num = rand()%10000;
913 
914 
915 无种随机:随机一次,以后每次运行都一样,在编译时确认
916 int num = rand()%10000;
917 
918 
919 随机打乱(洗牌)把容器内的数据随机打乱,只能对无序的容器打乱。
920 randdom_shuffle();
921 
922 
923 /**********************************************************************************************************************************************/
924                                                                       多态
925 /**********************************************************************************************************************************************/
926 重载和重写是构成多态的两种方式
927 
928 
929 
930 
931 
932      

17:35:59

原文地址:https://www.cnblogs.com/jiayezi/p/13226107.html