目录

CPP知识点笔记

记录cpp中大部分的知识点,个人理解有限,不一定都正确。

2.2 cout控制输出长度/格式:

1
2
3
4
5
6
7
cout<<setw(8)<<a<<endl;   //setw()在头文件<iomanip>中,只能右对齐,等价%8d
cout<<setiosflags(ios::left)<<setw(8)<<a<<endl;//这样为左对齐
cout<<setw(8)<<setprecision(2)<<setiosflags(ios::fixed)<<b<<endl;//b是一个浮点数,等价于%8.2f
cout<<setfill('x')<<set(8)<<a<<endl;//将空白用x填充
cout<<hex<<a<<endl;//等价于printf("%x\n",a);十六进制
cout<<oct<<a<<endl;//等价于printf("%o\n",a);八进制
cout<<a<<endl;//等价于printf("%d\n",a);十进制

2.3 函数重载(静多态)

匹配原则

  1. 严格匹配
  2. 隐式转换(小到大可以,大到小不可以)寻求匹配,找到则匹配
  3. 返回值不构成重载
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
extern "C"{} //将括号内按c的方式编译,不与其他同名函数重载。c库每一个头文件都有
//---------------------------------------------------------
#ifdef __cplusplus//如果是c++编译器
extern "c"{
#endif // __cplusplus

// 内容

#ifdef __cplusplus
}
#endif          //表示不使用c++的重载特性
//----------------------------------------------------------

2.4 运算符重载Operator Overload

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    //运算符其实就是函数
    struct zuobiao  //坐标
    {
        int x;int y;
    };
    zuobiao operator + (zuobiao a,zuobiao b)
    {
        zuobiao c;
        c.x=a.x+b.x;  c.y=a.y+b.y;
        return c;
    }

2.5 默认参数Default Parameters

参数只能从右往左默认,中间不能跳跃

2.6 引用Raference

&前为类型名时为引用,其他均为取地址

变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用是为已有变量起一个别名,声明如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int a;
int &ra=a;
//1. 引用是一种声明关系,必须初始化,引用不开辟空间
//2. 此种声明关系一经声明 不可变更
int &rr=ra;
//3. 可以对引用,再次引用。多次引用的结果,多个引用指向同一个变量
//引用的本质是对指针的包装,指针可以有引用,但不应该有引用的指针

//const int&ra=a; 能用const就用;use const whatever possible
//思考:引用的本质是什么?  int & ra = a;   int * const p = &a;  引用的本质是不可以修改指向的指针;

2.7 new/delete(申请动态内存)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int *p = new int(200);   //开一个int类型单变量并初始化为200
int *ps = new string("hellwo world");//开一个string类型单变量并初始化为 hellow world
int *p1 = new int[5]{0};//开一个int数组 每一项初始化为0;
int **pp = new int*[5];//指针数组,存放的指针
int (*p)[4] = new int[3][4] {{0}};//p是一个指针 它指向包含4个int元素的一维数组 p为首地址 记不住怎么写编译器可以给你提示
//也可以new一个结构体  但初始化应用 {} 因为可能有有多个值
/*应用程序在内存中分为
    代码区、
    全局(静态)数据存储区、
    栈区、
    堆区。
    通过new出来的内存在堆区。
    局部变量,函数参数等存在栈区
*/
int *p = new int ;
delete p;
int *p=new int[5];
delete []p;
int (*p)[4][3]=new int[5][4][3];
delete []p;
//异常处理  new很少出错 一般别用
int *p = new(std::nothrow) int ;
if(p == nullptr){return -1}//NULL 好像变成nullptr了c++11

2.8 内联函数

1
2
3
4
5
inline int sqr(int i)
{
    return i*i;
}//inline 是给编译器的一种建议,编译器来决定是否inline,但你不说编译器一定不inline    66666
//适用环境 函数体很小 且 频繁使用

