一、前语

最近刚好有空,趁这段时间,温习一下C++言语,进一步夯实根底,为以后的底层开发音视频开发跨渠道开发算法等方向的进一步学习埋下伏笔

咱们在上一篇文章中,现已充分阐明,C++言语是对C的扩展,建立在对C言语常识把握的根底上学习C++是事半功倍的
假如你对C言语现已淡忘,或者没有学过C言语,且一时半会没有思路怎么挑选牢靠的C言语学习材料,能够学习我的这几篇文章:

1. C言语中心常识

  • 01-温习C言语中心常识|总述
  • 02-温习C言语中心常识|根本语法、数据类型、变量、常量、存储类、根本句子(判别句子、循环句子、go to句子)和运算
  • 03-温习C言语中心常识|函数、效果域规矩、数组、枚举、字符与字符串、指针
  • 04-温习C言语中心常识|结构体、共用体、位域、输入&输出、文件读写
  • 05-温习C言语中心常识|预处理、头文件、强制类型转化、过错处理、递归、内存办理

二、友元、内部类与局部类

1. 友元

  • 类的主要特色之一是 数据躲藏,即类的私有成员无法在类的外部(效果域之外)拜访
  • 可是,有时候 需要在类的外部拜访类的私有成员,怎么办?
  • 处理办法是运用友元函数,友元函数是一种特权函数,C++答应这个特权函数拜访私有成员
  • 这一点从现实生活中也能够很好的了解:
    • 比方你的家,有客厅,有你的卧室,那么你的客厅是Public的,一切来的客人都能够进去,可是你的卧室是私有的,也便是说只要你能进去
    • 可是呢,你也能够答应你的闺蜜好基友进去。
    • 程序员能够把一个 大局函数、某个类中的成员函数、甚至整个类 声明为友元

1.1 友元语法

  • friend关键字只出现在声明处
  • 其他类、类成员函数、大局函数都可声明为友元
  • 友元函数 不是 类的成员,不带this指针
  • 友元函数 可拜访目标 恣意成员特色,包括私有特色
  • 示例代码:
class Building;
//友元类
class MyFriend {
     public:
         //友元成员函数
         void LookAtBedRoom(Building& building);
         void PlayInBedRoom(Building& building);
};
class Building {
     //大局函数做友元函数
     friend void CleanBedRoom(Building& building);
     #if 0
         //成员函数做友元函数
         friend void MyFriend::LookAtBedRoom(Building& building);
         friend void MyFriend::PlayInBedRoom(Building& building);
     #else   
         //友元类
         friend class MyFriend;
     #endif
     public:
         Building();
     public:
         stringmSittingRoom;
     private:
         stringmBedroom;
};
 void MyFriend::LookAtBedRoom(Building& building){
     cout<< "我的朋友观赏" << building.mBedroom << endl;
 }
 void MyFriend::PlayInBedRoom(Building& building){
     cout<< "我的朋友玩耍在" << building.mBedroom << endl;
 }
 //友元大局函数
 void CleanBedRoom(Building& building){
     cout<< "友元大局函数拜访" << building.mBedroom << endl;
 }
 Building::Building(){
     this->mSittingRoom = "客厅";
     this->mBedroom = "卧室";
 }
int main(){
     Buildingbuilding;
     MyFriendmyfriend;
     CleanBedRoom(building);
     myfriend.LookAtBedRoom(building);
     myfriend.PlayInBedRoom(building);
     system("pause");
     return EXIT_SUCCESS;
}
  • 友元类留意
      1. 友元联系不能被继承。
      1. 友元联系是单向的,类A是类B的朋友,但类B纷歧定是类A的朋友。
      1. 友元联系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C纷歧定是类A的朋友
  • 考虑: C++是纯面向目标的吗?
    • 假如一个类被声明为friend,意味着它不是这个类的成员函数,却能够修改这个类的私有成员,并且有必要列在类的界说中,因此他是一个特权函数。
    • C++不是彻底的面向目标言语,而仅仅一个混合产品。
    • 增加friend关键字仅仅用来处理一些实际问题,这也阐明这种言语是不纯的。
    • 究竟C++规划的意图是为了实用性,而不是追求理想的笼统。

1.2 友元|总结

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 友元包括 友元函数友元类
  • 假如将函数A(非成员函数)声明为类C的友元函数,那么函数A就能直接拜访类C目标的一切成员
  • 假如将类A声明为类C的友元类,那么类A的一切成员函数都能直接拜访类C目标的一切成员
  • 友元破坏了面向目标的封装性,但在某些频频拜访成员变量的地方能够进步性能

