C++总结

1.封装,继承,多态。
2.作用域运算符(::)
    ::前面不写东西,表示全局变量
3.名字控制(命名空间:namespace)
    1)命名空间只能在全局范围内定义
    2)命名空间可以嵌套命名空间
    3)命名空间是开放的,可以随时向命名空间添加东西(变量,函数,类等)
    4)声明和实现分离
    5)匿名命名空间,默认此空间中的函数,变量等只能在当前文件中有效,相当于给每个变量,函数前加static。
        nameespace  TestA
        {
            int a;//这是在命名空间中未初始化的定义,当在命名空间进行定义时,而且此命名空间放在头文件,当在其他文件进行调用
                  //时,会产生重复定义的问题,编译不过
            int b;//这是在命名空间中初始化的定义
            extern int c;//这是命名空间中的声明
        }
4.条件编译
    #pragma once (第一种)
    #ifndef___;#define____;#endif;____代表一个定义的名字  (第二种)
5.bool类型:bool类型的变量可以把“任何非0值”转换成1;
6.三目运算符:C和C++的区别
    C语言中,三目运算符返回的是一个变量的值,没有内存空间,不可以修改(如下)
    int a =10;int b = 20; a>b?a:b; (a>b?a:b )=30 (不可以这样操作);
    C++语言中,三目运算符返回的时一个变量本省,具有内存空间,可以修改
    int a =10;int b = 20; a>b?a:b; (a>b?a:b )=30 (是可以这样操作的);
7.当一个变量有内存空间时(只要在堆栈区),就可以通过他的地址修改内存里的值
8.const的使用
    如:C语言中,const修饰的变量时一个只读变量,具有内存;
        C++语言中,const修饰的为常量不具有内存(有额外情况,下面有),它放在符号表中。
    1)C++中两种情况不一样
        const int a = 10;此时a不具有内存,(用一个常量初始化) 它放在符号表中,所以不可以对它进行修改。
        int b=9; const int a= b;此时a具有内存(在栈区),因为对它进行赋值的时一个变量b(栈区),可以通过指针间接赋值改变a的值
    2)在局部变量中,(C语言和C++语言情况不一样)
        C++语言中:const int a=9; int *p=(int*)&a;*p=99;
        此时当对const常量a取地址,编译器会为它分配地址(在栈区);但是a常量从符号表中取值,不可被修改;
        C语言中,变量a可以被修改
    3)在全局变量(C语言和C++语言这两种情况一样)
        extern const int a =9; int *p=(int *)&a; *p=99  (第一种)
        const int a = 9;(它放在全局变量);int *p = (int *)&a; *p=99; (第二种)
        此时对const变量a取地址或声明extern时,编译器会为它分配地址(在只读变量区);这段代码可以编译通过,但是运行阶段会报错,不以被运行
    4)    C语言中const修饰的变量为外部链接(和普通变量一样)
        C++语言中,const 修饰的变量为内部链接(和普通变量不一样)
        不管是C还是C++语言,普通变量都是外部链接。
    5) const与#define的不同,const有类型,有作用域,可进行编译器类型安全检查;
        #define 无类型,不重视作用域,(从定义开始到文件结尾),不可以进行安全检查;
9.extern的使用
    使用范围是不同文件之间变量的调用。(不可以在同一文件不同函数之间使用)
    在不同文件之间调用一个外部链接的变量时,只用在调用的文件中写一个extern关键字就可以了
    当调用一个内部链接时,需要在被调文件和调用文件中的写extern关键字。
10.变量可以定义数组 int n = 9; int arr[n];
    不同的编译器编译环境效果不同,在vs2013中不可以通过,在QT Linux中可以通过,支持C99的编译器可以通过。
11.using 编译指令与 using 声明的区别
    namespace A{int a=9;func(){}}    
    namespace B{int a=9;func(){}}
    using声明:int main(){
        using A::a; 
        cout<<a<<endl;
        int a = 6;
    }
    当第二个a被定义时,函数编译通不过,命名冲突
    using 编译指令:
    using 编译指令:
    int main(){
        using namespace A;; 
        cout<<a<<endl;
        int a = 6;
        cout<<a<<endl;
    }
    输出结果为:9,6;
    此时打印最近定义的那个。
    
    int main()
    {
        using namespace A;
        using namespace B;
        cou<<a<<endl;
    }
    此时会报错,编译器不知道选择那个a
    注意:使用using 声明或using编译指令会增加命名冲突的可能性,也就是说,如果有名称空间,
    在代码中使用作用域解析运算符,则不会出现二义性
12.在C语言中,重复定义多个同名的全局变量时合法的(不正规),但是C++是不合法的。
--------------------------------------------------------------------------------------------------------
1.变量都是有内存空间的,当它在堆栈区时可以通过指针修改它的值。
2.引用:本质地址传递;编译器帮忙做了基本地址传递部分
    int &b=a;=========》 int* const b=&a;(实质)b是常指针,指向不可以改变
    1)统一个内存块可以取多个别名,int&b=a,int &c=a;
    2)引用的基本语法:int a =10;int &b=a;(引用)b=100;cout<<a<<endl; cout<<b<<endl; 输出结果都为100.
    3)指针的引用:int *p = NULL; int *&p1 = p; p1=(int*)malloc(sizeof(int));
    4)数组的引用: (两种方式)
        1.typedef int ARR[10]//建立数组类型, 
            int a[10] //创建一个数组,
            ARR &p=a; //对数组的引用;
            p1[i]=i; //对数组的某个元素赋值
        2.int(&p)[10]=a;//直接建立数组类型,进行引用,并初始化,
            p1[i]=i;//对数组的某个元素赋值
    5) 引用没有定义,是一种关系型的声明,声明它和原有某一变量(实体)的关系,故而类型与原类型保持一致,且不分配内存,
    与被引用的变量有着相同的地址声明引用变量时必须初始化, int&b;//错误,必须确保引用是和一块合法的内存块关联
    6)引用一旦初始化,不能改变 (可以建立数组引用) 
        int &b=a;b=c; (这种写法并不是改变b的指向,是将c的值赋给b;b并没有指向c)
    7)(常量引用) const int &b = a; b的值,指向都不能被改变,
        因为它等同于 : const int *const b=&a;(这种写法常用哎保护’实参‘不受’形参‘的改变)
    8)C++编译器在编译过程中使用“常指针”作为引用的内部实现,因此引用所占用的空间大小与指针相同。
    9)建立普通变量的引用
        int ma = 9;
        int &ra = ma;//建立引用
        int *const ra = &ma; //即ra = &ma;
        ra = 88 ;//通过引用修改变量的值。这步是编译器帮忙进行解引用,然后赋值的。
    
        //建立对指针的引用
        int *p2 = NULL;
        int *&p2 = p2; //建立引用,
        int **const mp2 = &p2;
        mp2 = (int *) malloc(sizeof(int));//通过引用给指针p2分配空间,也就是给p2重新赋值,让他重新指向。
        
        //建立对数组的引用
        typedef int Arr[10];//建立数组类型
        Arr a; //建立一个普通的数组
        Arr &3 = a; //建立引用,
        Arr *const p3 = &a;
        p3[3]=10; //对数组的第四个元素赋值。