2.9 cast强制类型转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int a=5;    float b=6.5;
static_cast //对于隐式类型可以转化的,可用这个
b=static_cast<float>(a);  //作者不想让你强转,所以搞这么麻烦 哈啊哈
void *p;int *q;
p=q;//可以
q=p;//不可以 q=static_cast<int*>(p);OK
dynamic_cast
reinterpret_cast //对于无隐式类型的转化,static_cast无效,例如将int赋值给int*指针  reinterpert重新解释
const_cast //脱常   只能用于指针和引用
void fun1(int & v)
{
    cout<<v<<endl;
}
int main()
{
    const int a;
    fun1(a);        //这是传不进去的
    fun1(const_cast<int &>(a));//这样就传进去了  不要妄图用这个方法修改const  其行为是未定义的且没有意义
    return 0;
}

2.10 const

1
2
3
#define N 200       //宏,在预处理阶段发生了替换
const int n = 200;  //在编译阶段发生了替换
//所以const永远不会发生改变

2.11 命名空间Namespace

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int v=55;
main()
{
    int v=5;
    cout<<v<<endl;//打印局部变量
    cout<<::v<<endl;//打印全局变量
}//全局命名空间其实是一个没有名字的命名空间,也可以用::作用域运算符访问。

namespace //是对全局命名空间的再次划分
namespace name//协同开发 可以在不同文件中定义同名空间,将自动合并
{
    int x;
    void func()
    {
        printf("haha");
    }
    struct xy
    {
        int x;int y;
    };
    //可以写很多,包括再写一个命名空间
}

2.12 字符串类型string

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//位于命名空间std;它是一个类不是关键字
//用cin输入时有空格会结束,但输出时字符串里可以有空格
string str="china"s2="0";
cout<<str.size()<<enld; //获得长度

char buf[100];
strcpy(buf,str.c_str());//strcpy位于string.h   str.c_str()对象函数返回一个c语言类型的字符串,以兼容c
cout<<buf<<endl;

str.swap(s2);//用对象方法交换

int n=str.find('a',0);//查找a,返回下标,第二个参数为从0开始,找不到返回-1
int n=str.find("in",0);//查找字串,有返回首下标,没有返回-(

//删空格
string ss="   sdfsdfs     ";
ss.erase(0,ss.find_first_not_of(' '));//从0删到第一个不是空格,参数一从什么地方删,参数二删几个
ss.erase(ss.find_last_not_of(' ')+1);//从最后一个不是空格删到最后

3.1 封装Encapsulation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//c语言的封装风格,数据放在一起打包为struck,然后吧数据以引用或指针的方式传递给行为。
struct date
{
    int year;
    int month;
    int day;
};
void init(date & d)
{
    cin>>d.year;
    cin>>d.month;
    cin>>d.day;
}
//c++认为c封装不彻底,1数据和行为分离,2没有权限控制。
//权限控制 private protected public
class date2
{
private:
    int year;
    int month;
    int day;
public:
    viod print()
    {
        cout<<year<<endl;
    }
};
//类名也是一种特别的名称空间

3.2 类的构造器constructor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//constructor 构造器
//1.与类名相同,无返回值,生成对象时自动调用,用于初始化
//2.可以有参数,可以重载,默认参数
class sss
{
public:
    sss()
    {
        cout<<"我是构造器"<<endl;
    }
    sss(int i)
    {
        cout<<i;
    }
};
sss s(100);//使用构造器的重载, 创建对象时加参数

3.2.2 类的析构器destructor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//1.~类名()无参无反,对象销毁时的内存处理工作
class sss
{
public:
    ~ksss()
    {
        cout<<"我是析构器"<<endl;
    }

};

4.1 自实现string

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//代码in坚果云
class A
{
public:
    A(int i=0;int j=0;int k=0)//参数列表,效率高,在函数声明之后,实现体之前,:开头
    :x(i),y(j),z(k)//初始化顺序,跟变量的声明顺序有关,初始化列表中的顺序无关,即xyz顺序可变
    {

    }
private:
    int x,y,z;

};
//拷贝构造器
    //由已存在的对象,创建新对象,也就是所新对象不是由构造器来构造,而是由
    //拷贝构造器来完成,拷贝构造器的格式是固定的
class A
{
    A(const A & another)//&是引用
};
    //若不提供,系统提供默认,自定义是系统不再提供默认
    //系统提供的默认是一种等位拷贝,浅浅的拷贝,会导致内存重析构。doble free
    //含有堆空间时要自实现拷贝