2. 内部类与局部类

2.1 内部类

  • 假如将类A界说在类C的内部,那么类A便是一个内部类(嵌套类)
  • 内部类的特色
    • 支撑public、protected、private权限
    • 成员函数能够直接拜访其外部类目标的一切成员(反过来则不可)
    • 成员函数能够直接不带类名、目标名拜访其外部类的static成员
    • 不会影响外部类的内存布局
    • 能够在外部类内部声明,在外部类外面进行界说
  • 声明和完成别离
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

2.2 局部类

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 在一个函数内部界说的类,称为局部类
  • 局部类的特色
    • 效果域仅限于所在的函数内部
    • 其一切的成员有必要界说在类内部,不答应界说static成员变量
    • 成员函数不能直接拜访函数的局部变量(static变量除外)

3. 强化训练(数组类封装)

  • MyArray.h
    #ifndef MYARRAY_H
    #define MYARRAY_H
    class MyArray{
        public:
            //无参结构函数,用户没有指定容量,则初始化为100
             MyArray();
            //有参结构函数,用户指定容量初始化
            explicit MyArray(int capacity);
            //用户操作接口
            //依据方位增加元素
            void SetData(int pos, int val);
            //取得指定方位数据
            int GetData(int pos);
            //尾插法
            void PushBack(int val);
            //取得长度
            int GetLength();
            //析构函数,开释数组空间
            ~MyArray();
        private:
            int mCapacity; //数组总共可容纳多少个元素
            int mSize; //当时有多少个元素
            int* pAdress; //指向存储数据的空间
    };
    #endif
    
  • MyArray.cpp
     #include"MyArray.h"
     MyArray::MyArray(){
         this->mCapacity = 100;
         this->mSize = 0;
         //在堆拓荒空间
         this->pAdress = new int[this->mCapacity];
     }
     //有参结构函数,用户指定容量初始化
     MyArray::MyArray(int capacity){
         this->mCapacity = capacity;
         this->mSize = 0;
         //在堆拓荒空间
         this->pAdress = new int[capacity];
     }
     //依据方位增加元素
     void MyArray::SetData(int pos, int val){
         if (pos < 0 || pos > mCapacity - 1){
             return;
         }
         pAdress[pos] = val;
     }
     //取得指定方位数据
     int MyArray::GetData(int pos){
         return pAdress[pos];
     }
     //尾插法
     void MyArray::PushBack(int val){
         if (mSize >= mCapacity){
             return;
         }
         this->pAdress[mSize] = val;
         this->mSize++;
     }
     //取得长度
     int MyArray::GetLength(){
         return this->mSize;
     }
     //析构函数,开释数组空间
     MyArray::~MyArray(){
         if (this->pAdress != nullptr){
             delete[] this->pAdress;
         }
     }
    
  • TestMyArray.cpp
    #include"MyArray.h"
    void test(){
        //创立数组
        MyArraymyarray(50);
        //数组中刺进元素
        for (int i = 0; i < 50; i++){
            //尾插法
            myarray.PushBack(i);
            //myarray.SetData(i, i);
        }
        //打印数组中元素
        for (int i = 0; i < myarray.GetLength(); i++){
            cout<< myarray.GetData(i) << " ";
        }
        cout<< endl;
    }
    

三、运算符重载

1. 运算符重载根本概念

  • 运算符重载,便是 对已有的运算符重新进行界说,赋予其另一种功用,以适应不同的数据类型
  • 运算符重载(operator overloading)仅仅一种”语法上的便利”,也便是它仅仅另一种函数调用的方式
  • 在C++中,能够界说一个处理类的新运算符
  • 运算符重载(操作符重载):能够为运算符增加一些新的功用
  • 这种界说很像一个普通的函数界说,仅仅函数的姓名由关键字operator及其紧跟的运算符组成
  • 差别仅此而已。它像任何其他函数相同也是一个函数,当编译器遇到适当的方式时,就会调用这个函数
  • 语法:
    • 界说重载的运算符就像界说函数,仅仅该函数的姓名是operator@,这儿的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素
      • 运算符是一元(一个参数)的仍是二元(两个参数);
      • 运算符被界说为大局函数(关于一元是一个参数,关于二元是两个参数)仍是成员函数(关于一元没有参数,关于二元是一个参数-此时该类的目标用作左耳参数)
    • 两个极端
      • 有些人很简略滥用运算符重载。它确实是一个风趣的东西,可是应该留意,它仅仅是一种语法上的便利而已,是另外一种函数调用的方式
      • 从这个视点来看,只要在能使涉及类的代码更易写,尤其是更易读时(请记住,读代码的时机比咱们写代码多多了)才有理由重载运算符
      • 假如不是这样,就改用其他更易用,更易读的方式
      • 关于运算符重载,另外一个常见的反应是惊惧:
      • 突然之间,C运算符的意义变得不同寻常了,一切都变了,一切C代码的功用都要改动!
      • 并非如此,关于内置的数据类型的表中的一切运算符是不可能改动的

