Kaze
Kaze
Published on 2022-04-09 / 81 Visits
0
0

MOOC C++ (二)

5. 继承

  • 写法:

    class 类名 : public 父类名
    {
    
    };
    
  • 在子类中访问同名父类成员:

    父类名::父类成员
    
  • 在子类构造函数中显式调用父类构造函数:

    //以初始化列表的方式调用父类构造函数
    derived::derived(arg_derived-list):base(arg base-list){}
    		子类构造函数					父类构造函数
    
  • 子类的析构函数被执行时,执行完子类的析构函数后,自动调用父类的析构函数。

  • 构造函数执行顺序:先执行父类的构造函数,再执行成员对象类的构造函数,最后执行子类的构造函数

  • 析构函数执行顺序:与构造函数相反

6. 多态

  • 在类的定义中,前面有virtua关键字的成员函数就是虚函数。只有虚函数才能使用多态机制

  • virtual关键字只用在类定义里的函数声明中,写函数体时不用。构造函数和静态成员函数不能是虚函数

  • 多态实现的原理:虚函数表

    每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着虚函数表的指针(放在对象的头部)。虚函数表中列出了该类的虚函数地址。多出来的4个字节就是用来放虚函数表的地址的。

  • 虚析构函数:

    • 通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数。但是,删除一个派生类的对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。
    • 解决办法:把基类的析构函数声明为virtua。派生类的析构函数可以virtua|不进行声明
    • 一般来说,一个类如果定义了虚函数,则应该将析构函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数。
  • 纯虚函数和抽象类:

    • 纯虚函数:没有函数体的虚函数

      virtual void Print()=0;

    • 包含纯虚函数的类叫抽象类。抽象类只能作为基类来派生新类使用,不能创建抽象类的对象

    • 抽象类的指针和引用可以指向由抽象类派生出来的类的对象

    • 在抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内部不能调用纯虚函数。

    • 如果一个类从抽象类派生而来,那么当且仅当它实现了基类中的所有纯虚函数,它才能成为非抽象类

7. 输入和输出模板

  • 输出重定向:freopen("test.txt","w",stdout);//将标准输出重定向到test.txt文件

  • 输入重定向:freopen(“t.txt",“r”,stdin);//cin被改为从t.txt中读取数据

  • 判断输入流结束:

    int x;
    while(cin>>x){}
    return 0;
    //如果是从文件输入,比如前面有freopen(“some.txt”,”r”,stdin);那么,读到文件尾部,输入流就算结束;如果从键盘输入,则在单独一行输入CtrI+Z代表输入流结束
    
  • istream类的成员函数

    • istream & getline(char* buf, int bufSize);//读取bufSize-1个字符到buf里,遇到'/n'提前结束

      istream & getline(char* buf, int bufSize, char delim);//读取bufSize-1个字符到buf里,遇到delim提前结束

    • bool eof();//判断输入流是否结束

    • int peek();//返回下一个字符,但不从流中去掉

    • istream & putback(char c);//将字符c放回输入流

    • istream & ignore(int nCount =1,int delim=EOF )从流中删掉最多nCount个字符,遇到EOF时结束。

  • 流操纵算子:

    • 使用流操纵算子需要#include

    • 整数流的基数:流操纵算子dec(10进制),oct(8进制),hex(16进制)

    • 指定输出浮点数的有效位数:cout<<setprecision(5)//可以连续输出

    • 指定输出浮点数的小数点后的有效位数:cout<<fixed<<setprecision(5)

    • 设置域宽:cin>>setw(4); cout<<setw(4);//宽度设置有效性是一次性的,在每次读入和输出之前都要设置宽度。

    • 用户自定义流操作算子:

      ostream & tab(ostream & output){
      	return output<<'/t';
      }
      
  • 文件读写:

    • #include//包含头文件

    • ofstream outFile("clients.dat",ios::out|ios::binary);//创建文件
      
      "clients.dat"要创建的文件的名字
      ios::out输出到文件,删除原有内容。
      ios::app输出到文件,保留原有内容,总是在尾部添加
      ios:binary以二进制文件格式打开文件
      //文件名可以给出绝对路径,也可以给相对路径。没有交代路径信息就是在当前文件夹下找文件
      
    • 文件的读写指针:标识文件操作的当前位置,该指针在哪里,读写操作就在哪里进行

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

      image-20220313150623687
    • 二进制文件读写

      image-20220313152839848
    • 二进制文件修改(既读又写)

      image-20220313154618073
  • 函数模板:

    • template <class T>
      void Swap(T & x,T & y){
      	T tmp=X;
      	x=y;
      	y=tmp;
      }
      
    • 函数模板中可以有不止一个类型参数。

    • 不通过参数实例化函数模板:Inc<double>(4)

    • 函数模板可以重载,只要它们的形参表或类型参数表不同即可。

    • 在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句
      1)先找参数完全匹配的普通函数(非由模板实例化而得的函数)。
      2)再找参数完全匹配的模板函数。
      3)再找实参数经过自动类型转换后能够匹配的普通函数。
      4)上面的都找不到,则报错。
      //匹配模板函数时,不进行类型自动转换
      
  • 类模板:

    • 写法和函数模板差不多

    • Pair<string,int> student("Tom",19);//实例化出一个类Pair<string,int>

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

    • 函数模版可以作为类模板的成员

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

      template <class T,int size>