//友元 同类对象无私处,异类对象有友元

//运算符重载
    //赋值运算符重载
    string s4=s3;//本质不是赋值,而是拷贝构造,在声明时候=号表示初始化
//this指针
    //系统创建对象时,默认生成当前对象的this指针。
//对对象来说,传引用效率很高,不然调用拷贝构造效率低。
//栈上的对象可以返回,但不能返回栈上的引用(除非实例不会销毁,比如对象方法返回自己,结束后对象还在,引用有效)
    A & func(){//这是错的,func执行完b会销毁,但这个回传的引用仍指向这个地址
        A b;
        return b;
    }

5.1 const static 修饰类

static

  1. 在c中,修饰全部变量表示吧作用于限制在本文件内
  2. 修饰局部变量表示吧变量生命周期提升为全局
  3. c++中在类内部,用来实现族类对象间的数据共享。
  4. c++中,修饰类成员时,表示这个成员变量属于整个类,而不是某个对象(既属于类,也属于对象,但终归于类)
    1
    2
    
    static int a;//声明写在类内
    int A::a=0;//初始化写在内外
    
  5. 修饰成员函数时,也属于整个类;因为他属于类,没有this指针(this只有对象实例有)
    1
    2
    3
    4
    
    //不能访问数据成员及成员,没有this
    static string getstr();
    const static int h=0;
    //static const 修饰数据成员,必须在类内初始化;就地初始化
    

const

  1. 修饰数据成员,初始化位置只能在 参数列表里;(新版本虽然可以直接等于号初始化,但别被惯傻了)
  2. 修饰成员函数(注意不是修饰返回类型),位置放在声明之后,实现体之前,大概也没有别的地方可以放了(haha)
  3. const函数承诺不会修改数据成员,也只能调用其他const函数。
  4. 还可以构成重载(wtf?)
    • 修饰类对象,从对象层面不修改成员,只能调用const成员函数,构成重载就是配饰有无const的对象的情况
    • 即const对象只能调用const成员函数,非const对象优先调用非const成员函数(若无则调用const成员函数)

5.2 指向类成员的指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//都指向非静态成员
"成员类型 类名::* 指针名 = &类名::成员名"
class stuu
{
    void print();
private:
    int a;
};
    //指向数据成员
        //在定义时必须和类关联,使用时必须和对象关联
            int stuu::* pa=&stuu::a;
            stuu dx;
            cout<<dx.*pa<<endl;
            //(内部实现,实际上时记录了偏移量)
    //指向函数成员
        //
        void (stuu::*pf)()=&stuu::print;
        (dx.*pf)();

    tip://在类中想用常量时
        enum{cnt=4};//因为用const必须参数列表初始化,static const 太浪费

5.3 友元

  1. 友元可以是友元函数,友元类,异类才有友元(同类无私处)
  2. 友元是单方面的,你拿我当朋友不等于我也拿你当朋友(涩会涩会)
  3. 访问时必须通过
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    class A
    {
        friend void dis(A & t);
        friend class B;
    };
    // 该函数可以访问A的私有成员
    void dis(A & t);//访问时必须通过对象访问,因为他不是成员函数
    // 该类可以访问A的私有成员
    class B{void dis(A & t);};
    

5.4 单双目运算符重载

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//不能重载的运算符
    .  .*  ::  ?:  sizeof //sizeof是运算符,不是函数
//除了=号外,基类中被重载的操作符都将被派生类继承
    //“赋值运算符重载函数”不是不能被派生类继承,而是被派生类的默认“赋值运算符重载函数”给shadow了
//通常情况下,双目运算符重载为成员需要一个参数,重载为友元要两个参数,做成员时this也是参数
//重载-(负号)时用const修饰成员函数,-c=t就不会过编译了,这才是对的,注意-(-c)所以只能用const修饰成员函数而不是返回值。
//
    Clock operator++(Clock& c) //前置单目运算符 ++c
    Clock operator++(Clock& c,int) //后置单目运算符 c++