2. 运算符重载碰上友元函数

  • 友元函数是一个大局函数,和咱们上例写的大局函数类似,仅仅友元函数能够拜访某个类私有数据
  • 事例: 重载左移操作符(<<),使得cout能够输出目标
  • 代码示例:
     class Person{
         friend ostream& operator<<(ostream& os, Person& person);
         public:
             Person(int id,int age){
                 mID= id;
                 mAge= age;
         }
         private:
             int mID;
             int mAge;
     };
     ostream& operator<<(ostream& os, Person& person){
         os<< "ID:" << person.mID << " Age:" << person.mAge;
         return os;
     }
     int main(){
         Personperson(1001, 30);
         //cout << person;//cout.operator+(person)
         cout<< person << " | " << endl;
         return EXIT_SUCCESS;
     }
    

3. 可重载的运算符

  • 简直C中一切的运算符都能够重载,但运算符重载的运用是适当受约束的
  • 特别是不能运用C中当时没有意义的运算符(例如用求幂)
  • 不能改动运算符优先级
  • 不能改动运算符的参数个数。这样的约束有意义
  • 否则,一切这些行为产生的运算符只会混杂而不是弄清寓语意
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

4. 自增自减(++/–)运算符重载

  • 重载的++和–运算符有点让人手足无措,由于咱们总是希望能依据它们出现在所效果目标的前面仍是后边来调用不同的函数。
  • 处理办法很简略,例如当编译器看到++a(前置++),它就调用operator++(a),当编译器看到a++(后置++),它就会去调用operator++(a,int)
  • 代码示例:
    class Complex{
         friend ostream& operator<<(ostream& os,Complex& complex){
             os<< "A:" << complex.mA << " B:" << complex.mB << endl;
             return os;
         }
         public:
             Complex(){
                 mA= 0;
                 mB= 0;
             }
         //重载前置++
         Complex&operator++(){
             mA++;
             mB++;
             return *this;
         }
         //重载后置++
         Complexoperator++(int){ 
             Complextemp;
             temp.mA = this->mA;
             temp.mB = this->mB;
             mA++;
             mB++;
             return temp;
         }
         //前置--
         Complex& operator--(){
             mA--;
             mB--;
             return *this;
         }
         //后置--
         Complexoperator--(int){
             Complextemp;
             temp.mA = mA;
             temp.mB = mB;
             mA--;
             mB--;
             return temp;
         }
         void ShowComplex(){
             cout<< "A:" << mA << " B:" << mB << endl;
         }
         private:
             int mA;
             int mB;
     };
     void test(){
         Complexcomplex;
         complex++;
         cout<< complex;
         ++complex;
         cout<< complex;
         Complexret = complex++;
         cout<< ret;
         cout<< complex;
         cout<< "------" << endl;
         ret--;
         --ret;
         cout<< "ret:" << ret;
         complex--;
         --complex;
         cout<< "complex:" << complex;
     }
    
  • 优先运用++和--的标准方式,优先调用前置++
  • 假如界说了++C,也要界说C++,递加操作符比较费事,由于他们都有 前缀后缀 方式,而两种语义略有不同。重载operator++和operator–时应该仿照他们对应的内置操作符
  • 关于++和–而言,后置方式是先回来,然后目标++或者–,回来的是目标的原值。前置方式,目标先++或–,回来当时目标,回来的是新目标。其标准方式为:
  • 调用代码时候,要优先运用前缀方式,除非确实需要后缀方式回来的原值,前缀和后缀方式语义上是等价的,输入工作量也适当,仅仅效率经常会略高一些,由于前缀方式少创立了一个暂时目标

5. 指针运算符(*、->)重载

  • 代码示例:
class Person{
    public:
        Person(int param){
            this->mParam = param;
        }
        void PrintPerson(){
            cout<< "Param:" << mParam << endl;
        }
    private:
        int mParam;
};
class SmartPointer{
    public:
        SmartPointer(Person* person){
            this->pPerson = person;
        }
        //重载指针的->、操作符
        Person operator->(){
            return pPerson;
        }
        Person& operator*(){
            return pPerson;
        }
        ~SmartPointer(){
            if (pPerson != NULL){
                delete pPerson;
            }
        }
    public:
        Person pPerson;
 };
 void test01(){
     //Person* person = newPerson(100);
     //假如忘记开释,那么就会造成内存泄漏
     SmartPointerpointer(new Person(100));
     pointer->PrintPerson();
 }

6. 赋值(=)运算符重载

  • 赋值符常常初学者的混杂。这是毫无疑问的,由于’=’在编程中是最根本的运算符,能够进行赋值操作,也能引起复制结构函数的调用
  • 代码示例
class Person{
    friend ostream& operator<<(ostream& os,const Person& person){
        os<< "ID:" << person.mID << " Age:" << person.mAge << endl;
        return os;
    }
public:
    Person(int id,int age){
        this->mID = id;
        this->mAge = age;
    }
    //重载赋值运算符
    Person& operator=(const Person& person){
        this->mID = person.mID;
        this->mAge = person.mAge;
        return this;
    }
private:
    int mID;
    int mAge;
};
//1. =号混杂的地方
void test01(){
    Personperson1(10, 20);
    Personperson2 = person1; //调用复制结构
    //假如一个目标还没有被创立,则有必要初始化,也便是调用结构函数
    //上述比方由于person2还没有初始化,所以会调用结构函数
    //由于person2是从已有的person1来创立的,所以只要一个选择
    //便是调用复制结构函数
    person2= person1; //调用operator=函数
    //由于person2现已创立,不需要再调用结构函数,这时候调用的是重载的赋值运算符
}
//2. 赋值重载事例
void test02(){
    Personperson1(20, 20);
    Personperson2(30, 30);
    cout<< "person1:" << person1;
    cout<< "person2:" << person2;
    person2= person1;
    cout<< "person2:" << person2;
}
//常见过错,当预备给两个相同目标赋值时,应该首要查看一下这个目标是否对自身赋值了
//关于本例来讲,无论怎么履行这些赋值运算都是无害的,但假如对类的完成进行修改,那么将会出现差异;
//3. 类中指针
class Person2{
    friend ostream& operator<<(ostream& os, const Person2& person){
        os << "Name:" << person.pName << " ID:" << person.mID << " Age:" << person.mAge << endl;
        return os;
    }
public:
    Person2(char* name,int id, int age){
        this->pName = new char[strlen(name) + 1];
        strcpy(this->pName, name);
        this->mID = id;
        this->mAge = age;
    }
    #if 1
        //重载赋值运算符
        Person2& operator=(const Person2& person){
            //留意:由于当时目标现已创立完毕,那么就有可能pName指向堆内存
            //这个时候假如直接赋值,会导致内存没有及时开释
            if (this->pName != NULL){
                delete[] this->pName;
            }
            this->pName = new char[strlen(person.pName) + 1];
            strcpy(this->pName,person.pName);
            this->mID = person.mID;
            this->mAge = person.mAge;
            return this;
        }
    #endif
    //析构函数
    ~Person2(){
        if (this->pName != NULL){
            delete[] this->pName;
        }
    }
    private:
        char* pName;
        int mID;
        int mAge;
};
void test03(){
    Person2 person1("John",20,30);
    Person2 person2("Edward",30,30);
    cout << "person1" << person1;
    cout << "person2" << person2;
    person2 = person1;
    cout << "person2" << person2;
}
  • 假如没有重载赋值运算符,编译器会主动创立默许的赋值运算符重载函数。
  • 行为类似默许复制结构,进行简略值复制。

7. 等于和不等于(==、!=)运算符重载

  • 代码示例:
class Complex{
    public:
        Complex(*char name*,int id,int age){
            this->pName = new char[strlen(name) + 1];
                 strcpy(this->pName, name);
            this->mID = id;
            this->mAge = age;
        }
    //重载==号操作符
    bool operator==(const Complex& complex){
        if (strcmp(this->pName,complex.pName) == 0 && 
            this->mID == complex.mID && 
            this->mAge == complex.mAge){
            return true;
        }
        return false;
    }
    //重载!=操作符
    bool operator!=(const Complex& complex){
        if (strcmp(this->pName, complex.pName) != 0 || 
        this->mID != complex.mID || 
        this->mAge != complex.mAge){
            return true;
        }
        return false;
    }
    ~Complex(){
        if (this->pName != NULL){
            delete[] this->pName;
        }
    }
    private:
        char pName;
        int mID;
        int mAge;
};
void test(){
    Complexcomplex1("aaa", 10, 20);
    Complexcomplex2("bbb", 10, 20);
    if (complex1 == complex2){ cout << "相等!" << endl; }
    if (complex1 != complex2){ cout << "不相等!" << endl; }
}