3.引用的几点基本常识:
    1)单独定义引用时,必须初始化;说明它很像一个常量;因为常量在定义的时候必须初始化 (const int a = 5)
    2)普通引用有自己的空间(在32位平台占4个字节),但是引用变量的地址和初始化它的变量时同一块地址,
        int &a=b; a和b的地址是相同的,
        struct teacher {int a;char b; int &d;double &c;};这个结构体所占内存为16
        struct teacher {int a;char b;}; 这个结构体所占用内存是8;
    3)引用的本质是一个常量指针;
4.函数中的引用:引用做函数的参数,引用做参数的返回值,
    1)引用做参数不需要初始化
    2)不能返回局部变量引用(和返回局部指针变量原因一样)
    3)引用做返回值(可以做左值和右值)
    4)指针的引用。
    5)(常量引用):const 对引用的使用(如上),const引用的值不能被改变,(主要用在函数的形参,不想用形参改变实参的值)
    6)函数的返回值当左值需要返回引用
5.类:
    1。使用class关键字,2)类里面可以放变量,函数。3)public:访问权限
6内联函数:
    C++使用(即有宏函数的效率,有没有普通函数的开销;可以向普通函数那样,进行参数,返回值类型的安全检查, 
        又可以作为成员函数,在C++中,定义内联函数,只是对编译器的一个建议,并不Id会成为内联函数)
    内联函数的语法:
    1)普通函数;inline void func(int x){return;} 但是必须注意函数体和声明结合在一起,否则编译器将作为普通函数来对峙
    2)要求:不能存在任何形式的循环语句,不能存在过多的条件判断语句,函数体不能过于庞大,不能对函数进行取址操作
7宏函数:
    1)默认参数:int func(int x=10,int y=20);此时形参的赋值就是默认参数,当函数调用不传参数时,就将使用默认参数
    2)注意:int func(int x,int y=0,int z=0);//函数的默认参数从左向右,如果一个参数设置默认参数,那么这个参数之后的参数必须
        设为默认参数,函数的声明和函数的定义不能同时写默认参数,(即使默认参数相同也不行) 编译器不知道该选择哪套
    3)占位参数:函数的占位参数也是参数,必须要给个值,只是函数内部用不了而已 int func(int,int,y);或int func(int,int y=3);
        当占位参数与默认参数结合时,int func(int =5,int y);erro;这种写法,int(int y,int =30);占位参数只能卸载最后一个形参的位置
        而且可以不给它传参,因为他有了默认参数
8函数重载:
    1)函数重载的条件:
        可以作为条件的:同一个作用域,函数名相同,形参的个数,形参的类型,形参的类型顺序不同,
        用cosnt 进行修饰的函数也可以进行重载,非cosnt对象有限调用非const
        const对象只能调用const函数,可以被const函数和非const函数调用,
    注意:不可作为条件的:函数的返回值不能作为函数重载的条件,函数重载和默认参数不能同时出现;函数重载碰到默认参数,
        name就要考虑是否会出现函数调用二义性(会报错,编译不通过)
    2)重载函数的调用:正常调用:函数调用正常匹配函数形参(可以找到)。隐式类型转换后调用:当找不到匹配的形参时,编译器会进行
        隐式转换,任然找不到后会进行报错(如下)
            void func(char b);
            int main()
            {
                int a=3;func(a);
            }
            此时就会进行隐式转换,因为与ASCII码匹配
    3)函数重载的原理
        编译器为了实现函数重载,在编译的时候做了一些优化,用不同的类型来修饰不同的函数名,
            如: void func(){}
                void func(int a){}
                void func(int a,char b){}
            上述三个函数编译后:
            生成的函数名为:_z4funcv v表示void 无参数
                            _z4funci i表示参数为int类型
                            _z4funcic i代表第一个参数是int类型的,第二个参数代表char类型的
----------------------------------------------------------------------------------------------
1.struct的区别(C和C++)
    C语言中只能定义变量。
    C++语言中可以定义变量和函数,同时C++语言中,struct中所有函数和函数都是public权限,
2.类的封装
3.类内部的三种权限
    public:公有属性(修饰的成员变量和方法;可以在类的内部和外部使用)
    private:私有属性,(修饰的成员变量和方法,只能在类的内部使用,不能在类的外部使用)
    protected:保护属性,主要用于继承,(修饰的成员变量和方法,可以在类的内部和继承的子类使用,不能在类的外部使用)
4.struct 和class的区别
    struct中成员默认权限为public;
    class中成员的默认权限为private;