//day 6.2
//流输入输出运算符重载
    istream & operator>>(istream&,自定义类&);
    ostream & operator<<(istream&,const 自定义类&);

5.5 类型转化

  1. 使用 类型转化构造器
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    //实现 其他类型 到 本类型 的转化
    class 本类型
    {
        本类型(const 其他类型 & Ta) //这是一个构造器
        [
            //实现转化
            //this->data = Ta.data ;
        ]
    };
    本类型 = 其他类型; //隐式转化可用  先调用类型转化,在调用赋值运算符重载
    //我们把只有一个参数的构造器成为类型转化构造器
    explicit //加在转化构造器前表示只能使用 显式转化
    本类型 = static_cast<本类型>(其他类型);
    
  2. 使用 类型转换操作符函数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    class 源类
    {
        //无参无返回
        operator 目标类型(void)
        {
            //根据需要进行转化
            //目类标型.data = this->data;
            return 目标类型(this->data2,this.data2);//特殊,里面有返回,
            //实际也可以这么写
            目标类型 swa;
            swa.data1 = this->data1;
            return swa;
        }
    };
    

5.6 运算符重载提高篇

  1. 函数操作符()重载 – 仿函数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    //把类对象当作函数名使用,主要应与于STL和模板
    class 类名
    {
        返回值类型 operator() (参数类型)
        {
    
        }
    };
    //lambda 本质就是仿函数
    
    operator bool();//对象可以对void * 重载,if(A)情况,会调用bool();
    operator void *();//对象可以对void * 重载,if(A)情况,没有bool()会调用void*(),然后void*被强转成bool;
    
  2. 重载 new delete new[] delete[]
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    //定制化时使用,给特定类定制
    void * operator new(size_t)
    void operator delete(void *)
    void * operator new[](size_t)
    void operator delete[](void *)
    //注:operator new 中 size_t 参数是编译器自动计算传递的 其实是 unsigned int
    
    //了解一下就行↓
    #include <memory>
    class A{int a;};
    auto_ptr<A> ptr(new A); //auto_ptr 类模板 auto_ptr<A>模板类
    ptr->a=1;
    //一个对象的行为想指针,重载了 -> () *
    //new被ptr托管后,不需要再关心delete的问题
    