8. 函数调用符号()重载

  • 代码示例:
    class Complex{
        public:
        int Add(int x,int y){
            return x + y;
        }
        int operator()(int x,int y){
            return x + y;
        }
    };
    void test01(){
        Complexcomplex;
        cout<< complex.Add(10,20) << endl;
        //目标作为函数来调用
        cout<< complex(10, 20) << endl;
    }
    

9. 不要重载&&、||

  • 不能重载operator&& 和 operator|| 的原因是:
    • 无法在这两种情况下完成内置操作符的完好语义
    • 说得更详细一些,内置版别版别特别之处在于:
      • 内置版别的&&和||首要核算左面的表达式,假如这彻底能够决议结果,就无需核算右边的表达式了–并且能够保证不需要
      • 咱们都现已习惯这种便利的特性了。
    • 咱们说 操作符重载其实是另一种方式的函数调用而已
    • 关于函数调用总是在函数履行之前对一切参数进行求值
  • 代码示例:
class Complex{
    public:
        Complex(int flag){
            this->flag = flag;
        }
        Complex& operator+=(Complex& complex){
            this->flag = this->flag + complex.flag;
            return *this;
        }
        bool operator&&(Complex& complex){
            return this->flag && complex.flag;
        }
    public:
        int flag;
};
int main(){
    Complexcomplex1(0);  //flag 0 
    Complexcomplex2(1);  //flag 1
    //本来情况,应该从左往右运算,左面为假,则退出运算,结果为假
    //这边却是,先运算(complex1+complex2),导致,complex1的flag变为complex1+complex2的值, complex1.a = 1
    // 1 && 1
    //complex1.operator&&(complex1.operator+=(complex2))
    if (complex1 && (complex1 += complex2)){   
        cout<< "真!" << endl;
    } else {
        cout<< "假!" << endl;
    }
    return EXIT_SUCCESS;
}
  • 依据内置&&的履行次序,咱们发现这个事例中履行次序并不是从左向右,而是先右猴左,这便是不满足咱们习惯的特性了。
  • 由于complex1 += complex2先履行,导致complex1 本身产生了变化,初始值是0,现在经过+=运算变成1,1 && 1输出了真

10. 调用父类的运算符重载函数

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

11. 符号重载总结

  • =, [], () 和 -> 操作符只能经过成员函数进行重载
  • << 和 >> 只能经过大局函数配合友元函数进行重载
  • 不要重载 && 和 || 操作符,由于无法完成短路规矩
  • 常规建议
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • 留意
    • 有些运算符不能够被重载,比方
      • 目标成员拜访运算符:.
      • 域运算符:::
      • 三目运算符:?:
      • sizeof
    • 有些运算符只能重载为成员函数,比方
      • 赋值运算符:=
      • 下标运算符:[ ]
      • 函数运算符:( )
      • 指针拜访成员:→

12. 强化训练_字符串类封装

  • MyString.h
    #define _CRT_SECURE_NO_WARNINGS
    #pragma once
    #include <iostream>
    using namespace std;
    class MyString {
        friend ostream&operator<< (*ostream*  & out, MyString& str);
        friend *istream*&operator>>(istream& in, MyString& str);
    public:
        MyString(const char *);
        MyString(const MyString&);
         ~MyString();
        char&operator[](int index); //[]重载
        //=号重载
        MyString& operator=(const char * str);
        MyString& operator=(const MyString& str); 
        //字符串拼接 重载+号
        MyString operator+(const char * str );
        MyString operator+(const MyString& str);
        //字符串比较
        booloperator== (const char * str);
        booloperator== (const MyString& str);
        private:
            char * pString; //指向堆区空间
            int m_Size; //字符串长度 不算'\0'
    };
    
  • MyString.cpp
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • TestMyString.cpp
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

13. 附录:运算符和结合性

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

四、仿函数(函数目标)

  • 仿函数:将一个目标当作一个函数相同来运用
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • 比照普通函数,它作为目标能够保存状况

