C++基础知识点

   1 "天命不足畏,祖宗不足法,人言不足恤"
   2 
   3 第一天 C++基础班 ************************************=============================================
   4 1.封装,继承,多态。
   5 2.作用域运算符(::)  
   6     :: 前面不写东西,表示调用全局变量
   7 3.名字控制(命名空间(namespace)):
   8     1)命名空间只能在全局范围内定义
   9     2)命名空间可以嵌套命名空间
  10     3)命名空间是开放的,可以随时向命名空间添加东西(变量,函数,类等)。
  11     4) 声明和实现可分离
  12     5) 匿名命名空间,默认此空间中的函数,变量等只在当前文件中有效,相当于给每个变量、函数前加入static.
  13     namespace TestA
  14     {
  15         int a;            //这是在命名空间中未初始化的定义。    当在命名空间进行定义时,而且此命名空间放在头文件,当在其他文件进行
  16                         //调用时,会产生重复定义的问题,编译不过。
  17         int b = 9;        //这是在命名空间中初始化的定义
  18         extern int c;     //这是命名空间中的声明。
  19     }
  20 
  21 4.条件编译
  22    #pragma once  (第一种)
  23     #ifndef ___  ;  #define _____;    #endif;   ____ 代表一个定义的名字。(第二种)
  24 
  25 5.bool 类型: bool 类型的变量可以把"任何非0值"转换为1.
  26 
  27 6.三目运算符:C和C++的区别:
  28     C语言中,三目运算符返回的是一个变量的值,没有内存空间,不可以修改。
  29     int a = 10; int b = 20;  a > b ? a : b;        (a > b ? a : b) = 30(不可以这样操作,原因如上);
  30     C++语言中,三目运算符返回的是一个变量本身,具有内存空间,可以修改。
  31     int a = 10; int b = 20;  a > b ? a : b;        (a > b ? a : b) = 30(可以这样操作,原因如上)
  32 
  33 7.当一个变量有内存空间时(只要在堆栈区),就可以通过它的地址修改内存里的值
  34 
  35 8.const 的使用
  36     如:C语言中,const 修饰的变量是一个只读变量,具有内存。
  37         C++语言中,const 修饰的为常量不具有内存(有额外情况,下面有),它放在符号表中。
  38 
  39     1)C++ 中两种情况不一样    
  40         const int a = 10;        此时 a 不具有内存,(用一个常量初始化)它放在符号表中。所以不可以对它进行修改。
  41         int b = 9; const int a = b;    此时a 具有内存(在栈区),因为对它进行赋值的是一个变量b(栈区),可以通过指针间接赋值改变 a 的值。
  42         (上面这种情况为用一个变量初始化 const 变量,所以它可以修改)。
  43 
  44     2)在局部变量中。( C语言 和 C++ 语言这种情况不一样)
  45         C++语言中: const int a = 9; int *p = (int *)&a; *p = 99; 
  46                 此时当对 const 常量 a 取地址,编译器会为它分配地址(在栈区);但是 a 常量从符号表中取值,不可被修改。
  47                 *p 修改的只是分配的内存空间的值。打印结果: a = 9; *p = 99;
  48         C语言中,变量 a 可以被修改。
  49 
  50     3)在全局变量( C语言 和 C++ 语言这两种情况一样)
  51     extern const int a = 9;     int *p = (int *)&a; *p = 99;(第一种)
  52     const int a = 9(它放在全局变量);  int *p = (int *)&a; *p = 99(第二种);
  53     此时对 const 变量 a 取地址或声明 extern 时,编译器会为它分配地址(在只读变量区);这段代码可以编译通过,但是运行阶段会报错,不
  54     以被运行。 
  55 
  56     4)C语言中 const 修饰的变量为外部链接(和普通变量一样)。
  57         C++语言中,const 修饰的变量为内部链接(和普通变量不一样)。
  58         不管是C语言还是C++ 语言,普通变量都是外部链接。
  59 
  60     5const#define 的不同, const 有类型,有作用域,可进行编译器类型安全检查。
  61         #define 无类型,不重视作用域(从定义开始到文件结尾),不可以进行安全检查。
  62 
  63 9.extern 的使用
  64     适用范围是不同文件之间变量的调用。(不可以再同一文件不同函数之间使用)
  65     在不同文件之间调用一个外部链接的变量时,只用在调用的文件中写一个 extern 关键字就可以了。
  66     当调用一个内部链接时,需要在被调文件和调用文件中都写 extern 关键字。
  67 
  68 10.变量可以定义数组    int n = 9; int arr[n];
  69     :不同编译器编译环境效果不同。在vs2013中不可以通过;在QT,linux中可以通过。支持C99的编译器可以通过。
  70 
  71 11.using 编译指令 与 using 声明的区别
  72     namespace A{ int a = 9; func(){} }
  73     namespace B{int a = 9; func(){} }
  74 using 声明: int main(){using A::a; cout << a <<endl; int a = 6;}当第二个 a 被定义时,函数编译通不过,命名冲突
  75 using 编译指令:int main(){using namespace A; cout <<a<<endl; int a = 6; cout << a << endl;} 打印结果为:96  76         此时打印最近定义的那个。 
  77         int main(){ using namespace A; using namespace B; cout << a << endl; } 此时就会报错。编译器不知道选择哪个a
  78 注意:使用using声明或using编译指令会增加命名冲突的可能性。也就是说,如果有名称空间,在代码中
  79         使用作用域解析运算符,则不会出现二义性。        
  80     
  81 12.在 C 语言中,重复定义多个同名的全局变量是合法的(不正规),但在C++ 中是不合法的。
  82 
  83 
  84 "天命不足畏,祖宗不足法,人言不足恤"
  85 第二天 C++基础班 ************************************=============================================
  86 
  87 1.变量都是有内存空间的,当它在堆栈区时可以通过指针修改它的值。
  88 
  89 2.引用:本质地址传递;编译器帮忙做了基本地址传递的部分。
  90     int &b = a;=====> int * const b = &a;(实质)    b常指针,指向不可以改变。
  91     (本质上:引用是一个常指针)
  92     1)同一个内存块可以取多个别名。int& b = a; int& c = a;
  93     2)引用的基本语法:int a = 10;    int &b = a;(引用) b = 100; cout << a << endl; cout << b << endl;
  94         输出的结果都为 100.
  95     3)指针的引用; int *p = NULL; int *&p1 = p;  p1 = (int *)malloc(sizeof(int));
  96     4)数组的引用:(两种方式)
  97         1. typedef int ARR[10];/*建立数组类型*/ int a[10];/*创建一个数组*/; ARR &p = a;/*对数组的引用*/    p[i] = i;/*对数组的某个元素赋值*/
  98         2.int (&p1)[10] = a;/*直接建立数组类型,进行引用,并初始化。*/    p1[i] = i; /*对数组的某个元素赋值*/
  99     "注意内容:" 
 100     5)    引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故 而类型与原类型保持一致,且不
 101         分配内存。与被引用的变量有相同的地址
 102         声明引用变量时必须初始化,    int& b; //错误
 103         必须确保引用是和一块合法的内存块关联( NULL 不可内存不可引用)。
 104         可以建立数组引用。
 105     6)引用一旦初始化,不能改变。(原因如上:本质)
 106         int &b = a;     b = c (这种写法并不是改变b的指向,是将c的值赋给 b; b并没有指向c);
 107     7)(常量引用)const int &b = a;   b的值、指向都不能改变。
 108         因为它等同于: const int *const b = &a;(这种写法常用来保护"实参"不受"形参"的改变).
 109     8)C++编译器在编译过程中使用"常指针"作为引用的内部实现,因此引用所占用的空间大小与指针相同。(非官方的说法,但是大家都这么说)
 110     9)
 111         //建立普通变量的引用
 112         int ma = 9;            
 113         int &ra = ma;        //建立引用。 ===》 int *const ra = &ma;    即 ra = &ma;
 114         ra = 88;            //通过引用修改变量的值。这步是编译器帮忙进行解引用,然后赋值的。
 115         
 116         //建立对指针的引用
 117         int *p2 = NULL;        
 118         int *&mp2 = p2;        //建立引用。===》 int ** const mp2 = &p2;
 119         mp2 = (int *)malloc(sizeof(int));    //通过引用给指针p2分配空间。也就是给p2重新赋值,让他重新指向。
 120 
 121         //建立对数组的引用
 122         typedef int Arr[10]; /*建立数组类型 */   Arr a;    //建立一个普通数组
 123         Arr &p3 = a;    //建立引用 ===》 Arr * const p3 = &a;
 124         p3[3] = 10;   //对数组的第四个元素赋值。
 125 
 126 3.引用的几点基本知识:
 127     1)单独定义引用时,必须初始化;说明它很像一个常量,因为常亮在定义时也必须初始化(const int a = 5)。
 128     2)    普通引用有自己的空间。(在32位平台下占4个字节。)但是引用变量的地址和初始化它的变量是同一块地址。
 129         int &a = b;    a 和 b 的地址相同。()
 130 struct teacher {int a; char b; int &d; double &c; };        这个结构体所占内存为16;
 131 struct teacher {int a; char b; };        这个结构体所占内存为8;
 132     3)引用的本质是一个常量指针。
 133     4 134 
 135 3.函数中的引用:引用做函数的参数,引用做函数的返回值
 136     1)引用做参数不需要初始化
 137     2)不能返回局部变量引用;(和返回局部指针变量原因一样)。
 138     3)引用做返回值。(可以做左值和右值)
 139     4)指针的引用。
 140     5)(常量引用):const 对引用的使用(如上:)。const 引用的值不能修改(主要用在函数的形参:不想用形参改变实参的值)
 141     6"函数的返回值当左值需要返回引用。"
 142 
 143 4.类:
 144     1)使用class关键字
 145     2)类里面可以放变量、函数。
 146     3public: 访问权限
 147     4 148 
 149 5.内联函数:C++中使用(既有宏函数的效率,又没有普通函数的开销;可以像普通函数那样,进行参数,返回值类型的安全检查,又可以
 150     作为成员函数。        在C++中,定义内联函数,只是对编译器的一个建议,并不一定会成为内联函数。
 151 (内联函数的语法)
 152     1)普通函数;  inline void func(int x){  return; }但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。 
 153     2)要求:不能存在任何形式的循环语句;不能存在过多的条件判断语句;    函数体不能过于庞大;    不能对函数进行取址操作
 154 
 155 6.宏函数:C语言中使用
 156     1#define      ADD(X, Y)  X+Y
 157         int main() {int ret = ADD(10,20); return 0;}
 158     2)副作用很多:无脑替换,不检查语法;没有作用域,从定义开始到文件结束
 159 
 160 
 161 7.函数的默认参数和占位参数:
 162     1)默认参数:int func(int x = 10,int y = 20);此时形参的赋值就是默认参数,当函数调用不传参数时,就将使用默认参数。。
 163     2)注意:
 164         int func051(int x, int y = 0, int z = 0);//函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数。
 165         函数的声明和函数的定义不能同时写默认参数,("即使默认参数相同也不行")编译器不知道该选择哪套
 166     3)占位参数:
 167     "函数的占位参数也是参数,必须要给个值,只是函数内部用不了而已. "  int func(int , int y);或 int func(int ,int y = 3);
 168     当占位参数与默认参数结合时:int func(int =5, int y);"erro" ;这种写法,int (int y, int =30);占位参数只能写在最后一个形参的位置。
 169     而且可以不给它传参。因为它有了默认参数。
 170 
 171 8.函数重载:
 172     1)函数重载的条件:
 173     "可以作为条件的:"
 174     同一个作用域:函数名相同,形参的个数、形参的类型、形参的类型顺序不同; 
 175const 进行修饰的函数也可以进行重载。 非 const 对象优先调用非 const 函数。
 176     const 对象只能调用 const 函数,const 函数只能调用 const 函数,可以被 const 函数和非 const 函数调用。
 177     注意:不可以作为条件的
 178           "函数的返回值不能作为函数重载的条件"
 179           "函数重载和默认参数不能同时出现";函数重载碰到默认参数,那么要考虑是否会出现函数调用二义性(会报错,编译通不过)。
 180     2)重载函数的调用:
 181     正常调用:"函数调用正常匹配函数形参(可以找到)。"
 182     隐式类型转换后调用:"当找不到匹配的形参时,编译器会进行隐式转换,仍然找不到后会进行报错。(如下)"
 183      void func(char b); int main(){ int a = 3; func(a);}  此时就会进行隐式转换,因为与ASCII码匹配。
 184      
 185     3)函数重载的原理:
 186         编译器为了实现函数重载,在编译的时候做了一些优化,用不同的类型来修饰不同的函数名。
 187         如:void func(){}
 188             void func(int a){}
 189             void func(int a,char b){}
 190         上述三个函数编译完后:生成的函数名为:_z4funcv    "v代表void,无参数"
 191                                               _z4funci   "i代表参数为int类型"
 192                                               _z4funcic      "i代表第一个参数为int类型,第二个参数为char 类型"
 193 
 194 
 195 "天命不足畏,祖宗不足法,人言不足恤"
 196 第三天 C++基础班 ************************************=============================================
 197 
 198 1.struct 的区别(C 和 C++):
 199     C语言中只能定义变量。
 200     C++语言中可以定义变量和函数。同时C++语言中,struct 中所有变量和函数都是 "public" 权限
 201 2.类的封装:
 202 3.类内部的三种权限
 203     public:共有属性(修饰的成员变量和方法; 可以在类的内部和外部使用。)
 204     private:私有属性(修饰的成员变量和方法,只能在类的内部使用,不能在类的外部使用)
 205     protected:主要用于继承,保护属性(修饰的成员变量和方法; 可以在类的内部和继承的子类使用,不能在类的外部使用)
 206 4.structclass 的区别:
 207     struct 中成员的默认权限为 public;
 208     class  中成员的默认权限为 private;
 209 5.类的调用(一个类调另一个类)
 210 6.对象的构造和析构;
 211     1)构造函数:"名称和类名相同""没有返回值 ""可以有多个,进行函数重载"
 212                 在内存开辟之后调用构造函数。
 213 
 214     2)无参构造函数和有参构造函数(又分为两种)
 215         1.无参构造函数:定义对象的时候,对象后不能加括号。如:class stu{ stu(){"无参构造函数"}};  stu st;(正确定义对象)。
 216                     如果加上括号: stu st(); 编译的时候不会报错,(因为编译器把它当作函数声明),运行的时候会报错。
 217         2.有参构造函数:
 218             "普通参数":    根据形参的类型和个数,也可以进行函数重载。
 219 class Animal{
 220     public:    
 221         Animal(int age)
 222         {
 223         cout << "一个参数构造函数数字!" << endl;
 224         mName = "undefined";
 225         mAge = age;
 226         }
 227         Animal(string name)
 228        {
 229             cout << "一个参数构造函数字母!" << endl;
 230             mName = name;
 231             mAge = 0;
 232         }
 233         Animal(string name,int age)
 234        {
 235             cout << "两个参数构造函数!" << endl;
 236             mName = name;
 237             mAge = age;
 238         }
 239         //拷贝(复制)构造函数  用一个对象来初始化另一个对象
 240         Animal(const Animal& animal)
 241         {    //拷贝构造函数,形参必须是引用。否则会造成死循环,一直调用它本身。本质上成了递归调用本身。
 242             cout << "拷贝构造函数!" << endl;
 243             mName = animal.mName;
 244             mAge = animal.mAge;
 245         }
 246             "拷贝参数":用一个对象来初始化另一个对象
 247             它的形参必须是引用。如果是普通变量,就会造成本质上的递归调用本身;无意义。
 248 
 249     3)析构函数:"名称=类名+"~""; "没有返回值""没有参数""一个类只可以有一个析构函数"
 250         在内存释放之前调用析构函数。
 251 7.构造函数调用规则:  (参考上面的代码)
 252     1) 括号法;
 253     Animal animal1("smith");    //一个参数构造函数字母!
 254     Animal animal2(20);            //一个参数构造函数数字!
 255     Animal animal3("John",30);    //两个参数构造函数!
 256     Animal animal4(animal3);    //拷贝构造
 257     2)显示调用构造函数(匿名对象调用拷贝构造的问题)
 258     Animal("Smith",30);         //匿名函数生命周期仅限于当前行;此行运行完后立即析构。
 259     Animal animal5 = Animal(30);    //    一个参数构造函数数字!;形参为普通变量
 260     Animal animal5 = Animal("smith",90);    注意:/"两个参数构造函数!;拷贝构造。这行比上一行多了一个拷贝构造,是因为string容器的缘故。只有这时才会有拷贝构造"
 261     Animal animal5(animal(30));        //一个参数构造函数数字!。
 262     Animal animal5(animal(30"smith"));    "两个参数构造函数!;拷贝构造。"    原因同上,当不调用 string 参数时,它不会调用拷贝构造。"是由编译器处理的,具体处理方式不清楚"
 263     "上面那两种情况,调不调用拷贝构造函数,主要看参数类型。===有string:就调拷贝构造函数;==== 没有string: 就不调拷贝构造函数===="
 264     匿名对象如果有变量来接,它就是一个对象;"此时这个变量就相当于匿名对象的名称。"
 265     匿名对象如果没有变量来接,他就是一个实例化对象。
 266     Animal4("john",92); Animal(animal4);    此时当这两句在一起,编译器会报错,重定义。
 267     3)等号法
 268     Animal animal5 = 10//不常用。调用一个函数构造函数
 269     Animal animal5 = animal4l;    “拷贝构造,常用
 270 8.拷贝构造常用的两种调用方式:
 271     Animal animal5 = animal4l;
 272     Animal animal5(animal4l);
 273 
 274 9.拷贝构造的调用时机:
 275     1)对象以值传递的方式传递给函数参数。    void func(Animal animal2){ };    用实参初始化形参,调用了拷贝构造函数
 276     2)用一个对象初始化另一个对象。Animal animal5 = animal4l;  或者:Animal animal5(animal4l);
 277     3)函数返回局部对象。(注意这里:debug模式 和release模式不一样)。
 278     debug模式下:会调用拷贝构造,打印两个地址也不相同。
 279     release模式下,不会调用拷贝构造,打印的地址相同,编译器这里做了优化,直接把原来的对象返回回去了。
 280     
 281     Animal MyBussiness()
 282     {
 283     //局部对象
 284     Animal animal;            //无参构造
 285     cout << &animal << endl;
 286     return animal;        //编译器先拷贝一份,然后返回。相当于返回一个匿名对象
 287    }
 288    void test03()
 289    {
 290     Animal animal = MyBussiness();        
 291     cout << &animal << endl;
 292    }
 293 
 294 10.构造函数调用规则:
 295     默认情况下,编译器至少为我们写的类增加三个函数
 296     1)默认构造函数(无参,函数体为空)
 297         默认析构函数(无参,函数体为空)
 298         默认拷贝构造函数,对类中非静态成员属性简单 ""拷贝。
 299     2) 如果用户定义了"拷贝构造函数",C++不会再提供"任何默认构造函数" 300     3)如果用户定义了"普通构造,"C++"不会"再提供"默认无参构造",但是"会提供默认拷贝构造" 301     4)在构造函数中调用构造函数是一个危险的行为。本质上:里面的构造函数相当于一个"匿名对象"。生命周期只有本行(没有变量接的情况下)。
 302 
 303 11.初始化列表:初始化成员列表只能在构造函数使用。
 304      "构造函数的初始化列表顺序:"
 305          1)当一个类中组合了其他类的对象,先执行被组合的构造函数。
 306          2)如果组合对象有多个,来自不同的类,根据定义的顺序进行构造函数的调用,而不是初始化列表的顺序。
 307          3)“被组合对象的构造顺序:与定义顺序有关系,与初始化列表的顺序,没有关系”
 308          4)若类成员中有 const 修饰,必须在对象初始化的时候,给 const 元素初始化。此时就用到了"初始化列表" 309          
 310          "析构函数:与构造函数的调用顺序相反。"
 311 
 312 12.深拷贝和浅拷贝
 313 class Person
 314 {
 315 public:
 316     Person(int a, int b) :_b(b),_a(b){}            "初始化列表"
 317     "深拷贝"
 318     Person(const Person &p1)
 319     {
 320         mstr = (char *)malloc(strlen(p1.mstr) + 1);        "核心步骤"        “会进行内存拷贝”
 321         strcpy(mstr, p1.mstr);
 322         _a = p1._a;
 323         _b = p1._b;
 324     }
 325 
 326     "浅拷贝"
 327     Person(const Person &p1)
 328     {
 329         mstr = p1.mstr;        "这里只会把一个对象的指针地址,赋给另一个对象的指针。";“不会进行内存拷贝”
 330         _a = p1._a;
 331         _b = p1._b;
 332     }
 333     void print_func()
 334     {
 335         cout << _a <<"   " << _b << endl;
 336     }
 337 private:
 338     int _a;
 339     int _b;
 340     char *mstr;
 341 };
 342 
 343 13.C++中 string 是一个类。
 344 
 345 14.已知一个Print类,在该类的main()函数里执行语句 "Print a, b[2], *c;" 后;程序会自动调用该类的构造函数 4 次。
 346     a会调用一次;b会调用两次;*c是空指针,没有开辟空间不会调用。
 347 
 348 15.
 349 
 350 "天命不足畏,祖宗不足法,人言不足恤"
 351 第四天C++ ++++++=======================================================================================
 352 
 353 1.explicit:修饰构造函数; 禁止编译器隐式转换
 354     1)针对单参数的构造函数  2)或者除第一个参数外其他参数都有默认值的多参构造函数。
 355 
 356 2. C++ 中动态分配内存:
 357     new :在堆上分配内存。分配对象的堆内存时会调用构造函数
 358     申请string类型的空间只能string类型数据,可改为string *ps = new string(“10”);
 359     delete:释放堆内存,在释放堆内存前会调用析构函数。
 360     delete void*指针,不会调用析构函数。
 361 
 362 3.创建动态数组:
 363     int * arr = new int[10];    创建一个 int 类型数组,元素10个。
 364     delete [] arr;    释放内存。
 365 4.创建自定义对象数组,必须提供无参构造函数。
 366     同时数组有几个元素就调用几次"无参构造函数和析构函数" 367     delete void* 指针可能会出错。没有调用析构函数
 368 class print
 369 {
 370 private:
 371     static int a;        //在类中声明静态变量
 372 public:
 373 
 374 };
 375 int print::a = 0;        //在类外初始化静态变量
 376 
 377 5.静态成员变量:
 378     1)在编译阶段就分配空间。对象还没有创建
 379     2)必须在类中声明,在类外初始化(或定义)。
 380     3)归同一个类的所有对象所有,共享同一个静态变量。在为对象分配的空间中不包含静态成员所占空间
 381     4)有权限限制:private静态变量  和 public静态变量。
 382         private静态变量;在类外不能访问和操作。    类外访问:print::a;"erro";    类外操作:print::a = 6;"erro"
 383         public静态变量;在类外可以访问和操作。    类外访问:print::a;"yes";    类外操作:print::a = 6;"yes"
 384     5const 静态成员变量(const static int a = 8);
 385         定义静态变量 const 静态成员变量时,最好在类的内部初始化。
 386 
 387 "静态变量的调用:"
 388     用类名加域作用符: print::a;
 389     用对象名加取址符: class p1;     p1.a;
 390 
 391 6.静态成员函数:(主要是为了访问静态变量)
 392     与静态变量一样,在没有创建对象前即可通过类名调用。
 393     1).静态成员函数 只能访问 "静态成员变量",不能访问普通成员变量。
 394     2).静态成员函数的使用规则和静态成员变量一样
 395     3)静态成员函数也有使用权限。类外无法访问私有静态函数。
 396     4)普通成员函数可访问"静态成员变量",也可访问"非静态成员变量"
 397 
 398 
 399 
 400 7.单例模式:一个类只存在一个对象。
 401     条件:1)构造函数、拷贝函数、析构函数私有化。
 402           2)提供私有化静态成员变量指向一个对象
 403           3)提供外部获得对象的静态成员函数。
 404     class person
 405     {
 406     public:
 407         void print_func(string content)
 408         {
 409             cout << "打印内容" << content << endl; 
 410             mcount++;
 411             cout << "打印次数" << mcount << endl;
 412         }
 413         static person *getInstance(){        //静态成员函数 “在公共区域”,提供接口
 414             return p1;
 415         }
 416     private:
 417         person(){                    //私有化构造函数
 418             mcount = 0;
 419         }        
 420         person(const person &){}    //私有化拷贝函数
 421         ~person(){}                    //私有化析构函数
 422     private:
 423         static person * p1;            //私有化静态成员变量
 424          int mcount;
 425     };    
 426 person *person::p = new person;    (类外初始化静态变量)//私有化的静态成员变量指向一个对象
 427 
 428 int main(void)
 429 {
 430     person *p1 = person::getInstance();        p1和p2的地址一样。
 431     person *p2 = person::getInstance();        因为他们为指向同一个对象。
 432 
 433     p1->print_func("晚上好!!!");
 434     p2->print_func("加油,胜利就在眼前!!!");
 435 
 436     return 0;
 437 }
 438 
 439 8.C++ 成员变量和函数的存储:(成员变量和函数分开存储)
 440     1)静态变量不在类对象中存储
 441     2)函数不在类对象中存储(静态函数与非静态函数都是)
 442     3)类对象中只存储 普通成员变量。
 443 
 444 9.this 指针:(因为C++成员的存储特点。)
 445     1)this指针永远指向当前对象。
 446     2"静态成员函数" 内部没有 this 指针,所以静态成员函数不能操作非静态成员变量。
 447   "this 指针的使用:"
 448   1)当形参与成员变量同名时,可以通过this指针区分。
 449   2)在类的非静态成员函数中返回对象本身。可以使用:return *this 450 class person
 451 {
 452 public:
 453     person(int a, string name)
 454     {
 455         this->a = a;
 456         this->name = name;
 457     }
 458     person func()
 459     {
 460         return *this;
 461     }
 462 
 463 public:
 464     int a;
 465     string name;
 466 };
 467 
 468 10.空对象指针访问成员的不同情况:由于( this 指针的存在)
 469     1)当不访问成员变量的时候是允许的。
 470     2)当访问成员变量时,函数通过this指针寻找成员变量,此时就会报错。
 471 
 472 class ARE
 473 {
 474 public:
 475     void print(){
 476         cout << "hello world !!1" << endl;
 477     }
 478     void get_func(){
 479         cout << ma << endl;
 480     }
 481 
 482 public:
 483     int ma;
 484 };
 485 
 486 int main(void)
 487 {
 488     ARE *a1 = NULL;
 489     a1->print();        "可以访问,因为此时不访问成员变量"
 490     a1->get_func();    "不可以访问,它队成员变量进行了访问"
 491     return 0 492 }
 493 
 494 10.const 对成员函数的修饰。(int func() const {});注意:const 要写在小括号后面,大括号前面。(===》int func(const ARE * const this ))
 495     1const 修饰 this 指针指向的内存区域。成员函数体内不可改变类中任何变量,除非变量前面用 mutable 进行修饰。
 496     mutable 对成员变量的修饰
 497     1)用它修饰后,任何情况下都可以对变量进行修改。
 498 11.const 修饰对象(常对象)。(不允许修改对象的属性)
 499     1)常对象可访问 const 或非 const 变量,不能修改变量。除非变量前用 mutable 修饰。
 500     2)常对象只能调用 const 修饰的常函数。
 501 
 502 
 503 12.友元函数。(全局函数和成员函数两种)
 504     1).friend 关键字只出现在声明处。
 505     2).其他类、类成员函数、全局函数都可声明为友元。
 506     3)友元函数不是类的成员,不带this指针。
 507     4)友元函数可访问对象的任意成员属性,包括私有属性。
 508 
 509 注意:注意:    
 510     1)友元关系不能被继承
 511     2)友元关系是单向的,类A是类B的友元,但类B不一定是类A的友元。
 512     3)友元关系不具有传递性。类B是类A的友元,类C是类B的友元,但类C不一定是类A的友元。
 513 
 514 #include <iostream>
 515 #include <string>
 516 using namespace std;
 517 class COp2;        "注意:这里必须对第二个类进行声明"
 518 class Myp1
 519 {
 520 public:
 521     void printf(COp2 &);        "这个类中的函数对另一个的成员变量进行操作"
 522 };
 523 
 524 class COp2
 525 {
 526     friend void print_func(COp2 &c1);        "全局函数 友元函数的声明"
 527     friend void Myp1::printf(COp2 &);        "类成员函数 友元函数的声明。注意这里只能声明"
 528     friend class Myp1;        "友元类的声明,此类中的所有函数都可以对声明的类中的变量进行访问"
 529 public :
 530     COp2(int a, string name)
 531     {
 532         this->a = a;
 533         this->name = name;
 534     }
 535 private:
 536     int a;
 537     string name;
 538 };
 539 
 540 void Myp1::printf(COp2 & c1)        "成员函数的 友元函数的实现""
 541 {
 542     c1.a += 1;
 543     cout << c1.a << endl;
 544     cout << c1.name << endl;
 545 }
 546 
 547 void print_func(COp2 &c1)        "全局函数的 友元函数的实现"
 548 {
 549     cout << c1.a << endl;
 550     cout << c1.name << endl;
 551 }
 552 void test()
 553 {
 554     COp2 c1(30,"bei jing");
 555     Myp1 m1;
 556     m1.printf(c1);
 557 }
 558 
 559 int main(void)
 560 {
 561     COp2 c1(30, "bei jing");
 562     Myp1 m1;
 563     m1.printf(c1);
 564     system("pause");
 565     return 0;
 566 }
 567 
 568 
 569 第五天C++==================================++++++++++++++++++++++++++++++++++++++++=
 570 
 571 1.友元函数案例:
 572     1)电视机的状态
 573     
 574     2)遥控调试
 575 
 576 
 577 2.数组案例:
 578     1)分文件( int  类型的数组)
 579     2 580 
 581 3.操作符重载:
 582     1)本质:"函数的调用" 583         函数的参数取决于两个因素:
 584             1)运算符是一元还是二元。
 585             2)运算符被定义为全局函数还是成员函数。
 586 
 587     2)运算符重载的限制:
 588         1)使用运算符重载不能改变预算符的优先级,不能改变运算符的参数个数。
 589         1)不能重载的运算符:"."; "::"; ".*"; "? :"; "sizeof".
 590         2)除"="号外,基类中重载的运算符都将被派生类继承。
 591     3)特殊运算符:
 592         1)“=”; “[]”; “()”;  “->”;操作符只能通过成员函数重载
 593         2)“<<”;  “>>”;     只能进行友元函数重载
 594         3)不要重载 “&&”; “||” ; 操作符,因为无法实现短路原则。 
 595     4.运算符重载建议:
 596         1)所有的一元运算符,          建议使用:成员函数
 597         2)“=”;“[]”;“”;“->”;   必须使用成员函数
 598         3)+=,-=,*=,&=,!=,%=,^=,    建议使用:成员函数
 599         4)其他二元运算符                建议使用:非成员函数
 600     5.重载"="号操作符的步骤
 601         1)先释放旧内存
 602         2)返回一个引用,实现链式编程。
 603 
 604 考试天==================================================================================
 605 1.断言函数;  assert(终止进程,抛出异常)   头文件:<assert.h>
 606 2. strcpy 返回值为目标内存的地址;是为了实现链式编程。
 607     int n = strlen(strcpy(des,src));    链式编程。
 608 3.
 609 
 610 
 611 C++第六天========================================++++++++++++++++++++++++++++++++++++++++++++++++++=
 612 "运算符重载"
 613 1.前置++;前置-- 614 2.后置++;后置-- 615     要创建一个临时变量接收计算时的那个变量,记住要把临时变量返回去。
 616 3.== 和 != 号的重载。
 617 
 618 4.继承:
 619     1)C++中的继承方式(public, private, protected)会影响子类的对外访问属性。判断某一句话,能否被访问
 620     (三看原则 )
 621         1)看调用语句,这句话写在子类的内部、还是外部
 622         2)看子类如何从父类继承(public, private, protected 623         3)看父类中的访问级别(public, private, protected);
 624 
 625     2)继承的使用情况:
 626         1protected 继承,只能在家族内部使用,不能在类的外部使用
 627         2)项目开发中,通常都是 public 继承。
 628 
 629 5.继承的类型兼容原则:
 630     1)子类对象可以当作父类对象使用
 631     2)子类对象可以直接赋值给父类对象
 632     3)子类对象可以直接初始化父类对象
 633     4)父类指针可以直接指向子类对象
 634     5)父类引用可以直接引用子类对象。
 635 
 636 6.继承与组合混搭情况下,构造和析构函数调用原则
 637     1) 先调用父类构造函数,再调用组合类构造函数,最后调用自己的构造函数
 638     2)先调用自己的析构函数,再调用组合类析构函数,最后调用父类析构函数,
 639     3)先构造的对象后释放
 640 
 641 7.不可以被继承的函数
 642     1)基类中的构造函数和析构函数
 643     2)基类中重载的运算符 = 号函数
 644 
 645 8.继承中同名成员函数和变量的问题:
 646     1)如果子类和父类中有相同的函数和变量名;子类调用时默认调用自身的函数和变量。
 647     2)如果想调用父类的函数和变量,需要加上 "类名和域作用域符",显式调用。
 648 
 649 9.继承中的静态成员特性:
 650     1)基类中定义的静态成员,可被所有派生类共享
 651     2"静态成员的访问权限和普通成员一样";遵守派生类的访问控制
 652     3)与函数重载一样,如果子类中重新定义了同名的静态函数,基类的函数将被隐藏
 653     4)静态成员函数不能是虚函数。
 654     
 655 9.继承中的函数重载现象;
 656     1)"如果重新定义了基类中的重载函数,"只要函数名相同,继承的基类中的重载函数将被隐藏。
 657         想要调用必须使用域作用符显式调用。
 658 
 659 9.多继承的概念:
 660     1)多继承可能会出现二义性; 解决办法:显示的调用不同类中的同名属性或方法
 661     2)多继承的相同数据重复继承。解决办法:虚继承:virtual 既保持一份数据,也不会产生二义性。
 662  663 10.菱形继承解决办法:
 664     加上 virtual ,用虚继承。
 665 11.多态 :分为:静态多态(编译时多态)和动态多态(运行时多态)。
 666 面向对象编程的设计思路:"开闭原则(对修改关闭,对扩展开放。)"
 667     C++的多态性是通过虚函数来实现的。(虚函数允许子类重新定义父类成员,这种做法叫做重写或覆盖)。
 668 12.向上类型转换
 669 
 670 13.虚函数:函数前加 virtual 修饰
 671     1virtual 关键字只能修饰成员函数
 672     2)构造函数不能为虚函数
 673     3)创建一个虚成员函数,只需在函数声明前加上 virtual 关键字。
 674     4)如果一个函数在基类中被声明为 virtual ,那么它在所有派生类中都是 virtual ;它派生类的函数前可以加也可以不加 virtual
 675     关键字;推荐加。
 676 
 677    纯虚函数:virtual int func() = 0; 
 678    1)当一个类中有纯虚函数时,这个类为抽象类;"同时注意:不能实例化一个抽象类,编译时会报错" 679    2)当继承一个抽象类时,必须实现所有的纯虚函数,否则抽象类派生的类也是一个抽象类。
 680 
 681 14.建立公共接口的目的:
 682     1)将子类中公共的操作抽象出来。可以通过一个公共接口操作呢一组类。这个公共接口不需要事先实现,即创建一个公共类。
 683 
 684 15.模板方法:就是通过创建一个公共借口来实现的。
 685 
 686 
 687 
 688 "==========================================================================================="
 689 一.多态的思想:
 690     1.封装:突破C函数的概念,。。。用类做函数参数的时候,可以调用对象的属性和对象的方法
 691     2.继承:A B代码可以复用,可以使用前人的代码
 692     3.多态。可以使用未来的代码,架构不用改变
 693 二.    多态成立的条件:
 694     1.要有继承
 695     2.要有虚函数重写
 696     3.用父类指针或父类引用 指向子类对象
 697 
 698 "==========================================================================================="
 699 
 700 C++第七天========================================++++++++++++++++++++++++++++++++++++++++++++++++++=
 701 
 702 1.虚析构函数的特点和目的:
 703     1)通过父类指针,把所有子类对象的析构函数都执行一边。
 704     2)通过父类指针,释放所有的子类资源
 705 
 706 2.函数的重写、重载、重定义
 707     1)重载:
 708         必须在同一个类中
 709         重载是在编译期间根据 参数类型和个数决定函数调用的
 710         子类无法重载父类的函数,父类同名函数将被覆盖
 711 
 712     2)重写:
 713         必须发生在父类与子类之间。(同时分为两类)
 714         并且父类与子类的函数有完全相同的原型。
 715         1virtual 具有这个关键字的,发生重写时会产生多态。
 716         2)无这个关键字的,叫重定义。
 717     3)重定义:(非虚函数重写叫重定义)
 718         父类与子类的有完全相同的函数原型,而且无 virtual 关键字修饰,此时叫做函数重定义。
 719 
 720 3.子类中的 vptr 指针的分步初始化
 721     1)当创建子类对象时,如果它具有 vptr 指针, 那么此时是分步初始化的。
 722     2)当执行父类的构造函数时,子类的 vptr 指针会指向父类的虚函数表
 723         当父类的构造函数执行完毕后,会把子类的 vptr 指针指向子类的虚函数表
 724     3)结论:子类的vptr指针是分步初始化的。它在程序运行时会进行寻址操作,效率会降低。不建议将每个成员函数都声明为虚函数。
 725 
 726 4.纯虚析构函数:
 727 
 728 5.多态的案例:
 729     1)思路:
 730         1.定义一套自己的数据接口,打包放在结构体中,数据接口使用函数指针。
 731         2.初始化模块接口。(将自己定义的接口与生产商实现的函数联系在一起。)
 732         3.
 733 
 734 
 735 6.函数指针
 736 
 737 
 738 
 739 C++第八天================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 740 
 741 1.C++的模板(用于实现泛型编程)
 742     1)模板关键字: template<class T,class T1>或 template<typename T, typename T1>    T,T1代表数据类型;
 743     2)定义的模板关键字当前行下一个函数或类有用。
 744     3)分为 函数模板 和 类模板;
 745     4)一旦声明了多个类型 T ,不管用或不用,都必须给它指定类型。
 746 
 747 2.函数模板的编译机制:(原理)
 748     1)函数模板具有自动推导功能
 749     2)编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
 750     3)函数模板通过具体的类型产生不同的函数。
 751 
 752 3.模板函数与普通函数的区别
 753     1)函数模板不允许自动类型转换,必须严格匹配。
 754     2)普通函数能自动进行类型转换
 755     template<class T> 
 756     T ptint(T a, T b ){ }
 757 
 758     int show(int a,int b){ }
 759     void main(){ int a = 6; char b = 'c';  print(a,b);"error",两个变量类型必须相同,不允许进行自动转换    
 760                   show(a, b);"YES"   “普通函数能自动进行类型转换”};
 761 
 762 4.函数模板和普通函数一起调用规则
 763     1)函数模板可以向普通函数一样被重载
 764     2)如果函数模板与普通函数一样,C++编译器优先调用普通函数。
 765     例如:    template<class T> 
 766             void print(T a, T b ){ }
 767             void print(int a, int b){ }
 768             void  main(){ int a = 4; int b = 9; print(a, b); "此时优先调用普通函数"}
 769     3)如果函数模板能产生更好的匹配,则优先调用函数模板。
 770     例如:template<class T,class T2> 
 771             void print(T a, T2 b ){ }
 772             void print(int a, int b){ }
 773             void  main(){ int a = 4; char b = 'u'; print(a, b); "此时优先调用函数模板"}
 774     4)可以通过空模板实参列表的语法限定编译器只能调用模板函数
 775         例如:(此目录第二个函数例子)
 776             void main(){ int a = 4; int b = 6; print<>(a, b); }
 777 4.类模板:
 778     1)只能显示的指定类型,不具有自动推导功能。
 779     2)类模板做函数形参(必须显示指定模板类型)
 780     3 781 
 782 5.类模板与函数模板(有自动推导功能)的混合使用案例,有奇效。
 783 template <class T1,class T2>
 784 class Person
 785 {
 786 public:
 787     Person(T1 name, T2 age)
 788     {
 789         this->mName = name;
 790         this->mAge = age;
 791     }
 792 public:
 793     T1 mName;
 794     T2 mAge;
 795 };
 796 //类模板做函数参数(指定类型)
 797 void Dobusiness(Person<string, int> &p1)
 798 {
 799     cout << "name:" << p1.mName << " age:" << p1.mAge << endl;
 800 }
 801 //类模板与函数模板混合使用(使用函数模板的自动推导功能)
 802 template <class T1, class T2>
 803 void Dobusiness02(Person<T1, T2> &p1)
 804 {
 805     cout << "name:" << p1.mName << " age:" << p1.mAge << endl;
 806 
 807 }
 808 void test04()
 809 {
 810     Person<string, int > p1("jonh", 30);
 811     Dobusiness(p1);
 812 
 813     Person<string, int > p2("jonheqeq", 303);
 814     Dobusiness02(p2);
 815 }
 816 
 817 6.类模板派生普通类和类模板类:
 818     1)继承的话,应该继承一个具体的类,因为一个具体的类,编译器才知道分配多大的内存。
 819     2)如果想继承类模板,应该给继承过程中的类模板显示指定类型。
 820     例如(类模板派生普通类)
 821         template <class T>
 822         class MyClass{  private:     "父类模板"
 823                         T age;  };
 824         class SubClass : public Mycalss<int>{    "普通类,这里继承时,必须对类模板显示指定类型,编译器才知道给父类分配多大的内存"    
 825             public:
 826                 int a;
 827         };
 828     例如(类模板派生子类模板)
 829         template <class T>
 830         class MyClass{  private:     "父类模板"
 831                         T age;  };
 832         template <class T>            
 833         class SubClass : public Mycalss<T>{    "子类类模板,这里继承时,可以用子类的类型指定父类的模板"    
 834             public:
 835                 T a;
 836         };
 837         
 838 
 839 7.类模板分文件编写
 840     C++文件的编译过程:先每个文件独立编译,然后由链接器将编译好的文件链接在一起(这一步就是寻找各种调用函数,和头文件的定义内容)
 841     需要引入 .cpp 文件,原因:
 842         1)C++编译机制的原因;(分文件编译导致的)
 843         2)二次编译有关
 844     解决办法文件,将他们放入:    
 845         类模板的声明与定义要放在一个文件中编写。创建一个.hpp将他们放入
 846 
 847 
 848 "C++的类型转换"
 849 
 850 8.静态类型转换:static_cast<>;
 851     1)用于类层次结构中基类和派生类之间"指针或引用"的转换
 852         1)上行转换:把派生类的指针或引用转换成基类的是安全的    Animal *ani = static_cast<Animal*>( dog ) ;
 853         2)下行转换:把基类的指针或引用转换成派生类的是不安全的,因为没有动态类型检查。    Dog *dog = static_cast<Dog *>(ani);
 854         3)总结一句就是:可以把大的转成小的; 不能把小的转成大的。
 855     
 856     2)当两个类之间无继承关系时,不可以进行转换。
 857      如:Teacher *t1 = static_cast<Teacher *>(dog);  "error,因为两个类之间无关系"
 858 
 859     3)基础数据类型之间的转换:
 860         1)可以把高精度的类型转换成低精度的;double da; int a = static_cast<int>(da);
 861         2)当把低
 862         精度的类型转换成高精度时,安全性需要程序员保证。(原理和上一样)    double da = static_cast<double>(a);
 863 
 864 9.动态类型转换:dynamic_cast<>(类与类之间的转换)
 865     dynamic_cast<> 具有类型检查功能,比 static_cast<>类型转换安全。
 866     1)只能转换具有父子关系的"指针或引用".
 867         上行转换:只能将子类指针转换成父类指针(可以大转小)
 868         下行转换:不能将父类指针转换成子类指针(不能小转大);
 869     例如: parent *parent = dynamic_cast<parent*>son;
 870         
 871 10.数组模板:
 872     "1)自定义类型做元素构建数组时,必须提供无参构造函数。"
 873 
 874 
 875 C++第九天================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 876 
 877 1.常量转换:(const_cast<>)用来修改类型的 const 属性。
 878     1)常量指针、引用 被转换成 非常量指针、引用,并且仍然指向原来的对象()互相转换,将非常量转换为常量。
 879     例如u:
 880     2)不能直接对非指针、非引用的变量直接使用 const_cast<> 操作符去移除它的 const 属性。
 881 
 882 2.重新解释转换或强势类型转换。reinterpret_cast<>(不安全的转换,各种类型之间的强转)
 883 
 884 3.异常的处理(需要了解 C 的异常处理 和C++的处理)
 885     C的异常处理的缺陷。和特点
 886 
 887 4.C++的异常特点
 888     1)异常不能被忽略,而且异常可以跨函数,异常并不是简单的 int  型数字,最好有明确的意义。
 889     2)C++提供的异常机制,具有跨函数和不可忽略的特点。
 890     3)异常捕获的严格类型匹配。
 891 
 892 
 893 5.栈解旋:异常被抛出后,从进入 try 起,到异常处理完毕,这期间在栈上创建的所有对象都将会自动析构;析构的顺序与创建的顺序相反。
 894 
 895 6.异常接口声明:
 896     1)可以在函数前声明抛出的异常类型,如果不声明,表示可以抛出各种类型的异常。
 897     例如: void func() throw(int , double, char){ }可以抛出三种类型的异常。
 898             void func() throw(){ }不可以抛出任何类型的异常。
 899             void func() { }    可以抛出任何类型的异常。
 900     2"但是:"
 901     C++的异常规范在不同编译器下可能执行效果会不同。(编译器的不同会导致结果不同)
 902     VS下会忽略C++的异常规范,但是会发出警告,程序照常执行;     QT会报错,终止程序运行。    
 903     通常情况下:如果一个函数抛出了它的异常接口声明所不允许抛出的类型,unexcepted 函数将被调用,该函数默认调用 terminate 函数中断程序。
 904 
 905 7.异常变量的声明周期
 906     1)异常也可以抛出一个类的对象。此时可以使用纯虚函数做接口,声明每种不同的异常对象(减少代码量)。
 907     例如: class BaseException{
 908     public:    
 909         virtual void print_exception() = 0;
 910     };
 911     class TargetException : public BaseException{
 912     public:
 913         virtual void print_exception(){
 914             cout<<"目标 空间空指针异常" <<endl;
 915         }
 916     };
 917     class DestException : public BaseException{
 918     public:
 919         virtual void print_Exception(){
 920             cout<<"源空间的空指针异常"  << endl;
 921         }
 922     };
 923     void CopyString(char *dest, const char *source){
 924         if(dest == NULL){
 925             throw TargetException();
 926          }
 927          if(source == NULL){
 928              throw DestException();
 929         }
 930         memcpy(TargetException,DestException,strlen(source)+1 );
 931      }
 932      int  main(){
 933          const char *source = "nnakew";
 934          char dest[1024] = {0};
 935          try{
 936              CopyString(dest, source);
 937          }catch(BaseException& ex )
 938          {
 939              ex.print_exception();
 940          }
 941          return 0;
 942      }
 943 
 944 
 945 
 946 8.C++标准异常类:系统头文件 <exception>
 947 
 948 9.创建自己的异常类:
 949     引入系统头文件,继承系统的标准 出错类;重载父类的 what 函数和虚析构函数。
 950 
 951 10.标准输入输出操作
 952     cin 是由缓冲区获取文件到 目标内存或变量,若缓冲区没有内容时,才需要从键盘上输入
 953     cout 是有缓冲区输出文件到屏幕的,若缓冲区没有满时,或强制释放,它是不会在屏幕上输出内容的。
 954     endl 的作用是刷新缓冲区,将内容输出到屏幕或文件里。
 955 
 956 10.格式化输入输出操作
 957 
 958 
 959 
 960 C++第十天(STL课程阶段)================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 961 
 962 1.STL六大组件
 963 
 964 
 965 2.STL三大组件:容器、算法、迭代器
 966 
 967     每一个容器都有自己的迭代器,迭代器用来遍历容器中的元素。
 968     遍历:不重复的进行访问每个元素。
 969 
 970 3.容器,迭代器,算法的初步使用。
 971     1)先引入所需要的容器头文件和算法头文件
 972 
 973     void Myprint( int val){         算法中的回调函数,需要自己写,
 974         cout<< val <<" ";            内容为自己所想打印的内容
 975     }
 976     void test(){
 977     vector<int > V;
 978     for (int i = 0; i < 5; i++)
 979     {
 980         V.push_back(i+ 10);
 981     }
 982     vector<int>::iterator pBegin = V.begin();
 983     vector<int>::iterator pEnd = V.end();
 984     
 985     //直接使用迭代器进行容器的遍历
 986     while (pBegin != pEnd)
 987     {
 988         cout << *pBegin << " ";
 989         pBegin++;
 990     }
 991     cout << endl;
 992 
 993     //模拟算法的最后一个回调函数的使用
 994     pBegin = V.begin();
 995     while (pBegin != pEnd)
 996     {
 997         Myprint(*pBegin);
 998         pBegin++;
 999     }
1000     cout << endl;
1001 
1002     //使用算法遍历容器,并且打印出来。注意:算法的最后一个参数为回调函数,需要自己写(所想要打印的内从容)
1003     for_each(V.begin(), V.end(), Myprint);
1004     cout << endl;
1005     }
1006     };
1007 
1008     2)容器的分类:序列式容器和关联式容器
1009         序列式容器:容器元素在容器中的位置由元素进入容器的时间和地点来决定。如:(vector,deque , list, stack, queue )
1010         关联式容器:容器具有自己的规则,元素在容器中的位置由容器的规则来定。如(树状容器: set/mutilset; map/mutilmap )
1011     3)每个容器都有自己的迭代器,由容器自己提供。
1012         
1013 3.string 容器:
1014     1)从 const char * 到 string 有隐式转换;反之则没有。
1015 
1016 3.下标操作符: [] 和 at()方法。
1017     两个的区别在于:1)[] 越界程序会直接挂掉,  2)at()方法越界程序会提供错误信息。
1018 
1019 4.vector 容器:
1020     1)连续的内存空间
1021     2)单口
1022     3)会实现内存空间动态增长(当已有内存占满时);申请更大的空间,原来的空间析构,原来的迭代器会失效。
1023     4)它的内存空间不会随着 clear()方法清除数据而消失的,只有当容器生命周期结束时,它的容量才会释放;
1024         "所以当想在生命周期存在时减少或增大容器内存,可以使用 swap() 方法。"(如下)
1025 void test04(){
1026 
1027     vector<int> v;
1028     for (int i = 0; i < 100000; i++){
1029     v.push_back(i);
1030     }
1031 
1032     cout << "capacity:" << v.capacity() << endl;
1033     cout << "size:" << v.size() << endl;
1034 
1035     v.resize(3);
1036     cout << "capacity:" << v.capacity() << endl;            "注意容量与长度不一样,"
1037     cout << "size:" << v.size() << endl;
1038 
1039     //收缩内存
1040     //vector<int>(v).swap(v);
1041 
1042     vector<int>(v).swap(v); //匿名对象
1043 
1044     cout << "capacity:" << v.capacity() << endl;
1045     cout << "size:" << v.size() << endl;
1046 }
1047     5)reserve的使用:"预留空间",在此空间还没有初始化时是不允许访问。可以通过push_back()插入元素后,进行访问。
1048     6)resize的使用:开辟空间并且初始化,它的空间申请完后是可以访问的。
1049     重新指定容器的长度为num,若容器变长,则默认值或指定值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除,"只是长度变短,但是容器的容量没有变化,"1050     7)只要容器的容量产生变化,原来的迭代器就会失效。(如删除元素之后,原来的迭代器就不能用了)。
1051 
1052 5.迭代器的注意事项:
1053     1"(当内存由于动态增长,更换地址后,原来的迭代器就失效了,不可以再使用)。"
1054     2)只要能遍历容器中所有元素的,都是容器认可的迭代器。
1055     3)每个容器的迭代器不一样,有自己的独立迭代器。
1056     4) const_iterator 只读
1057         reverse_iterator 逆序迭代器
1058         iterator 最普通的正向迭代器
1059     51060 6.迭代器的种类:5种。
1061     1)每个容器只提供一种
1062     2)都是由对应的容器自己提供的迭代器。(因为每个容器的实现机制不一样)。
1063     3)输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器
1064 
1065 
1066 6.vector 申请和释放空间的注意:
1067     尽量不要频繁申请和释放空间(太浪费时间);可以先预测一下所用空间的大小,直接用reserve()申请足够大的空间,在逐步初始化。
1068 
1069 
1070 7.随机迭代器:(vector容器支持随机访问)
1071 
1072 8.deque 容器:
1073     1)是一种双向开口,动态以分段连续内存空间组合而成,随时可以增加一块新内存的容量器。
1074     2)可以在首尾两端分别作元素的删除和插入操作。
1075     31076 
1077 
1078 C++第十一天(STL课程阶段)================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1079 
1080 1.栈容器(stack) :元素先进后出,单口容器
1081     1)不提供迭代器;没有遍历功能
1082     2)以其他的容器作为底层,它相当于提供接口
1083     3)只有栈顶元素才可以被外界取用。所以想遍历容器,只能从栈顶开始,取出一个元素,就删除它,当遍历完后,容器也就没有元素了。
1084 
1085 2.队列容器(queue):元素先进先出
1086     1)没有迭代器。不支持随机访问
1087     2)两个出口,一个只进,一个只出。
1088     3)和 stack 栈容器一样,遍历完后,容器的元素也被删除空。
1089 
1090 3.( stack 和 queue 容器叫受限的线性表)
1091 
1092 3.链表容器(list):双向循环链表
1093     1)采用动态存储分配,不会造成内存浪费和溢出。
1094     2)元素的插入和删除十分方便,修改指针即可。
1095     3)list容器提供自己的排序算法;
1096     4)list容器的排序(重点在回调函数的使用)
1097     5)swap()既可以交换两个链表的数据,也可以动态的伸缩内存。
1098 
1099     
1100 
1101 4.set容器(红黑树,平衡二叉树的一种)
1102     1)它的元素既是键值又是实值,而且所有的元素都会自动排序。
1103     2)不允许两个元素有相同的键值。
1104     3)不允许通过迭代器修改元素的值。
1105     4)与list有某些相同的性质,当对容器中的元素进行插入和删除操作时,操作之前的所有迭代器,在完成操作完成后都是有效的,除了被删除的那个元素的迭代器
1106 
1107 5.multiset容器:
1108     1)特性和set一样,唯一区别在于它允许键值重复。
1109 
1110 6.算法的默认排序原则:都是由小到大;如果想从大到小,就需要自己建立一个回调函数
1111 
1112 7.对组( pair ):    将一对值组合成一个值,这一对值可以具有不同的数据类型。两个值可以分别用对组的公有属性 first 和 second 访问。
1113     创建对组的两种方式:1)pair<string ,int> pair1("xiaobai",20);    cout<< pair1.first <<endl;    cout<< pair1.second <<endl;
1114     2)pair<string, int> pair2 = make_pair("xiaohong",23); cout<< pair2.first <<endl;  cout<< pair2.second <<endl;
1115     3)pair<string, int> pair3 = pair2;    cout<< pair3.first <<endl;  cout<< pair3.second <<endl;
1116 
1117 8.map 容器:(它的所有元素都是一个对组)
1118     1)键值对;键值不可以相同,实值可以相同。所有的元素都会根据键值自动排序。
1119     2)插入元素的四种方式:map<int , int> m;
1120     m.insert(pair<int, int>(1, 2));
1121     m.insert(make_pair(2, 2));
1122     m.insert(map<int, int>::value_type(3, 2));
1123     m[4] = 4;
1124 
1125     3)如果通过[] 访问一个不存在的key值,那么编译器会创建一个,实值为默认值。
1126     4)map的迭代器与普通容器不同。是一个对组迭代器。可以通过迭代器修改实值的值,不可以修改键值的值。
1127     5)map的迭代器与list迭代器有某些相同的性质,对容器元素进行插入和删除时,操作之前的所有迭代器在操作完成之后不会失效,当然那个被删除的元素迭代器除外。
1128     6)指定map 的排序规则:(因为它的元素为对组,所以排序规则需要自己写函数确定)
1129 
1130 9.multimap容器:
1131     与map 容器操作类似,唯一不同在于它的键值可以相同。
1132 
1133 10.如何判断容器支持随机访问(或提供随机迭代器):
1134     只需要看容器提供的迭代器能否 +2,+3 
1135     vector<int>::iterator it = v.begin();    it = it+3;(可以,提供随机迭代器)
1136     queue<int>::iterator it = q.begin();  it = it+2;(不可以向后跳跃,不提供随机迭代器)。
1137 
1138 11.STL中,所有的拷贝都是值寓意,所提供的内容必须是可以拷贝的。
1139     1)此时就涉及到深拷贝和浅拷贝的问题;当由指针存在,而且它指向堆内存时,用容器提供的拷贝只会复制指针的指向,并没有拷贝到指针所指向的数据。
1140         当生命周期结束,进行空间析构时,就会出现同一块内存二次析构,程序挂掉。
1141     2)必须自己重载一个深拷贝函数(和类的函数重载一样;(一般都是:一个拷贝函数和一个重载=号操作函数)。
1142 
1143 12.STL使用 的时机
1144 
1145 C++第十二天(STL课程阶段)================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1146 
1147 1.容器中的 find()函数默认区分大小写,而且返回值为迭代器.
1148 
1149 2.仿函数:重载函数调用操作符的类,其对象成为函数对象,也叫仿函数。
1150     1)仿函数是一个类,不是一个函数。重载了()
1151     2)仿函数重载了()操作符,使得函数对象可以像函数一样使用。
1152     3)重载的operator()需要一个参数的,这样的类对象称为 "一元仿函数"; 依次类推。
1153 
1154 3.预定义函数对象:(实现了数据类型与算法的分离。)
1155     1)plus<int> p; 预定义好的函数对象,能实现不同类型数据的 + 运算。
1156     21157 
1158 3.函数没有类型,不能定义变量。函数对象有类型,因为他是一个类。
1159 
1160 4.函数对象与普通函数的区别:(021161     1)函数对象超出了普通函数的概念,可以保存函数的调用状态
1162     2)函数对象可以做参数和返回值
1163     
1164 5.谓词:
1165     1)普通函数或重载的operator()"返回值为bool 类型"的函数对象(仿函数)。
1166     2)一个参数的叫一元谓词,以此类推。
1167 6.兰博打函数表达式:    vector<int> v1;
1168     for_each(v1.begin(), v1.end(), [](int val){ cout<< val<<" "; });
1169     for_each(v1.begin(), v1.end(), [](int val)->void{ cout<< val<<" "; });
1170 
1171 7.内建函数对象
1172 /*
1173 template<class T> T plus<T>//加法仿函数
1174 template<class T> T minus<T>//减法仿函数
1175 template<class T> T multiplies<T>//乘法仿函数
1176 template<class T> T divides<T>//除法仿函数
1177 template<class T> T modulus<T>//取模仿函数
1178 template<class T> T negate<T>//取反仿函数
1179 */
1180 7.函数对象适配器:
1181     1). 让自己编写的函数对象继承基类,如果是一元函数对象需要继承unary_function,
1182     如果是二元函数对象继承binary_function
1183     2). 函数对象operator()函数后增加 const
1184     3). 使用bind2nd  bind1st(将一个二元函数对象转变为一元函数对象),
1185         区别为: bind1st: 将参数绑定为函数对象的第一个参数    bind2nd: 将参数绑定为函数的第二个参数。
1186     
1187     4)for_each():遍历打印容器内容。返回值为最后一个函数对象的类型。
1188     
1189     6)取反适配器
1190     find_if():查找容器中有无所寻找的内容,返回值为查找类型的迭代器。
1191     对一元函数对象、谓词取反用 not1; 对二元函数对象、谓词取反用 not2;
1192 
1193     7)给普通函数绑定参数(需要:先把一个普通的函数指针适配成函数对象)
1194         ptr_fun函数指针适配器:把一个普通函数指针适配为函数对象。
1195         void Myprint(int val1, int val2){  cout<< val1 + val2 <<" ";  }
1196         void main() { for_each(v1.begin(), v1.end(), bind2nd(ptr_fun(Myprint), 200));  }
1197     8)成员函数适配器(mem_fun_ref:容器中存放的为对象实体。)(mem_fun:容器中存放的为对象指针。)
1198         vector<person> v1;   person p1("aaa", 10);    v1.push_back(p1);
1199         for_each(v1.begin(), v1.end(), mem_fun_ref(&person::showperosn));
1200         vector<person*> v1;  v1.push_back(&p1);
1201         for_each(v1.begin(), v1.end(), mem_fun(&person::showperosn));
1202 
1203 
1204 8.算法:
1205     遍历算法:
1206         1)for_each算法:第三个参数,接受的函数类型。
1207         2)transform;
1208 
1209     查找算法
1210         1)find(查找元素或对象) 与 find_if(查找指针所指向的内容)
1211         2)adjacent_find查找相邻重复元素
1212         3)binary_search(二分查找法),需要查找的容器元素有序排列
1213         4)count 与 count_if (统计容器中元素的个数)
1214 
1215     排序算法
1216         1)合并两个有序序列(merge)两个序列必须是有序的。且顺序应该一样(或从小大,或从大到小,两种方式参数有点区别,默认从小到大,从大到小需要额外添加函数对象)
1217         2) sort算法(条件:容器必须支持随机访问)
1218         3)random_shuffle(打乱容器中的顺序)    
1219         4)reverse (反转)
1220         51221     拷贝和替换算法
1222         1)copy (拷贝)
1223         2)replace(替换)把所有要替换的值都替换成目标值。
1224         3)replace_if
1225         4) swap
1226 
1227     算数生成算法
1228         1)accumulate (累加容器中元素)
1229         2)fill(填充)
1230         31231     集合算法
1232         1)set_intersection (求交集)    注意它的返回值。
1233         2)set_union (求并集)  
1234         3)set_difference (差集);
1235 
1236 
1237 C++第十三天(数据结构、算法课程阶段)================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1238 1.线性表:分类:
1239 
1240     1)线性表顺序存储:
1241 
1242     2)线性表线性存储:
1243         1)单向链表
1244         2)单向链表(企业链表)
1245         3)循环链表(企业链表单向)
1246         4)解决约瑟夫问题(用单项循环链表)
1247         5)双向链表
1248         6)受限的线性表
1249             1)栈的顺序存储( stack )
1250             2)栈的链式存储( stack )
1251             3)队列的顺序存储( queue )
1252             4)队列的链式存储( queue )
1253             5)栈的应用(1.就近匹配,用来测试编码的括号使用规范)
1254             6)栈的应用(2.中缀表达式转后缀表达式)
1255             7)(计算机)基于后缀表达式的计算;
1256             
1257     
1258 3.树的特点:
1259     1)非线性结构,有 1 个直接前驱,但可能有多个直接后继(1 :n);
1260     2)树的定义具有递归性,树中还有树。
1261     3)树可以为空,即节点为0。
1262 
1263 4.树的一些专业名称
1264     1)节点的度:节点挂接的子树数(即一个节点有几个直接后继就有几度)。
1265     2)节点的层次:即根到该节点的层数(根节点算一层)
1266     3)树的度:所有 节点的度 中的最大值
1267     4)树的深度(或高度):指所有节点中的最大层数。
1268     5)节点数:一棵树中的所有节点个数(包括根节点)。
1269     6)叶子节点:即终端节点,没有后继。
1270 
1271 5 二叉树
1272     1)二叉树的基本特征:(121273         每个节点最多只有两个子树
1274         左子树和右子树的位置不能颠倒
1275     2)二叉树的性质:
1276         1)在二叉树的第 i 层上,最多只有2^(i-1)个节点。(i > 01277         2)深度为 k 的二叉树,最多只有 2^k -1 个节点。(k>01278         3)对于任何一个二叉树,若度为2 的节点有n2个,则它的叶子数必为 n2+1个。
1279         4)满二叉树:深度为K,有 2^K-1 个节点。特点是:每层都充满了节点。
1280 6.完全二叉树:
1281     1)除最后一层外,每层的节点数都达到了最大值,且最后一层的节点尽力靠左。
1282     2)对于完全二叉树:若从上至下,从左至右编号,则编号为i的节点,其左孩子编号为 2i, 右孩子编号为 2i+1, 双亲的编号为 i/2;(i等于1时,为根除外)。
1283     1)左孩子右兄弟:可以将一颗多叉树转变为二叉树
1284     2)递归遍历(周游)二叉树
1285     3)非递归遍历二叉树(用栈的方法)
1286     4)遍历的三种方法:
1287         1)先序:先根再左再右
1288         2)中序:先左再根再右
1289         3)后序:先左再右再根
1290     51291         1)已知二叉树的"先序"遍历和"后序"遍历序列"不能"唯一地确定这这棵树。
1292         2)已知二叉树的"先序"遍历和"中序"遍历"可以"唯一地确定这这棵树。
1293             1)先根据先序找到根节点,在根据中序确定根节点左右两边的节点
1294             2)再根据先序找到下一个根节点,再根据中序找到根节点左右两边的节点。
1295         3"中序""后序"可以确定一棵树
1296         4)总体结论为:带"中序"的都可以确定一棵树,反之则确定不了
1297 
1298     6)三种遍历的特点:
1299         1)先序遍历:可以确定每棵树的根节点。(特点: 它的输出序列中:第一个数为树的总根节点;剩下的子根节点,和中序遍历可以确定)
1300         2)中序遍历:可以确定根节点的左右子树。(它的特点为:输出序列中,根节点两边的为它的左右子树,和先序遍历配合可以确定一棵树)
1301         3)后续遍历:(它的特点;输出序列中,最后一个数为为树的总根节点。)
1302 
1303 C++第十四天(数据结构、算法课程阶段)================================================++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1304 
1305 1. 求二叉树叶子节点的数目(用全局变量写一遍)
1306     可以用静态变量和全局变量,还有形参参数。
1307 2. 求二叉树的高度(深度)
1308 
1309 3.拷贝二叉树:
1310 
1311 4.释放二叉树的节点。
1312 
1313 5.井号法创建树。
1314 
1315 6.排序算法
1316     1)新型冒泡排序(加标识量)
1317     2)新型选择排序(加标识量)
1318     3)插入排序:
1319         1.构建有序和无序序列,2.无序序列插入到有序序列中。
1320         2.应用场景:序列基本有序,元素较少。
1321         3.两步操作:元素拿出来,符合条件的元素后移。
1322     4)希尔排序:
1323         要点:增量:(len / 3 + 1);进行分组
1324         
1325         缺点: 排序不稳定,相同大小的几个元素,排列完后前后位置可能会发生变化。
1326         2345565; 这几个5排序完后,可能与现在的前后顺序不同。  
1327 
1328     5)快排:
1329             要点:基准数(选取)
1330             一轮遍历下来,基准数的插入位置左边部分都比他小,右边部分都比它大。
1331 
1332     6)归并排序:(将两个有序序列合并成一个有序序列)
1333         要点:1)先分组,将数据最后分成一个元素为一组的有序数列
1334              2)再两两合并排序; 
1335              3)需要一个与本来元素相同大小的辅助空间。
1336 
1337     7)堆排序:
1338         大顶堆:所有的根节点元素都比子节点两个元素大(完全二叉树)。
1339         小顶堆:………都比子节点元素小。
1340         1)先找出最后一个子根节点(len/2-1: len为数据的个数 。)
1341         2)从这个子根节点开始对堆进行数·初始化,形成(大顶堆或小顶堆)
1342         3)对初始化后的堆进行从第一个节点对最后一个节点交换(两两对应),每次交换完后,再对堆进行调整,(因为堆的结构可能发生变化)。
1343 
1344     8)等号= 对排序效率的影响。
1345 
1346 "C++实现链表:"
1347     1)老江的链表,宝哥的链表;有头节点,无头节点链表;
1348     2)与C做对比。
1349 
1350 考试:
1351 一. 类中三种变量的初始化:
1352     1.static 静态成员变量时:          (正确写法:在类外初始化:类型 类名::成员名)
1353     2.const 成员初始化;        (正规写法:在类内用初始化列表初始化)
1354     3.const static 成员初始化:    (正确写法:本地初始化;在类内定义的地方初始化)
1355     class person{
1356     public:
1357         person(int num = 0) : Age(10)      "初始化列表"
1358         { }    
1359         void show()
1360         {
1361             cout<<"Age:"<<Age<<" ID"<<ID<<" size"<<size<<endl;
1362         }
1363     private:
1364         static int ID;                    "在类外初始化"
1365         const int Age;                    "初始化列表"
1366         const static int size = 100;    "在本地初始化:即定义的地方"
1367     };
1368     int person::ID = 30;
1369 
1370 二.容器打印的三种方式
1371     1.回调函数
1372     2.回调对象
1373     3.兰博打表达式
1374     "================================="
1375     4.C++11 的表达式:
1376     for(int val : arr){            "其中arr是一个数组。V是一个vector容器"
1377         v.push_back(val);
1378     }
1379     5.STL 求和表达式的第三个参数:累加初始值,容器的所有元素的和加上这个值为最后的求出值。
1380 
1381     6.谓词;count_if() 的使用。
1382     7.绑定适配器:
1383 
1384 三.用栈(两个)实现队列
1385     1.可以在一个类里面包含两个栈,用来实现队列的方式。当用这个类定义对象时,他就是一个队列。
1386     2.有意思;但是没想出来,两种方法做一遍,存博客
1387 
1388 四.用归并的思想合并链表
1389     1.

    五.迭代器的理解:
    1.erase()函数的返回值,它的迭代器在循环遍历中的奇特之处;
    2.循环遍历,it++放置不同的位置;
      1)在正常的for循环位置
      1)符合条件时erase()容器中的某个元素,但是没有接返回值
      2)符合条件时erase()容器中的某个元素,接了返回值;
      3)符合条件时,接了返回值,同时在下面接着 it--;看容器首位元素符合条件和不符合条件的两种情况。
      2)不放在正常位置
      1)放在不符合条件的情况下it++;

原文地址:https://www.cnblogs.com/yyx1-1/p/5697524.html