6.1 继承与派生Inherit&Derive

  1. c++ 通过继承关系实现了代码的可重用性。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    class 派生类名:继承方式 基类名
    {
    
    };
    //一个派生类可以有多个基类
    //继承方式 public protected private
    /*
                父类        公有继承    保护继承        私有继承
        成员属性↘ public      public       protected      private
                protected   protected    protected      private
                private     不可访问     不可访问       不可访问(inaccessable)
    */
    
    protected 对于外界访问属性来说等同于 private,但在派生类中可见
    
    几点说明:
    * 可见不可见都全盘接收,除了构造器与析构器,基类可能会造成派生类的成员冗余,是需要设计的
    * 派生类有了自己的个性,才有意义
    
  2. 派生类的构造器
    1
    2
    3
    4
    5
    6
    7
    
    类名(参数总列表)
        :父类名(父类构造器列表),自己的成员
    {
        //在参数列表里调用父类构造器,然后再初始化自己的成员
        //在父类中如果有标配,重载或默认,把无参的情况包含进来,子类可以不显式的调用父类构造器,隐式调用
    }
    //初始化顺序,父类,其他类对象成员,本类的初始化
    
  3. 派生类的拷贝构造器
    1
    2
    3
    4
    5
    6
    7
    8
    
    //子类未实现拷贝时,会调用父类构造器
    //子类一旦实现拷贝构造,则必须显式的调用父类拷贝构造器
    son(const son & another)
        :father(another)
    {
        // ↑↑↑赋值兼容(子类对象(引用或指针)可以赋给父类对象)
        this->data=another.data;
    }
    
  4. 派生类赋值重载
    1
    2
    3
    4
    5
    6
    7
    8
    
    //子类中未实现赋值重载时,会调用父类的赋值重载
    //子类一旦实现赋值重载,不会自动调用父类赋值重载
    son& operator=(son& another)
    {
                            //赋值兼容
        father::operator=(another);//重载是会被子类继承的,所以可以调用。与构造器不同
        this->data=another.data;
    }
    
  5. 重名问题
    1. 子类中会把父类中重名的成员shadow掉,只需要名字相同即可
    2. 尽量不要重名,否则,调用时加上命名空间
    3. overload 重载 发生在同一作用域,函数名相同,参数列表不同(个数,类型,顺序)
    4. shadow 发生在父子类当中,只要函数名相同即可
  6. 派生类的友元函数
    1. 父类的友元函数,子类也可以调用
    2. 传参时子类赋值给了父类,赋值兼容
  7. 派生类的析构器
    1. 析构顺序:子类,成员,基类
    2. 与构造相反
  8. 多继承
    1. 你有n个爹(逃)
    2. 三角问题,多个父类中相同的成员,用到要加父类作用域
      • 给调用者带来很大的不便
      • 办法,提取相同成员到一个公共类M中,然后让被提取的类,分别继承这个公共类M(三角变成了四角)
        • 但是子类还是无法访问这个成员,因为两个基类都继承了M,这个成员还是有两份
        • 最终办法 虚继承
  9. 虚继承 virtual(加在继承方式前面)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    //上面的 M 就叫做 虚基类
    //虚继承的意义:在多继承中,保存各个父类的多份同名成员,虽然有时是必要的,可以在不同数据成员中存放不同数据,但在大多数情况下
    //是我们不希望出现的,因为保留多份数据成员的拷贝,不仅占用较多的存储空间,还增加了访问困难
    //虚基类和虚继承,实现了在多继承中只保留一份共同(同名)成员。
    //虚基类,需要抽象和设计,虚继承,是一种继承的扩展
    //初始化问题,多继承的类中一定要调用虚基类的构造器,父类可以用默认
    class A{
        A(int i){}
    };
    class B:virtual public A{
        B(int n):A(n){}
    };
    class C:virtual public A{
        C(int n):A(n){}
    };
    class D:public B,public C{
        D(int n):A(n),B(n),C(n){}//结果有A()控制,其他配角
    };
    

7.1 多态PolyMorphism

男人不止一面(逃),几个相似但不完全相同的对象,收到同一个消息后,执行不同的操作

c++中所谓的多态是指,由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应

增加程序的灵活性,可以减轻系统升级,维护,调试的工作量和复杂度

  1. 赋值兼容(多态实现的前提)
    • 赋值兼容规则 是指在需要基类对象的任何地方都可以使用 公有派生类(其他不行) 的对象来替代,赋值兼容是一种默认行为,不需要任何的显式步骤
    • 派生类的对象可以赋值给基类对象
    • 派生类的对象可以初始化基类的引用
    • 派生类的对象的地址可以赋给指向基类的指针
    • 只能使用基类原有的成员
  2. 多态形成的条件
    1. 静多态: 函数重载,也是一种多态现象,通过命名倾轧在编译阶段决定,故称静多态
    2. 动多态: 在运行阶段决定 条件是
      1. 父类中有虚函数
      2. 子类override(覆写)父类中的虚函数
      3. 用已被子类对象赋值的父类指针或引用(对象不行),调用共用接口(发生虚函数调用)
  3. 虚函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    class A
    {
    public:
        virtual void func();//只需在声明时加;
    };
    //父类时有虚函数时,子类覆写override虚函数时(同名同参同返回),可以不加virtual,但建议加上
    //将子类对象赋值给父类引用或指针时,用这个父类指针或引用访问的虚函数为子类的虚函数(如果有的话)
    //子类中虚函数的访问权限可以不和父类一致
    //覆写范围是很大的,如果不覆写默认从父类继承
    
  4. 纯虚函数
    1
    2
    3
    4
    5
    6
    7
    8
    
    class A
    {
        virtual void func()=0;//格式,没有实现体
    };
    //含有纯虚函数的类,称为抽象基类(java中叫interface),不可以实例化(A a; 不可以)
    //作用就是给族类提供接口用的,抽象基类不能实例化,但可以用指针和引用指向一个子类,实现多态
    //抽象基类存在的意义就是被继承
    //如果派生类中没有对该函数override,则这个函数在派生类中仍为纯虚函数,派生类仍为纯虚基类
    
  5. 含有虚函数的析构: 虚析构,就是为了delete基类指针时将子类对象析构完全
    • 含有虚函数的类,析构函数也应该声明为虚函数
    • 在delete父类指针的时候,会调用子类的析构函数,实现完整析构
    • 当一个类中有虚函数的时候,请将析构器声明为vritual
  6. 若干限制
    1. 静态成员函数不能是虚函数,静态成员函数不受对象的捆绑,只有类的信息
    2. 内联函数,普通函数,不能是虚函数
    3. 构造函数不能是虚函数
    4. 析构函数通常声明为虚函数
  7. 设计模式 – 依赖倒置原则 //定义,高层模块不应该依赖底层模块,二者都应该依赖抽象 //抽象不应该依赖细节,细节应该依赖抽象 //核心:面向接口编程