五、智能指针(Smart Pointer)

1. 传统指针与智能指针

  • 传统指针存在的问题
    • 需要手动办理内存
    • 简略产生内存泄露(忘记开释、出现反常等)
    • 开释之后产生野指针
  • 智能指针便是为了处理传统指针存在的问题
    • auto_ptr:归于C++98标准,在C++11中现已不引荐运用(有缺陷,比方不能用于数组)
    • shared_ptr:归于C++11标准
    • unique_ptr:归于C++11标准

2. 智能指针的简略自完成

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

3. shared_ptr

  • shared_ptr的规划理念
    • 多个shared_ptr能够指向同一个目标,当最终一个shared_ptr在效果域范围内完毕时,目标才会被主动开释
  • 能够经过一个已存在的智能指针初始化一个新的智能指针
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • 针对数组的用法
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

4. shared_ptr的原理

  • 一个shared_ptr会对一个目标产生强引证(strong reference)
  • 每个目标都有个与之对应的强引证计数,记录着当时目标被多少个shared_ptr强引证着 能够经过shared_ptr的use_count函数取得强引证计数
  • 当有一个新的shared_ptr指向目标时,目标的强引证计数就会+1
  • 当有一个shared_ptr毁掉时(比方效果域完毕),目标的强引证计数就会-1
  • 当一个目标的强引证计数为0时(没有任何shared_ptr指向目标时),目标就会主动毁掉(析构)

5. 考虑下面代码有没有问题

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

6. shared_ptr的循环引证

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

7. weak_ptr

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • weak_ptr会对一个目标产生弱引证
  • weak_ptr能够指向目标处理shared_ptr的循环引证问题

8. weak_ptr处理循环引证

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

9. unique_ptr

  • unique_ptr也会对一个目标产生强引证,它能够保证同一时间只要1个指针指向目标
  • 当unique_ptr毁掉时(效果域完毕时),其指向的目标也就主动毁掉了
  • 能够运用std::move函数搬运unique_ptr的一切权
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

六、模板(template)

1. 泛型编程

  • 泛型,是一种将类型参数化以达到代码复用的技能,C++中运用模板来完成泛型
  • 模板的运用格局如下:
    • template <typename\class T>
    • typename和class是等价的
  • 模板没有被运用时,是不会被实例化出来的
  • 模板的声明和完成假如别离到.h和.cpp中,会导致链接过错
  • 一般将模板的声明和完成一致放到一个.hpp文件中

2. 编译链接

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

3. 函数模板

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

4. 多参数模板

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

5. 类模板

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

6. 类模板中的友元函数

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

七、类型转化

1. 转化符

  • C言语风格的类型转化符
    • (type)expression
    • type(expression)
  • C++中有4个类型转化符
    • static_cast
    • dynamic_cast
    • reinterpret_cast
    • const_cast
  • 运用格局:xx_cast<type>(expression)

2. const_cast

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 一般用于去除const特色,将const转化成非const

3. dynamic_cast

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 一般用于多态类型的转化,有运转时安全检测

4. static_cast

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 比照 dynamic_cast ,缺乏运转时安全检测
  • 不能穿插转化(不是同一继承体系的,无法转化)
  • 常用于根本数据类型的转化、非const转成const
  • 运用范围较广

5. reinterpret_cast

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 归于比较底层的强制转化,没有任何类型查看和格局转化,仅仅是简略的二进制数据复制
  • 能够穿插转化
  • 能够将指针和整数相互转化

八、C++标准的开展

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

1. C++11新特性

  • auto
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    • 能够从初始化表达式中推断出变量的类型,大大简化编程工作
    • 归于编译器特性,不影响最终的机器码质量,不影响运转效率
  • decltype
    • 能够获取变量的类型
      04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • nullptr
    • 能够处理NULL的二义性问题
      04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • Lambda表达式
    • Lambda表达式
      • 有点类似于JavaScript中的闭包、iOS中的Block,实质便是函数
      • 完好结构: [capture list] (params list) mutable exception-> return type { function body }
        • capture list:捕获外部变量列表
        • params list:形参列表,不能运用默许参数,不能省略参数名
        • mutable:用来说用是否能够修改捕获的变量
        • exception:反常设定
        • return type:回来值类型
        • function body:函数体
      • 有时能够省略部分结构
        • [capture list] (params list) -> return type {function body}
        • [capture list] (params list) {function body}
        • [capture list] {function body}
    • 示例
      04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    • 外部变量捕获
      04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    • mutable
      04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