5.类的调用(一个类调用另外一个类)
6.对象的构造和析构:
    1)构造函数: "名称和类名相同";没有返回值,可以有多个,进行函数重载,在内存中开辟之后调用构造函数
    2)无参构造和有参构造:
        1无参构造函数:定义对象的时候,对象后面不能加括号,如class stu{std(){“无参构造函数”}};stu st;(正确定义对象)
            如果加上括号:stu st();编译时不会报错(因为编译器把它看成函数声明),运行的时候会报错。
        2.有参构造函数:普通参数,根据形参的类型和个数,也可以进行函数重载,
        class Animal{
            public :
                Animal(int age)
                {
                    cout<<"一个参数构造函数数字!"<<endl;
                    mName="undefined";
                    mAge=age;
                }
                Animal(string name)
                {
                    cout<<"一个参数构造函数字母"<<endl;
                    mName=name;
                    mAge=0;
                }
                Animal(string name ,int age)
                {
                    cout<<"两个参数的构造函数"<<endl;
                    mName=name;
                    mAge=age;
                }
                //拷贝构造函数,用一个对象来初始化另一个对象,
                Animal(const Animal &animal)
                {
                    //拷贝构造函数,形参必须是引用,否则会造成死循环,一直调用它本身,本质上成了递归调用本身
                    cout<<"拷贝构造函数"<<endl;
                    mName= animal.mName;
                    mAge=animal.mAge;
                    
                }
                拷贝参数:用一个对象来初始化另外一个对象,它的形参必须是引用,如果是普通的变量,就会造成本质上的递归调用本身,无意义
        }
        3)析构函数:名字=类名+~;没有返回值,没有参数,一个类只能有一个析构函数,在内存释放前调用析构函数
7.构造函数调用的规则:(参考上面的代码)
    1)括号法:    Animal1("smith");//一个参数的构造函数(字母)
                Animal2(20);//一个参数构造的函数(数字)
                Animal3("john",20);//两个参数的构造函数
                Animal4(animal3);拷贝构造
    2)显示调用构造函数(匿名对象调用拷贝构造的问题)
        Animal("Smith",20);//匿名函数生命周期仅限于当行,慈航运行完之后立即析构。
        Animal animal5 = Animal(30);//一个参数构造的函数(数字); 形参为普通变量
        Animal animal5 = Animal("smith",30);//注意:两个参数的构造函数,拷贝构造,这行比上一行多了一个拷贝构造,
                                            //是因为string容器的缘故,只有这时才会有拷贝构造;
        Animal animal5(animal(30));一个参数构造的函数(数字)
        Animal animal5(animal(30,"smith"));两个参数构造函数,拷贝构造,当不调用string参数时,它不会调用拷贝构造,由编译器决定,具体方式不清楚
    以上两种情况,调不调用拷贝构造,主要看参数类型,有string:就调用拷贝构造,没有string就不调用拷贝构造
    匿名对象如果没有变量来接,他就是一个对象,此时这个变就相当于匿名对象的名称,
    匿名对象如果没有变量来接没他就是一个实例化的对象,
    3)等号法,
        Animal animal5=10;//不常用,调用一个函数,构造函数,
        Animal animal5=animal4;"拷贝构造,常用"
8.拷贝构造常用的两种调用方式:
    Animal animal5 = animal4;
    Animal animal5(animal4);
9.拷贝构造的调用时机:
    1)对象以传递的方式传递给函数参数,void func(Animal animal){};用实参初始化形参,调用额拷贝构造函数,
    2)用一个对象初始化另一个对象,Animal animal5 = animal4; 或者Animalanimal5(animal4);
    3)函数返回局部对象,(注意这里:debug模式和release模式不一样)。
        debug模式下:会调用拷贝构造,打印洗的地址相同,编译器这里会做优化,直接把原来的对象返回回去,
        Animal MyBussiness()
        {
            //局部对象
            Animal animal ;//无参构造函数”
            cout<<&animal<<endl;
            return animal;//编译器县拷贝一份,然后返回,相当于返回一个匿名对象
        }
        void test03()
        {
            Animal animal=MyBussiness();
            cout<<&animal<<endl;
        }
10.构造函数调用的规则
    1)    默认构造函数,(无参,函数体为空)
        默认析构函数(无参,函数体为空)
        默认拷贝构造函数,对类中非静态成员属性简单,“值”拷贝
    2)如果用户定义了“拷贝构造函数”,C++不会再提供“任何默认构造函数”
    3)如果用户定义了普通构造,C++ 不会再提供 默认无参构造,但是会提供默认拷贝构造
    4)在构造函数中调用构造函数是一个危险的行为,本质上:里面的构造函数相当于一个匿名对象,生命周期只有本行(没有变量接的情况下)
11.初始化列表:初始化成员列表只能在构造函数使用。
    构造函数的初始化列表顺序
    1)当一个类中组合了其他类的对象,先执行被组合的构造函数,
    2)如果组合对象有多个,来自不同的类,根据定义的顺序进行构造函数调用,而不是初始化列表顺序,
    3)被组合对象的构造顺序:与定义顺序有关系,与初始化列表的顺序,没有关系
    4)若类成员中有const修饰,必须在对象初始化的时候,给const元素初始化,此时就用到了“初始化列表”
        析构函数:与构造函数调用顺序相反,
12.深拷贝和浅拷贝
    class Person
    {
        public:
            Person(int a ,int b)
            _b(b),_a(a){}//初始化列表”
            深拷贝
            Person(const person &p1)
            {
                mstr=(char *)malloc(strlen(p1,mstr)+1);//核心步骤,会进行内存拷贝
                strcpy(mstr,p1,mstr);
                _a=p1._a;
                _b=p1._b;
            }
            浅拷贝
            Person(cosnt Person &p1)
            {
                mstr=p1.mstr.这只会把一个对象的指针地址,赋给另外一个对象的额指针,不会进行内存拷贝
                _a=p1._a;
                _b=p1._b;
            
            }    
            void print_func()
            {
                cout<<_a<<" "<<_b<<endl;
            }
            private:
                int _a;
                int _b;
                char *mstr;
    };