8.1 多态的实现原理

  1. 虚函数表 virtual table
    • 类实例化时,内存最前面先放一个指向v-table的指针,然后是数据成员
    • v-table中,先放父类虚函数,再放子类虚函数
    • 如果子类override了父类虚函数,则替换虚函数表相应位置的函数指针
    • 多态实现的前题,一定是生成完了v-table之后,所以在父类构造器调用虚函数实现不了多态

8.2 运行时类型信息RTTI

run time type identificaition。运行时信息,来自于多态,所以一下运算符只用于基于多态的继承体系中

  1. typeid运算符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    //使用这个运算符 要包含头文件<typeinfo>
    //返回包含数据信息的type_info对象引用
    //type_info重载了==,!=来比较是否相等,函数name()返回类型名称
    //type_info的拷贝赋值为私有,不可操作,只用来做调试
    B b;
    cout<<typeid(b).name()<<endl;//输出b信息
    A * a=&b;
    cout<<typeid(*a).name()<<endl;//输出b信息
    //可以帮你辨别一个指针到底指向谁,在多态中
    

8.3 typecast

  1. static_cast<>()
    • 在一个方向上可以做隐式转换,在另一个方向方向可以做静态转换
    • 发生在编译阶段,不保证后续使用的正确性
    • 用于父子类不安全
  2. reinterpret_cast<>()
    • 既不在编译期也不再运行期进行检查,安全性完全由程序员决定
  3. dynamic_cast<>()
    • 运行时的类型转换方法,检查指针所指类型,然后判断是否与要转换的类型有一种“is a”的关系
    • 如果是,返回对象地址,不是返回null
    • 只用于含有虚函数的父子类中

9 c++模板 template

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//泛型(Generic Programming):多种数据类型上皆可操作的含义
//语法格式
template<typename/class 类型参数表>
返回类型 函数模板名(函数参数表)
{
    函数模板实现体
}

template<typename T,typename T2....>
void swapp(T a,T b)
{
    a^=b;b^=a;a^=b;
}

template<typename T>
class stackk{
    stackk();
};
template<typename T>
stackk::stackk<T>()
{}
//定义实现分开时,都要顶着帽子,(写类时,函数在外部实现也要戴帽子,并且函数名后加<T>)
//模板适用于参数个数相同,函数体相同的情况
//参数个数如果不同,不要使用模板

10 C++ 流类综述