2. C++14

  • 泛型Lambda表达式
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • 对捕获的变量进行初始化
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

3. C++17

  • 设置C++标准
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
  • 能够进行初始化的if、switch句子
    04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

九、过错&&反常

1. 过错

  • 编程过程中的常见过错类型
  • 语法过错
  • 逻辑过错
  • 反常
  • ……

2. 反常

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 反常是一种在程序运转过程中可能会产生的过错(比方内存不够)
  • 反常没有被处理,会导致程序停止
  • throw反常后,会在当时函数中查找匹配的catch,找不到就 停止当时函数代码,去上一层函数中查找。假如最终都找不 到匹配的catch,整个程序就会停止

3. 反常的抛出声明

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

  • 为了增强可读性和便利团队协作,假如函数内部可能会抛出反常,建议函数声明一下反常类型

4. 自界说反常类型

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

5. 阻拦一切类型的反常

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

6. 标准反常(std)

04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】
04-C++核心语法|面向对象2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转换、 C++标准、错误&&异常、智能指针】

专题系列文章

1. iOS底层原理前常识

  • 01-探求iOS底层原理|总述
  • 02-探求iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
  • 03-探求iOS底层原理|LLDB
  • 04-探求iOS底层原理|ARM64汇编

2. 根据OC言语探求iOS底层原理

  • 05-探求iOS底层原理|OC的实质
  • 06-探求iOS底层原理|OC目标的实质
  • 07-探求iOS底层原理|几种OC目标【实例目标、类目标、元类】、目标的isa指针、superclass、目标的办法调用、Class的底层实质
  • 08-探求iOS底层原理|Category底层结构、App启动时Class与Category装载过程、load 和 initialize 履行、关联目标
  • 09-探求iOS底层原理|KVO
  • 10-探求iOS底层原理|KVC
  • 11-探求iOS底层原理|探求Block的实质|【Block的数据类型(实质)与内存布局、变量捕获、Block的品种、内存办理、Block的修饰符、循环引证】
  • 12-探求iOS底层原理|Runtime1【isa详解、class的结构、办法缓存cache_t】
  • 13-探求iOS底层原理|Runtime2【音讯处理(发送、转发)&&动态办法解析、super的实质】
  • 14-探求iOS底层原理|Runtime3【Runtime的相关运用】
  • 15-探求iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
  • 16-探求iOS底层原理|RunLoop的运用
  • 17-探求iOS底层原理|多线程技能的底层原理【GCD源码剖析1:主行列、串行行列&&并行行列、大局并发行列】
  • 18-探求iOS底层原理|多线程技能【GCD源码剖析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
  • 19-探求iOS底层原理|多线程技能【GCD源码剖析2:栅门函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
  • 20-探求iOS底层原理|多线程技能【GCD源码剖析3:线程调度组dispatch_group、事情源dispatch Source】
  • 21-探求iOS底层原理|多线程技能【线程锁:自旋锁、互斥锁、递归锁】
  • 22-探求iOS底层原理|多线程技能【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
  • 23-探求iOS底层原理|内存办理【Mach-O文件、Tagged Pointer、目标的内存办理、copy、引证计数、weak指针、autorelease

3. 根据Swift言语探求iOS底层原理

关于函数枚举可选项结构体闭包特色办法swift多态原理StringArrayDictionary引证计数MetaData等Swift根本语法和相关的底层原理文章有如下几篇:

  • 01-Swift5常用中心语法|了解Swift【Swift简介、Swift的版别、Swift编译原理】
  • 02-Swift5常用中心语法|根底语法【Playground、常量与变量、常见数据类型、字面量、元组、流程操控、函数、枚举、可选项、guard句子、区间】
  • 03-Swift5常用中心语法|面向目标【闭包、结构体、类、枚举】
  • 04-Swift5常用中心语法|面向目标【特色、inout、类型特色、单例方式、办法、下标、继承、初始化】
  • 05-Swift5常用中心语法|高级语法【可选链、协议、过错处理、泛型、String与Array、高级运算符、扩展、拜访操控、内存办理、字面量、方式匹配】
  • 06-Swift5常用中心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、呼应式编程、Swift源码剖析】