13.C++中string是一个类,
14.一直一个Peint类,在该类的main()函数里执行语句“Print a,b[2],*c”后,程序会自动调用该类的构造函数4次
    a调用一次,b调用两次,*c是空指针,没有开辟空间不会被调用。
    
----------------------------------------------------------------------------------------------------------
1.expicit:修饰构造韩式;进制编译器隐式转换,
    1.针对单参数的构造函数,
    2.或者除第一个参数外其他参数都有默认值的多参数构造函数,
2.C++中动态分配内存:
    new:在对上分配内存,分配对象的堆内存时会调用构造函数
    申请string类型的空间只能string类型数据,可以改为string *ps= new string("10");
    delete:释放堆空间,在释放堆呃逆存在前会调用析构函数
    delete void* 指针可能会报错,没有调用析构函数
3.创建动态数组:
    int *arr = new int[10];创建一个int 类型数组,元素10个,delete[] arr;释放内存。
4.穿件自定义对象数组,必须提供无参构造函数,同时数组有几个元素就电泳几次“无参构造函数和析构函数”
    delete void*指针可能会出错,没有调用析构函数
        class print
        {
            private:int a ;//在类外声明静态变量
            public:
        };
        int print::a=0;//在类外初始化静态变量
5.静态成员变量:
    1)在编译阶段就分配空间,对象还没有创建;
    2)必须在类中声明,在类外初始化(或定义)
    3)归同一个类的所有对象所有,共享同一个静态变量,在为对象分配的空间中不包含静态成员所占空间
    4)有权限限制:private静态变量和public静态变量
        public静态变量,在类外不能访问和操作,类外访问:会报错
        public:在类外可以访问和操作,
    5)cosnt静态成员变量(cosnt static int a=8);
            定义今天变量cosnt 静态成员变量时,最好在类内的内部初始化,
            静态变量的调用:用类名加域作用符:print::a;
            用对象名加去地址符:class p1;p1.a;
6静态成员函数(主要是尾了访问静态变量)
    与静态变量一样,在没有创建对象前即可通过类名调用。
    1)静态成员函数,只能访问“成员函数变量”不能访问普通成员变量
    2)静态成员函数的使用规则和静态成员变量一样
    3)静态成员函数也有使用权限,类外无法访问私有静态函数。
    4)普通成员函数可访问“成静态成员变量”,也可以访问“非静态成员变量”
7.单例模式:一个类只存在一个对象
    条件:
        1)构造函数。拷贝构造,析构函数私有化,
        2)提供西优化静态成员变量指向一个对象
        3)提供外部获得对象的静态成员函数
        class person
        {
            public:
                void print_func(string content)
                {
                    cout<<"打印内容"<<content<<endl;
                    mcount++;
                    cout<<"打印次数"<<mcount<<endl;
                }
            static person *getinstance()
            {
                //静态成员函数“在公共区域”,提供接口
                return p1;
            }
            private:
                static person *p1; //私有化静态成员变量
                int mcount;
        };
        int main()
        {
            person *p1 = person::getinstance();//p1和p2的地址一样
            person *p2 = person::getinstance();//因为他们为指向同一个对象,
            p1->print_func("晚上好!!!");
            p2->print_func("加油,胜利就在眼前");
            return 0;
        }
8.C++成员变量和函数的存储:(成员变量和函数分开存储)
    1)静态变量不在类对象中存储
    2)函数不在类对象中存储(静态函数与非静态函数都是)
    3)类对象中之存储,普通成员变量
9.this指针
    1)this指针永远指向当前对象
    2)“静态成员函数”,内部没有shis指针,所以静态成员函数不能操作非静态成员变量
        this指针的使用:
    1)当形参与成员变量同名时,可以通过this指针区分。
    2)在类的非静态成员函数中返回对象本身,可以使用 return*this;
    class person
    {
        public:
            person(int a,string name)
            {
                this->a=a;
                this-name=name;
            }
            person func()
            {
                return *this;
            }
        public:
            int a;
            string name;
    
    }
10.空对象指针访问成员的不同情况:由于(this指针的存在)
    1)当不访问成员变量的时候是允许的
    2)当访问成员变量时,函数通过this指针孕照成员变量,此时就会报错。
    class ARE
    {
    public:
        void print()
        {
            cout<<"hello world!!"<<endl;
            
        }
        void get_func()
        {
            cout<<ma<<endl;
        }
        public:
            int ma;
    }
    int main()
    {
        ARE *a1 = NULL;
        a1->print(); //可以访问,因为此时不访问尘缘变量
        a1->get_func; //不可访问,它对成员变量进行了访问
        return 0;
    }
11.cosnt 对成员函数的修饰,(int func() const{});注意:const要在小括号后面,大括号前面,(--->int func(const ARE *const this))
    1)cosnt 修饰this指针指向的内存区域,成员函数体内不可改变类中任何变量,除非变量前面用mutable进行修饰
        mutable 对成员变量的修饰
    2)用它修饰后,任何情况下都可以对变量进行修改。
12.const 修饰对象(常对象) 不允许修改对象属性
    1)常对象对象可以访问const或非const的变量,不能修改变量,除非变量前用mutable修饰
    2)常对象只能帝爱用coanst修饰常函数
13.友元函数。(全局函数和成员函数两种)
    1)friend 关键字只出现在声明处。
    2)其他类,类成员函数,全局函数都可以声明伪为友元。
    3)友元函数不是类的成员,不带this指针
    4)友元函数访问对象的任意成员属性,包括私有属性。
        注意:    1.友元不能被继承
                2.友元关系是单向的,类A是类B的友元。但类B不一定是类A的友元。
                3.友元关系不具有传递性,类B是类A的友元,类C是类B的友元,但类C不一定是类A的友元