流类对象都重载了 <<>>

  1. 输入输出IO流
    • IO对象的构造器和赋值重载是private,不能拷贝或赋值,使用时只能传引用或指针
    • IO对象是由缓冲区的,缓冲区写满或遇到endl才写入,(cin背后是键盘,cout背后是屏幕)
    • endl,flush,unitbuf 都可以刷缓冲
  2. 标准输出 cout cerr clog
    1
    2
    3
    4
    
    //cout.unsetf(ios::dec);
    //cout.setf(ios::hex);
    //<iomanip> 流算子头文件,上面并不好用
    //cout<<hed<<a<<endl;
    
  3. 标准输入 cin
    1
    2
    3
    4
    5
    6
    7
    
    //istream 成员函数
    cin.get();//读一个字符返回,包括空格回车tab空字符
    cin.get(buf,1024,'\n');
    cin.getline(buf,1024,'默认回车');
    istream& ignore(streamsize n=1,int delim=EOF);//跳过n个或遇到delim为止,delim也被跳过
    int peek();//窥视 当前指针不改动,返回内容(int 型),自己转char
    istream& putback(char c);//回推指针,并在此位置替换为c
    
  4. 文件IO流
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    
    ifstream ifile("1.txt");//只能从文件读入 默认模式 ios::in
        if(!ifile)cout<<"打开失败";
    ofstream ofile("2.txt");//只能写到文件 默认模式 ios::out|ios::trunc
        //没有这个文件自动创建
    fstream  iofile("3.txt");//默认模式 ios::in|ios::out|ios::app
        iofile.close();//刷缓冲区
    ios::in ios::out //不会自动创建文件
    ios::trunc //覆盖原来 会自动创建文件
    ios::app //追加 会自动创建文件
    ios::binary //二进制方式
    
    ios::cur //文件当前位置
    ios::end //文件末尾
    f.seekg(50,ios::cur);//seekget 从当前位置向后50
    f.seekp(-50,ios::end);//seekput 从文件尾向前50
    
    //标识
    f.eof();//达到文件尾,返回true
    f.bad();//读写过程出错,返回true
    f.fail();//除了和bad()一样,格式错误(例如想要读整数,得到字母),遇到eof都返回true
    f.good();//上面任何返回true,则返回false
    f.clear();//将所有标记位置位正常,不是清空流
    cin.sync();//清空流
    //遍历读,一个个读
    char c;
    while(f.get(c),!f.eof()){
        f2.put(c);//写入另一个文件
    }
    //一行一行读
    char buf[1024];
    while(f.getline(buf,1024,'\n'),!f.eof()){
        f2<<buf<<endl;//用get不会跳过\n,用getline \n被跳过,还要补;
    }
    //byte流读
    ostream & write(const char * buffer,int len);
    istream & read(const char * buffer,int len);
    struct stu{
        char name[100];
        char sex;
        int age;
    };
    stu s[5]=
    {
        {"as",'y',12},
        {"gg",'x',1},
        {"rr",'y',3},
        {"ww",'x',100},
        {"qq",'x',107}
    };
    fstream f("asd.binary",ios::in|ios::out|ios::trunc|ios::binary);
    //f.write(s,sizeof(s));
    for(int i=0;i<5;i++)
    {
        f.write((char *)&s[i],sizeof(stu));
    }
    f.seekg(0,ios::beg);
    stu tmp;
    while(f.read((char*)&tmp,sizeof(stu)),!f.eof()){
        cout<<tmp.name<<endl;;
    }
    

11 异常(EXCEPTION)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//在可能出错的地方使用try-catch结构
void func(int x){
    if(x<0)
        throw -1;//在try下面调用的函数中可以抛出异常,终止程序往下执行
            //程序立即转到上面捕获异常的地方
            //异常传递是一个拷贝,如果自定义类型,注意拷贝构造,
}
try{
    func(3);
}catch(int e)//如果下方扔出的异常不是int型,将接不到,程序调用另一个函数,终止整个程序
{//可以自己写异常信息类型,这样能得到更多信息
    cout<<"catch a exception "<<e<<endl;
}//catch执行完毕,继续执行后面的语句,如果没有匹配,系统调用terminate终止程序
//可以有多个catch
catch(...){
    //如果上面都没有接到,这个来接,有这个的时候,系统就不会调用terminate
}
//如果有多层try-catch结构,底层抛出一个异常,上层没有人接的话,继续传给上上层
//如果直到尽头都没有人接,系统才调用terminate
    //为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型
    //例如:
        void func() throw(int ,A ,B ,double);//括号没内容表示不抛出异常
//栈自旋 throw抛出异常时,会在离开栈空间时销毁栈,不执行后面的代码,但看作函数执行完毕,退出销毁
    //这也是为什么throw不传引用的原因

===========================华丽的分割线===================================

全剧终。。。

至此你看完了大部分c++ 没错,是看!!!