4. C言语中心语法

  • 01-温习C言语中心常识|总述
  • 02-温习C言语中心常识|根本语法、数据类型、变量、常量、存储类、根本句子(判别句子、循环句子、go to句子)和运算
  • 03-温习C言语中心常识|函数、效果域规矩、数组、枚举、字符与字符串、指针
  • 04-温习C言语中心常识|结构体、共用体、位域、输入&输出、文件读写
  • 05-温习C言语中心常识|预处理、头文件、强制类型转化、过错处理、递归、内存办理

5. C++中心语法

  • 01-C++中心语法|C++概述【C++简介、C++来源、可移植性和标准、为什么C++会成功、从一个简略的程序开始知道C++】
  • 02-C++中心语法|C++对C的扩展【::效果域运算符、姓名操控、struct类型加强、C/C++中的const、引证(reference)、函数】
  • 03-C++中心语法|面向目标1【 C++编程标准、类和目标、面向目标程序规划事例、目标的结构和析构、C++面向目标模型初探】
  • 04-C++中心语法|面向目标2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转化、 C++标准、过错&&反常、智能指针】
  • 05-C++中心语法|面向目标3【 继承和派生、多态、静态成员、const成员、引证类型成员、VS的内存窗口】

6. Vue全家桶

  • 01-Vue全家桶中心常识|Vue根底【Vue概述、Vue根本运用、Vue模板语法、根底事例、Vue常用特性、归纳事例】
  • 02-Vue全家桶中心常识|Vue常用特性【表单操作、自界说指令、核算特色、侦听器、过滤器、生命周期、归纳事例】
  • 03-Vue全家桶中心常识|组件化开发【组件化开发思维、组件注册、Vue调试东西用法、组件间数据交互、组件插槽、根据组件的
  • 04-Vue全家桶中心常识|多线程与网络【前后端交互方式、promise用法、fetch、axios、归纳事例】
  • 05-Vue全家桶中心常识|Vue Router【根本运用、嵌套路由、动态路由匹配、命名路由、编程式导航、根据vue-router的事例】
  • 06-Vue全家桶中心常识|前端工程化【模块化相关标准、webpack、Vue 单文件组件、Vue 脚手架、Element-UI 的根本运用】
  • 07-Vue全家桶中心常识|Vuex【Vuex的根本运用、Vuex中的中心特性、vuex事例】

7. 音视频技能中心常识

  • 01-音视频技能中心常识|了解音频技能【移动通信技能的开展、声响的实质、深入了解音频】
  • 02-音视频技能中心常识|建立开发环境【FFmpeg与Qt、Windows开发环境建立、Mac开发环境建立、Qt开发根底】
  • 03-音视频技能中心常识|Qt开发根底【.pro文件的配置、Qt控件根底、信号与槽】
  • 04-音视频技能中心常识|音频录制【命令行、C++编程】
  • 05-音视频技能中心常识|音频播放【播放PCM、WAV、PCM转WAV、PCM转WAV、播放WAV】
  • 06-音视频技能中心常识|音频重采样【音频重采样简介、用命令行进行重采样、经过编程重采样】
  • 07-音视频技能中心常识|AAC编码【AAC编码器解码器、编译FFmpeg、AAC编码实战、AAC解码实战】
  • 08-音视频技能中心常识|成像技能【重识图片、详解YUV、视频录制、显示BMP图片、显示YUV图片】
  • 09-音视频技能中心常识|视频编码解码【了解H.264编码、H.264编码、H.264编码解码】
  • 10-音视频技能中心常识|RTMP服务器建立【流媒体、服务器环境】

其它底层原理专题

1. 底层原理相关专题

  • 01-核算机原理|核算机图形烘托原理这篇文章
  • 02-核算机原理|移动终端屏幕成像与卡顿

2. iOS相关专题

  • 01-iOS底层原理|iOS的各个烘托结构以及iOS图层烘托原理
  • 02-iOS底层原理|iOS动画烘托原理
  • 03-iOS底层原理|iOS OffScreen Rendering 离屏烘托原理
  • 04-iOS底层原理|因CPU、GPU资源耗费导致卡顿的原因和处理计划

3. webApp相关专题

  • 01-Web和类RN大前端的烘托原理

4. 跨渠道开发计划相关专题

  • 01-Flutter页面烘托原理

5. 阶段性总结:Native、WebApp、跨渠道开发三种计划性能比较

  • 01-Native、WebApp、跨渠道开发三种计划性能比较

6. Android、HarmonyOS页面烘托专题

  • 01-Android页面烘托原理
  • 02-HarmonyOS页面烘托原理 (待输出)

7. 小程序页面烘托专题

  • 01-小程序结构烘托原理