--------------------------------------------------------------------------------------------------
1.友元函数,概念
2.数组类型
3.操作符重载:
    1)本质;函数的调用
        函数的参数取决于两个因素
        1.运算符是一元还是二元
        2.运算符被定义为全局函数还是成员函数
    2)运算符重载的限制:
        1.使用运算符重载不能改变运算符的优先级,不能改变运算符的参数个数。
            不能重载的运算符有 : .--::--. *--? :--sizeof.
        2.除“=”号外,基类中重载的运算符都将被派生类继承。
        3.特殊运算符:
            1.“=”,“[]”,"()","->"操作符只能通过函数重载
            2."<<";">>" 只能进行友元函数重载
            3.不要重载“&&”,“||” 操作符,因为无法实现短路原则,
        4.运算符重载建议:
            1.所有一元运算符,建议使用:成员函数,
            2.“=”。“[]”.“”。”->“;必须使用成员函数
            3.+=  -=  *= &=  != %= ^=,建议使用:成员函数。
            4.其他二元运算符 建议使用:非成员函数。
        5重载“=”号操作符的步骤,
            1.先释放旧内存。
            2.返回一个引用,实现链式编程,
------------------------------------------------------------------------------------------
运算符重载。
    1.前置++;后置--;
    2.后置++;前置--;
        要创建一个临时变量接受计算时的变量,记住要把临时变量返回去
    3.== 和 != 的重载
    4.继承,多态
        1)C++的继承方式(public(公有),private(私有),protected(保护))会影响子类的对方访问属性,判断某一句话,能否被访问
            1三个原则
            1-看调用语句,这句话写在子类的内部,还是外部
            2-看子类如何从父类继承(public,private,protected)
            3-看父类中的访问级别(public,private,protected);
        2)继承的使用情况:
            1-protected继承,只能在家族内部使用,不能在类的额外部使用
            2-项目开发中,通常都是public继承;
    5.继承的类型兼容原则:
        1)子类对象可以当做父类对象使用
        2)子类对象可以直接赋值给父类对象
        3)子类对象可以直接初始化父类对象
        4)父类指针可以直接指向之类对象
        5)父类引用可以直接引用子类对象
    6.继承与组合混搭情况下,构造函数和析构函数调用原则
        1)先调用父类构造函数,在调用组合类构造函数,最后调用自己的构造函数
        2)先调用自己的析构函数,在调用组合类析构函数,最后调用父类析构函数
        3)先构造的对象后析构
    7.不可被继承的函数
        1)基类中的构造函数和析构函数
        2)基类中重载的运算符 =号运函数 
    8.继承中同名成员函数和变量的问题;
        1)如果子类和父类中有相同的函数和变量名,子类调用时默认调用自身的函数和变量。
        2)如果想调用父类的函数和变量,需要加上“类名和域作用符”,显示调用
    9.继承中的静态成员特性:
        1)基类中定义的静态成员,可被所有派生类共享
        2)静态成员的访问权限和普通成员函数一样,遵守派生类的访问控制
        3)与函数重载一样,如果子类中重新定义了同名的静态函数,基类的函数将被隐藏
        4)静态成员函数不能是虚函数
    10.继承中的函数重载现象;
        1)如果重新定义了基类中的重载函数,只要函数名相同,继承的基类中的重载函数将被隐藏
            想要调用必须使用域作用符显示调用
    11.多继承的概念
        1)多继承可能会出现二义性,解决办法,显示的调用不同类中的同命名属性或方法,
        2)多继承的相同数据重复继承,解决办法:虚继承:virtual 即保持一份数据,也不会产生二义性
            菱形继承解决方法: 加上virtual,用虚继承
    12.多态:分为:静态多态(编译时多态)和动态多态(运行时多态)。
        面向对象编程的设计思路,开闭原则(对修改关闭,对扩展开放)
        C++的多态性是通过虚函数来实现。(虚函数允许子类重新定义父类成员,这种做法叫做重写或覆盖)
    13.向上类型转换
    14.虚函数:函数前加virtual修饰
        1)virtual 关键字只能修饰成员函数
        2)构造函数不能为虚函数
        3)创建一个虚成员函数,只需要在函数声明前加上virtual 关键字。
        4)如果一个函数在基类中被声明virtual,那么它在所有派生类中都是virtual;它派生类的函数前,(可加可不加virtual)
            关键字推荐加上;
            纯虚函数:virtual  int func()=0;
            1-当一个类中有纯虚函数时,这个类为抽象类,同时注意,不能实例化一个抽象类,编译时会报错
            2-当继承一个抽象类时,必须实现所有的纯虚函数,否则抽象类派生的类也是一个抽象类
    15.建立公共接口的目的:
        将子类中公共的操作抽象出来,可以通过一个公共结构操作一组类,这个公共接口不需要事先事先,及创建一个公共类
    16模板方法:就是通过创建一个公共接口来实现的
        一。多态的思想
            1)封装:突破C函数的概念,用类做函数参数的时候,可以调用对象的属性和对象的方法
            2)继承:A B 代码可以复用,可以使用前人的代码
            3)多态。可以使用未来的代码,架构不需要改变
        二。多态成立的条件
            1)要有继承
            2)要有虚函数重写
            3)用父类指针或者父类引用 指向子类对象
    17.虚析构函数的特点和目的:
        1)通过父类指针,把所有子类对象的析构函数都执行一遍
        2)通过父类指正,释放所有子类资源
    18.函数的重写、重载、重定义
        1).重载:必须在同一个类中,重载是在编译期间根据参数类型和个数决定函数调用的,子类无法重载父类函数,父类同名函数将被覆盖
        2)重写:必须发生在父类与子类之间(同时分为两类),并且父类与子类的函数有完全相同的原型。
            1-virtual具有这个关键字,发生重写时会产生多态。
            2-无这个关键字,叫重定义
            3-重定义(非虚函数重写叫重定义)。父类与子类的有完全相同的函数原型,而且无virtual关键字修饰,此时叫做函数重定义
        3)子类中的vptr指针的分布初始化
            1-当创建子类对象时,如果它具有vptr指针,那么此时是分布初始化的。
            2-当执行父类的构造函数时,子类对vptr指针会指向父类的虚函数表
                当父类的构造函数执行完毕后,会把子类的vptr指针指向子类的虚函数表
            3-结论:子类的vptr指针的分布初始化的,它在程序运行时会进行寻址操作,效率会降低,不建议每个成员函数都声明为虚函数
            4-纯虚析构函数:
            5-多态案列:
                1->思路,
                    定义一套自己的数据结构,打包放在结构体中,数据接口使用函数指针
                    初始化模块接口(将自己定义的接口与生产商实现的函数联系在一起)
    19.函数指针