8. 标准模板库STL(一)

  • string类:

    • string类是模板类

      typedef basic_string<char> string;

    • 使用string类要包含头文件<string>

    • 初始化:

      string s1("Hello");
      string month="March";
      string s2(8,'x');//8个‘x’组成的字符串
      
    • 不能用数字或者单字符初始化string类对象(无对应构造函数),但是可以用单字符赋值给string对象

    • string支持流读取运算符cin,支持getline函数

    • 用assign成员函数部分复制

      string s1("catpig"),s3;
      s3.assign(s1,1,3);//从s1中下标为1的字符开始复制3个字符
      
    • 单字符复制:

      • s2[5]=s1[3]='a';
      • s1.at(0)
      • 成员函数at会做范围检查,如果超出范围,会抛出out of range:异常,而下标运算符[]不做范围检查。
    • 连接字符串:

      • 用+运算符连接字符串
      • 用成员函数append连接字符串
    • 比较字符串:

      • 用关系运算符比较string的大小:==,>,>=,<,<=,!=。返回值都是bool类型

      • 用成员函数comparel比较string的大小,大于返回1,小于返回-1,相等返回0

        s1.compare(1,2,s3,0,3)
        //s1从下标1开始的2个字符与s3从下标0开始的3个字符进行比较
        
    • 子串:s2=s1.substr(4,5);//下标4开始5个字符

    • 交换string:成员函数swap

    • 寻找string中的字符:

      • 成员函数find(),rfind()。支持从指定下标开始查找
      • 成员函数find_first_of(),find_last_of()。在s1中查找"abcd”中任何一个字符第一次出现的地方,如果找到,返回找到字母的位置,如果找不到,返回string::npos。
      • 成员函数find_first_not_of()。在s1中从前向后查找不在“abcd”中的字母第一次出现的地方,如果找到,返回找到字母的位置
    • 删除string中的字符:

      • 成员函数erase(n)。去掉下标n及之后的字符
    • 替换string中的字符:

      • 成员函数replace()。

        s1.replace(2,3,"haha");
        //将s1中下标2开始的3个字符换成"haha"
        
    • 在string中插入字符:

      • 成员函数insert()。s1.insert(5,s2);//将s2插入s1下标5的位置
    • 转换成C语言式char*字符串:

      • 成员函数c_str()。s1.c_str()返回传统的const char*类型字符串,且该字符串以'/0'结尾。
    • 字符串流处理-字符串输入流istringstream

      • string input("Input test 123 4.7 A");
        istringstream inputString(input);
        string stringl,string2;
        int i;
        double d;
        char c;
        inputString>>stringl>>string2>>i>>d>>c;
        
    • 字符串流处理-字符串输出流istringstream

      • ostringstream outputString;
        int a=10;
        outputString<<"This "<<a<<"ok"<<endl;
        cout<<outputString.str();
        
  • STL概述:

    • 容器:可容纳各种数据类型的通用数据结构,是类模板

      • 1)顺序容器vector,deque,list
        2)关联容器set,multiset,map,multimap
        3)容器适配器stack,queue,priority_queue
        
    • 迭代器:可用于依次存取容器中的元素,类似于指针

    • 算法:用来操作容器中的元素的函数模板

    • 对象被插入容器中时,被插入的是对象的一个复制品

    • 顺序容器:

      • vector:头文件<vector>。动态数组。元素在内存连续存放。随机存取任何元素能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)。
      • deque:头文件<deque>。双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)
      • list:头文件<list>。双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
    • 关联容器:

      • 简介:

        • 元素是排序的

        • 插入任何元素,都按相应的排序规则来确定其位置

        • 在查找时具有非常好的性能

        • 通常以平衡二叉树方式实现,插入和检索的时间都是O(logN)

        • set/multiset 头文件<set>

          set即集合。set中不允许相同元素,multiset中允许存在相同的元素。
          
        • map/multimap 头文件<map>

          map与set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second,map根据first值对元素进行从小到大排序,并可快速地根据first来检索元素。map同multimap的不同在于是否允许相同first值的元素。
          
    • 容器适配器:

      • stack:头文件<stack>。栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。后进先出。
      • queue:头文件<queue>。队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出。
      • priority_queue:头文件<queue>。优先级队列。最高优先级元素总是第一个出列
    • 顺序容器和关联容器中都有的成员函数:

      • begin 返回指向容器中第一个元素的迭代器
      • end 返回指向容器中最后一个元素后面的位置的迭代器
      • rbegin 返回指向容器中最后一个元素的迭代器
      • rend 返回指向容器中第一个元素前面的位置的迭代器
      • erase 从容器中删除一个或几个元素
      • clear 从容器中删除所有元素
    • 顺序容器的常用成员函数:

      • front:返回容器中第一个元素的引用
      • back:返回容器中最后一个元素的引用
      • push_back:在容器末尾增加新元素
      • pop_back:删除容器末尾的元素
      • erase:删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面的那个元素的迭代器
      • insert:插入元素,第一个参数为要插入的地方
    • 迭代器:

      • 定义一个容器类的迭代器:容器类名::iterator 变量名; 常量迭代器const_iterator 反向迭代器reverse_iterator
      • 访问一个迭代器指向的元素:* 迭代器变量名
      • 迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,此时再使用它,就会出错,类似于使用NULL或未初始化的指针一样。
      • image-20220322204941250
    • 算法:

      • 算法就是一个个函数模板,大多数在<algorithm>中定义
      • 算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器。比如,排序和查找
      • find()函数返回值是一个迭代器。如果找到,则该迭代器指向被找到的元素。如果找不到,则该迭代器指向终止元素的后面一个元素。
      • 对数组应用find()函数时,数组名即为迭代器,指向数组头一个元素。
  • vector:

    • 创建二维数组:vector<vector<int> > v;
  • deque:

    • 所有适用于vectorl的操作都适用于deque。
    • deque还有push_front(将元素插入到前面)和pop_front(删除最前面的元素)操作,复杂度是O(1)
  • list:

    • image-20220322220822421
    • splice: 在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素
  • 函数对象:

    • 若一个类重载了运算符(),则该类的对象就成为函数对象
    • 函数对象可以当作参数传入成员方法中
    • greater是函数对象类模板,greater<int>是函数对象类,greater<int>()是个临时函数对象