---------------------------------------------------------------------------------------------------------------------------------
C++模板:用于实现泛型编程
    一、1)模板关键字 template<class T ,class T1>或template<typename T,typename T1> T,T1 代表数据类型
        2)定义的模板关键字当前行下一个函数或类有用
        3)分为函数模板和类模板
        4)一旦声明了多个类型T,不管用或者不用,都必须给它指定类型
    二、函数模板的编译机制:(原理)
        1)函数模板具有自动推到功能
        2)编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
        3)函数模板通过具体的类型产生不同的函数,
    三、模板函数和普通模板的区别
        1)函数模板不允许自动类型转换,必须严格匹配
        2)普通函数能自动进行类型转换
        template <class T>
        T print(T a, T b){}
        int show(int a,int b){}
        void main()
        {
            int a=6;char b='c';
            printf(a,b); //error,两个变量类型必须相同,不允许进行自动转换
            
            show(a,b);"yes", 普通函数能自动类型转换;

        }
    四.函数模板和普通函数一起调用规则
        1)函数模板可以向普通函数一样被重载
        2)如果函数模板与普通函数一样,C++编译器优先调用普通函数
            例如:
                template <class T>
                void print (T a,T b){}
                void print (int a,int b){}
                void main(){int a=4,int b=9; printf(a,b)};
                此时优先调用普通函数
        3)如果函数模板能产生更好的匹配,则有限调用函数模板。
            例如:template <class T,class T1>
            void print(T a,T b){}
            void print(int a , int b){}
            void main()
            {
                int a=4;
                char b='u';
                print(a,b);
                此时优先调用函数模板
            }
        4)可以通过空模板实参列表的语法限定编译器只能调用模板函数
            例如:第二个函数例子
            void main(){int a=4;int b=6; print<>(a,b)};
    
    五.类模板
        1)只能显示的指定类型,不具有自动推到功能
        2)类模板做函数参数(必须显示指定类型模板类型)
    六.类模板与函数模板(有自动推到功能)的混合使用案例,有奇特。
        template <class T1,class T2>
        class Person
        {
            public:
                Person(T1 name,T2 age)
                {
                    this->mName=name;
                    this->mAge=age
                }
            public:
                T1 mName;
                T2 mAge;
            
        }
        //类模板做函数参数 (指定类型)
        void Dobusiness(Person<string,int>&p1)
        {
            cout<<"name:"<<p1.mName<<"age:"<<p1.mAge<<endl;
        }
        //类模板与函数模板混用使用(使用函数模板的自动推倒功能)
        template<class T1,class T2>
        void Dobusiness01(Person<T1,T2>&p1)
        {
            cout <<"name:"<<p1.mName<<"age"<<p1.mAge<<endl;
        }
        void test04()
        {
            Person<string ,int >p1("john",30);
            Dobusiness(p1);
            Person<string,int>p2("johneqeq",303);
            Dobusiness01(p2);
        }
    七。类模板派生普通类和类模板类:
        1)继承的话,一个继承一个具体的类,因为一个具体的类,编译器才知道分配多大的内存
        2)如果想继承类模板,应该给继承过程中类模板显示指定类型。
        例如:(类模板派生普通类)
            template<class T>
            class MyClass{private:"父类模板" T age;};
            class SubClass:public MyClass<int>{"普通类",这里继承时,必须对类模板显示指定类型,编译器才知道父类分配多大的内存
            public:
                int a;
            }
            
        例如1:(类模板派生子类模板)
            template<class T>
            class MyClass{
                private:"父类模板" T age;
            };
            template <class T>
            class SubClass : public MyClass<T>{
                "子类模板,这里继承时,可以用子类的类型指定父类的模板"
                public:
                    T a;
            };
    八.类模板分文件编写
        C++恩建的编译过程:先每个文件独立编译,然后由连接器将编译好的文件链接在一起(这一步就是寻找各种调用函数,和头文件的定义内容)
        需要引入 .cpp, 原因:
        1)C++编译机制的原因;(分文件编译导致的)
        2)二次编译有关
            解决编发文件,将他们放入:
                类模板的声明与定义要放在一个文件中编写,创建一个.hpp将他们放入
            C++的类型转换
    九.静态类型转换:
        1)上行转换:把派生类的指针或者引用转换成基类的安全的 Animal *ani = static_cast<Animal*>(dog);
        2)下行转换:把基类的指针或引用转换成派生类的不安全的,因为没有动态类型检查,Dog *dog = static_cast<Dog *>(ani);
        3)总结一句话:就是把大转成小的,把小转成大的,
        4)当两个类之间无继承关系时,不可以进行转换。
            如:Teacher *t1= static_cast<Teacher *>(dog);//error,因为两个类之间无关系
        5)基础数据类型之间的转换:
            1-可以把高精度的数据类型转换成低精度的数据类型; 如:double da; int a = static_cast<int>(da);
            2-当把低精度的数据类型转换成搞精度时,安全性需要程序员保存,(原理和上一样),double da= static_cast<double>(a);
    十.动态类型转换:
        dynamic_cast<>(类与类之间的转换)
        dynamic_cast<>具有类型检查功能,比static_cast<>类型转换安全
        1)只能转换具体父子关系“指针或引用”
            上行转换:只能讲子类指针转换成父类指针(可以大转小)
            下行转换:不能讲父类指针转换成子类指针(不能小转大);
            例如:parent *parent = dynamic_cast<parent*>son;
    十一.数组模板:
        1)自定义类型做元素构建数组时,必须提供无参构造函数;
-------------------------------------------------------------------------------------------------------------------------------
    1.常量转换:(const_cast<>)用来修改类型的cosnt属性
        1)常量指针。引用 被转换成 非常量指针、引用,并且仍然指向原来的对象()互相转换,将非常量转换为常量
        2)不能直接对非指针、引用的变量直接使用const_cast<> 操作符去移除它的const属性
    2.重新解释转换或强制类型转换。reinterpret_cast<>(不安全的转换类型,各种类型之间的强转)
    3.异常处理(需要了解C的异常处理和C++的异常处理)C的异常处理的缺陷,和特点。
    4.C++的异常特点。
        1)异常不能被忽略,而且异常可以跨函数,一床并不是简单的int类型数字,最好明确的意义,
        2)C++提供的异常机制,具有跨函数和不可忽略的特点。
        3)异常捕获的严格类型匹配
    5.栈解旋:异常被抛出后,从进入try起,到异常处理完毕,这期间在栈上创建的所有对象都会讲被自动析构掉,析构的顺序和创建的顺序相反
    6.异常接口声明:
        1)可以在函数前声明抛出的异常类型,如果不声明,表示可以抛出各种类型的异常,
            例如:void func()throw(int ,double, char ){} //可以抛出三个类型的异常。
            void func()throw(){}不可以抛出任何类型的异常。
            void func(){};//可以抛出任何类型的异常
        2)但是:C++的异常规范在不同编译器下可能只需效果会不同(编译器的不同会导致结果不同)
            VS下回忽略C++的异常规范,但是会发生警告,程序照常只需;QT会报错,终止程序运行
            通常情况下:如果一个抛出了它的异常接口声明所不允许抛出的类型,unexcepted函数将被调用,该函数默认调用terminate函数终端程序
    7.异常变量的生命周期:
        1)异常也可以抛出一个类的对象,此时可以使用纯虚函数做接口,声明每种不同的异常对象(减少代码量)
        例如:
            class BaseException{
                public:    
                    virtual void print_execption()=0;
            }
            
            class TagetException:public BaseException{
                public:
                    virtual void print_execption()
                    {
                            cout<<"目标 空间空指针异常"<<endl;
                    }
            };
            class DestException:public BaseException
            {
                public:
                    virtual void print_Exception()
                    {
                        cout<<"源空间的空指针异常"<<endl;
                    }
            };
            
            void CopyString(char *dest,const char *source){
            
                if(source == NUll)
                {
                    throw DestException();
                }
                memcpy(TargetException,DestException,strlen(source)+1);
                
            }
            int mian()
            {
                const char *source = "nnakew";
                char dest[1024]={0};
                try{
                    CopyString(dest,source);
                }
                catch(BaseException& ex)
                {
                    ex.print_execption();
                }
                return 0;
            }
    8.C++标准异常类:系统头文件<exception>
    9.创建自己的异常类:
        引入系统头文件,继承系统的标准 出错类;重载父类的what函数和虚析构函数
    10.标准输入输出操作。
        cin由缓冲区获取文件到目标内存或变量,若缓冲区没有内存时,才需要从键盘上输入,
        cout 是有缓冲区输出文件到屏幕的,若缓冲区没有满时,或强制释放,它是不会在屏幕上输出内容的。
        endl 的作用是刷新缓冲区,将内容输出到屏幕或文件里。
    11.格式化输入输出操作。
--------------------------------------------------------------------------------------------------------------------
    1.STL
    2.STL三大组件:容器、算法、迭代器
        没一个容器都有自己的迭代器,迭代器用来遍历容器中的元素,
        遍历:不重复的进行访问每个元素。
    3.容器,迭代器,算法的初步使用。
        1.先引入所需要的容器头文件和算法头文件
            void Myprint(int val)
            {
                算法中的回调函数需要自己写,
                cout<<val<<" ";内容为自己所想打印的内容
            
                void test()
                {
                    vector<int>V;
                    for(int i=0;i<5;i++)
                    {
                        V.push_back(i+10);
                    }
                    vector<int>::iterator pBegin = V.begin();
                    vector<int>::iterator pEnd = V.end();
                    //直线使用迭代器进行容器的变量
                    while(pBegin != pEnd)
                    {
                        cout<<*pBegin<<" ";
                        pBegin++;
                    }
                    cout<<endl;
                    //模拟算法的最后一个回调函数的使用
                    pBegin = V.begin();
                    while(pBegin !=pEnd)
                    {
                        Myprint(*pBegin);
                        pBegin++;
                    }
                    cout<<endl;    
                            //使用算法遍历容器,并且打印出来,注意:算法的最后一个参数为回调函数,需要自己写(所需要打印的内容)
                    for_each(V.begin(),V.end(),Myprint);
                    cout<<endl;
                }
            };
        2.容器的分类:序列式容器和关联系容器
            序列式容器:容器元素在容器中的位置有元素的时间和地点来决定的
                如(vector 、 deque、 list、 stack 、queue)
            关联式容器:容器具有自己的规则,元素在容器中的位置,由容器自己来决定,
                如(树状容器、set/mutilset,map/mutilmap)
        3.每个容器都有自己的迭代器,有容器自己提供
    4.string容器:
        1.从const char*到string有隐式转换;反之则没有
        2.下标操作符:[] 和 at()方法。
            两个的区别在于:
                1)[]越界程序会直接挂掉
                2)at()方法越界程序会提供错误信息
    5.vector容器:
        1)连续的内存空间
        2)单口
        3)会实现内存空间动态增长(当已有内存占满时);申请更大的空间,原来的空间析构掉,原来的额迭代器会失效
        4)它的内存空间不会随着clear()方法清除数据而消失,只会当容器生命周期结束时,它的容量才会释放;
            所以当想在生命周期存在时减少或增大容器内存,可以使用swap()方法。(如下)
            void test04()
            {
                vector<int>V;
                for(int i=0;i<10000;i++)
                {
                    v.push_back(i);
                }
                cout<<"capacity:"<<v.capacity()<<endl;
                cout<<"size:"<<v.size()<<endl;
                //收缩内存
                //vector<int>.swap(v);
                vector<int>(v).swap(v);//匿名对象
                cout<<"capacity:"<<v.capacity()<<endl;
                cout<<"size:"<<v.size()<<endl;
            }
            
    6.reserve的使用:“预留空间”,在此空间还没有初始化时不允许访问,可以通过push_back()插入元素后,进行访问
    7.resize的使用:开辟空间并初始化,它的空间申请完后是可以访问的
        重新指定容器的长度num,若容器边长,则默认值或指定值填充新位置,如果容器变短,则末尾超出容器长度则被删除
            只是边长变短,但是容器的容量没有变化,
    8迭代器的注意事项:
        1)(当内存由于动态增长,跟换地址后,原来的迭代器就会失效,不可以在使用了)
        2)只要能遍历容器中所有元素的,都是容器认可的迭代器
        3)每个容器的迭代器不一样,有自己的独立的迭代器
        4)const_iterator 只读
            reverse_iterator 逆序迭代器
            iterator 最普通的正向迭代器
    9.迭代器的种类 5种
        1)每个容器值提供一种
        2)都是由对应的容器自己提供的迭代器,因为每个容器的实现机制不一样
        3)输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器
    10.vector 申请和释放空间的注意:
        尽量不要频繁申请和释放空间(太浪费时间);可以先预测一下所用空间的大小,直接用reserve()申请足够大的空间,在逐步初始化
    11.随机迭代器:(vector容器支持随机访问)
    12.deque容器:
        1)是一种双向开口,动态以分段连续内存空间组合而成,随时可以增加一块新内存容器,
        2)可以在首尾两端分别作元素和删除和插入操作。