9. 标准模板库STL(二)

  • set/multiset:

    • image-20220406204133357

      image-20220406204746753

    • multiset <A>a等价于multiset<A,less<A>>a

      插入元素时,multiset会将被插入元素和已有元素进行比较。由于less模板是用<进行比较的,所以,这要求A的对象能用<比较,即适当重载了<

    • 插入set中已有的元素时,会忽略插入

  • map/multimap:

    • image-20220406212630843

      image-20220406215747278
    • 若pairs为map模版类的对象,pairs [key]返回对关键字等于key的元素的值(second成员变量)的引用。若没有关键字为key的元素,则会往pairs里插入一个关键字为key的元素,其值用无参构造函数初始化,并返回其值的引用。

  • 容器适配器:

    • stack:

      • stack是后进先出的数据结构,只能插入,删除,访问栈顶的元素。

      • stack上可以进行以下操作:

        push插入元素
        pop弹出元素 
        top返回栈顶元素的引用
        
    • queue:

      • 同样也有push,pop,top函数。但是push发生在队尾;pop,top发生在队头。先进先出。有back成员函数可以返回队尾元素的引用
    • priority_queue:

      • priority_queue通常用堆排序技术实现,保证最大的元素总是在最前面。即执行pop操作时,删除的是最大的元素;执行top操作时,返回的是最大元素的引用。
    • stack,queue,priority_queue都有:

      empty()成员函数 用于判断适配器是否为空

      size()成员函数 返回适配器中元素个数

10. C++11新特性

  • 智能指针shared_ptr:

    • 需要包含头文件<memory>
    • 语法:shared_ptr<T> ptr(new T);
    • ptr可以像指针一样来使用,即*ptr就是用new动态分配的那个对象,而且不必操心释放内存的事。
    • 多个shared_ptr对象可以同时托管一个指针(通过赋值),系统会维护一个托管计数。当无shared_ptr托管该指针时,将delete该指针。
    • shared_ptr对象不能托管指向动态分配的数组的指针,否则程序运行会出错
    • 放弃托管:ptr.reset()
  • foreach循环

  • 无序容器(哈希表):

    • 需要头文件<unordered_map>
    • 用法和map一样,效率比map高,插入元素不排序
  • Lambda表达式:

    • image-20220409215207540

Comment