---------------------------------------------------------------------------------------------------------------------------
    1.栈容器(stack):元素先进后厨,单口容器
        1)不提供迭代器:没有遍历功能
        2)以其他的容器作为底层,它相当于提供接口‘
        3)只有栈顶元素才可以被外界取用,所以向辨别能力容器,只能从栈顶开始,去除一个元素,就删除它,当遍历完之后,
            容器也就没有元素了
    2.队列容器(queue):元素先进先出
        1)没有迭代器,不支持随机访问,
        2)两个出口,一个只进,一个只出;
        3)和stack栈容器一样,遍历完后,容器的元素也被删除空。
        4.(stack 和queue 容器叫受限的线性表)
    3.链表容器(list):双向循环链表
        1)采用动态存储分配,不会造成内存浪费和溢出
        2)元素插入和删除十分方便,修改指针即可
        3)list容器提供自己的排序算法:
        4)list容器提供的自己的排序算法;
        5)swap()即可交换两个链表的数据,也可以动态的伸缩内存。
    4.set容器(红黑树,平衡二叉树的一种)
        1)它的元素即是键值对又是是实值,而且所有的元素都会自动排序。
        2)不允许两个元素有相同的键值’
        3)不允许通过迭代器修改元素的值
        4)与list有某些相同的性质,当对容器中的元素进行插入和删除造作时,操作之前的所有迭代器,在完成操作完成后有效的,
            除了被删除的那个元素的迭代器
    5.multiset容器:
        1)特性和set一样,唯一却别在于它允许键值重复
    6.算法的默认排序原则,都是由小到大,如果想从到小,就需要自己建立一个回调函数
    7.对组(pair):将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别对组的公有属性first和second访问
        创建对象的两种方式:
        1)pair<<string,int>pair<"xiaobai",20>;cout<<pair.first<<endl;cout<<pair.second<<endl;
    8.map 容器:(它的所有元素都是一个对组)
        1)键值对:键值不可以相同,实值可以相同,所有元素都会根据键值自动排序。
        2)插入元素的四种方式:
            map<int,int>m;
            m.insert(pair<int,int>(1,2));
            m.insert(make_pair(2.2));
            m.insert(map<int,int>::value_type(3,2));
            m[4]=4;
        3)如果通过[]访问一个不存在的key值,那么编译器创建一个,实值为默认值
        4)map的迭代器与普通容器不同模式一个队组迭代器,可以通过迭代器修改实值的值,不可以修改键值的值,
        5)map的迭代器与list迭代器有某些相同的性质,对容器元素进行插入和删除时,操作之前的所有迭代器
            在操作完成之后不会失效,当然那个被删除的元素迭代器除外
        6)指定map的排序规则:(因为它的元素为对组,所以排序规则需要自己写函数确定)
    9.multimap容器:
        与map容器操作类似,唯一不同在于他的键值可以相同,
    10.如何判断容器支持随机访问(或提供随机迭代器):
        只需要看容器提供的迭代器能否 +2,+3;
        vector<int>::iterator it=v.begin(); it = it+3;(可以提供随机迭代器)
        queue<int>::iterator it=q.begin(); it= it +2;(不可向后跳跃,不提供随机迭代器)
    11.STL中,所有的拷贝都是值寓意,所以提供的内容是可以拷贝的
        1.此时就设计到深拷贝和浅拷贝的问题,当由指针存在,而且它指向堆内存时,用容器提供拷贝只会复制指针的指向,
            并没有拷贝到指针指向的数据,当生命周期结束,进行空间析构时,就会出现统一块内存二次析构,程序会挂掉
        2.必须自己重载一个深拷贝函数(和类的函数重载一样);(一般都是:一个拷贝函数和一个虫子啊=号操作函数)
    12.STL 使用的时机
        
        
        
        
    
    
    
    
    
    
    
    
    
    
    
    
    
    
            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值