c++知识点整理(上)

好久没整理笔记了,整理了一份自己的c++学习笔记,为了方便查阅复习某模块知识


c++上部分

概要

  • 1、基本知识c++
  • 2、面向对象:封装,继承,多态
  • 3、模版
  • 4、STL
  • 5、特点:①过程编程②对象编程③泛型编程

基础知识:

istream ostream 输入流:流的输入,输入的数据
    标准输入         标准输出         标准错误
    (键盘)         (显示屏)
c    stdin          stdout          stderr
c++  cin            cout             cerr

std(标准命名空间)::(作用域运算符)

  • 命名空间:防止命名冲突
  • <<流输出运算符
  • cin输入的时候多个数据默认以空格,tab,enter这些空白字符进行分隔
#include <iostream> //istream ostream
using namespace std;
namespace A
{
    int a=3;
}
namespace B
{
    int a=5;
}
using namespace A;
//using namespace B;
int main()
{
    //cout<<"hello world"<<endl;

    //cout<<A::a<<" "<<B::a<<endl;

    cout<<a<<endl;

    return 0;
}

引用&

int a=5;
int &ra=a;//定义一个引用ra,ra是a的一个别名对引用的改动就是对引用的对象的改动引用能够引用任何合法的变量

传引用交换两个数的实例

#include <iostream>
using namespace std;

void Swap(int &ra,int &rb)
{
    int r=rb;
    rb=ra;
    ra=r;
}
int main()
{
    int a=5,b=5;
    int &ia=a;
    cout<<ia<<endl;
    ia=3;
    cout<<a<<endl;
    Swap(a,b);  //int &ra=a,int &b=b引用的方式进行交换
    cout<<"a="<<a<<"  b="<<b<<endl;
    return 0;
}

返回一个引用,返回该变量本身而不是副本,从而提高了效率

#include <iostream>
using namespace std;
int main()
{
    int a=10;
    const int &ra=a;//不能通过改变已经const 修饰ra,改变a;
    a=2;
    cout<<ra<<endl;
    return 0;
}

内联函数(inline)

内联函数:在函数的定义和声明前加关键字inline,则该函数为内联函数,定义为内联函数的函数,调用它的时候,程序的执行流程不能有循环语句或者开关语句,负责就是为普通函数处理内联扩展是用来消除函数调用时的时间开销。它通常用于频繁执行的函数。 一个小内存空间的函数非常受益。

注:

  • 如果有声明,在声明之前加了inline,函数的定义不用加。
  • 声明为内联函数的函数体内不能有循环语句或者开关语句,否则就是为普通函数处理。
  • 内联函数的函数体语句通常在5行左右。
//内联函数实例
#include <iostream>
using namespace std;
inline int fun(int a)
{
    return a*a*a;
}
int main()
{
    int i;
    for(i=1;i<11;i++)
    {
        cout<<fun(i)<<endl;
    }
    return 0;
}

函数的重载

函数的重载,多个函数具有相似的功能,我们可以把这些函数取同一个函数名。前提条件是这些函数在参数列表上有差异

参数列表差异:

  • 参数的个数不同fun(int) fun(int, int)
  • 参数的类型不同fun(int) fun(char)
  • 参数的顺序不同fun(int,char) fun(char,int)

三者只要满足一个,即可用同名函数
在调用时,会根据函数的实参去自动寻找最佳匹配的参数列表,找到就调用。
寻找最佳匹配的三个顺序:

  • 严格查找匹配 fun(int)fun(1)
  • 查找能够进行内部数据类型转换后匹配的fun(int t)fun(‘m’)
  • 查找强制类型转换后匹配的fun(int) fun((int)1.23)
//比较四个数的大小(函数重载)
#include <iostream>
using namespace std;
int Max(int n,int m,int k,int j)
{
    int t=Max(n,m,k);
    return t>j?t:j;
}
int Max(int n,int m,int k)
{
    int t=Max(n,m);
    return t>k?t:k;
}
int Max(int n,int m)
{
    return n>m?n:m;
}
int main()
{
    int a=1;
    int b=2;
    int c=3;
    cout<<Max(a,b,c)<<endl;
    return 0;
}
#include <iostream>
using namespace std;
void Print(int n)
{
    cout<<n*n<<endl;
    cout<<"xxx"<<endl;
}
void Print(double n)
{
    cout<<n*n<<endl;
    cout<<"xxx"<<endl;
}
int main()
{
    int a=1;
    double b=1.2;
    Print(a);
    Print(b);

    return 0;
}

//拓展:判断连续输入的数是否重复
#include <iostream>
using namespace std;
int main()
{
    int curval=0,val=0;
    if(cin>>curval)
    {
        int count=1;
        while(cin>>val)
        {
            if(curval==val)
                ++count;
            else
            {
                cout<<curval<<" occurs "<<count<<" times"<<endl;
                curval=val;
                count=1;
            }
        }
        cout<<curval<<" occurs "<<count<<" times"<<endl;
    }
    return 1;
}
//一年当中的第几天
#include <iostream>
using namespace std;
struct Data
{
    int i;
    int j;
    int k;
}data;
int mon[12]={31,29,31,30,31,30,31,31,30,31,30,31};

int islyear(int year)
{
    return (year%4==0 && year%100!=0) || year%400==0;
}
int main()
{
    int day=0;
    cout<<"year:";
    cin>>data.i;
    cout<<"mon:";
    cin>>data.j;
    cout<<"day:";
    cin>>data.k;
    int n=0;
    for(n=0;n<data.j-1;n++)
    {
        day+=mon[n];
    }
    day+=data.k;
    if(!(data.i%4)&&(data.i%100)||!(data.i%400))
        if(data.j>=3)
        {
            day--;
        }
    cout<<":"<<day<<endl;
    return 0;
}
//十三个人123当中的叛徒
typedef struct link{
    int num;
    int flag;
    struct link *next;
}LINK,* pLINK;

#include <iostream>
using namespace std;
#inlcude <stdio.h>
int main()
{
    int i,j=1;
    pLINK head=(pLINK)malloc(sizeof(LINK));
    for(i=1;i<14;i++,j++)
    {
        pLINK p=(pLINK)malloc(sizeof(LINK));
        p->num=i;
        if(j==4)
        {
            j=1;
        }
        p->flag=j;
        if(head->next==NULL)
        {
            head->next=p;
            p->next=NULL;
            continue;
        }
        pLINK tail=head->next;
        for (;tail->next!=NULL; tail=tail->next)
        {}
        tail->next=p;
        p->next=NULL;
    }
    pLINK tail=head->next;
    for(;tail->next!=NULL;tail=tail->next);
    tail->next=head->next;
    pLINK temp=head->next;
    int n;
    for(i=j;i<5;i++)
    {
        if(temp->flag==3)
        {
            temp=temp->next;
            i--;
        }
        else
        {
            if(i==4)
            {
                i=1;
            }
            temp->flag=i;
            if(n==temp->num)
            {
                cout<<temp->num<<"  "<<endl;
                return 0;
            }
            n=temp->num;
            temp=temp->next;
        }
    }
    return 0;
}

参数默认值函数

参数的默认值函数:在函数定义或声明的时候,给参数进行赋值,如果调用没有相应的实参,就取默认值
  • 设置默认值的时候,从最左到右边开始设置,必须是连续的
    替换时从最左开始依次替换,没有替换采取默认值
int i(3),j(5);//int i=3,j=5;
int Add(int a,int b=i+j,int c=i*j)
{
    return a+b+c;
}
int main()
{
    int x(10);
    cout<<Add(x)<<endl;
    return 0;
}

封装

引入:描述一个学生diary score name

struct student
{
//特征:
    private:
    int diray;//只有自己可以看
    protected:
    float score;//只有家人朋友可以看
    public:
    char *name;//大家都可以看
//行为:
    void write()
    {
        ...
    }
    void test()
    {
        ... 
    }
};

student stu1;
stu1.diray;
stu1.score;
stu1.name;

类(class)

类:类型,自定义的一个类型,在c++中封装了特征(成员数据),和行为(成员函数),使它更直接恰当的描述一个对象

类+对象
怎么定义一个类:

  • 关键字:struct
struct 类名{
    数据成员;
    成员函数;
};
  • 关键字:class
class A
{
    private:
        int a;
    public:
    void Print
    {
        count<<a<<endl;
    }
};

//查询是不是闰年
class data{
    private:
    int year;
    int month;
    int day;
    public:
    void Setday(int y,int m,int d)
    {
        year=y;
        month=m;
        day=d;
    }
    void Print()
    {
        cout<<year<<" "<<month<<" "<<day<<endl;
    }
    int Islyear()
    {
        return (year%4==0 && year%100!=0)|| year%400==0;
    }
};
int main()
{
    data d;
    d.Setday(2016,1,1);
    d.Print();
    if(d.Islyear())
    {
        cout<<"is leap year"<<endl;
    }
    else
    {
        cout<<"not leap year"<<endl;
    }
    return 0;
}

对象

用类定义对象: A a;定义了一个A类型的对象a;

class Spot{
    private:
        int x;
        int y;
    public:
        void Setspot(int a,int b);
        void Mvspot(int a,int b);
        void Print();
};
void Spot::Setspot(int a,int b)
{
    x=a;
    y=b;
}
void Spot::Mvspot(int a,int b)
{
    x+=a;
    y+=b;
}
void Spot::Print()
{
    cout<<"x:"<<x<<" --- y:"<<y<<endl;
}
int main()
{
    Spot s;
    s.Setspot(0,0);
    s.Print();
    int a,b;
    cin>>a>>b;
    s.Mvspot(a,b);
    s.Print();
    return 0;
}

在一个类里面,拥有数据成员和成员函数,并且每一个成员都要标明它的被访问权限,也就是要用下面关键字来限制说明每一个成员

  • private:修饰成员,在类的外面不能被访问
  • protected:修饰成员,在类的外面不能被访问
  • public:修饰成员,在任何都能被访问

class和struct的区别:(面试)

  • class定义的类,不写被访问权限默认为私有的private
  • struct定义的类,不写被访问权限默认为公有的public

使用类时要注意:

  • 每个成员都要有被访问权限至于顺序随便
  • 可以在类里面定义成员函数,但是不能在类里面定义数据成员
  • 定义类的时候自身类的对象不能作为该类的数据成员
class A
{
    private:
        A  a;//错误
        A *p;//正确
};
//模拟取钱系统
class Card{
    private:
    int a;
    int b;
    int c;
    public:
    void setcard(int x,int y,int z)
    {
        a=x;
        b=y;
        c=z;
    }
    void printcard()
    {
        cout<<"余额:"<<a<<"元"<<b<<"角"<<c<<"分"<<endl;
    }
    void depcard(int x,int y,int z)
    {
        a+=x;
        b+=y;
        c+=z;
    }
    void takecard(int x,int y,int z)
    {
        a-=x;
        b-=y;
        c-=z;
    }
};
int main()
{
    Card user;
    user.setcard(0,0,0);
    while(1)
    {
        cout<<"1.余额"<<endl;
        cout<<"2.存款"<<endl;
        cout<<"3.取款"<<endl;
        cout<<"0.EXIT"<<endl;
        int key;
        cin>>key;
        int x,y,z;
        switch(key)
        {
            case 1:
                user.printcard();
                break;
            case 2:
                cout<<"请输入存款(元,角,分):"<<endl;
                cin>>x>>y>>z;
                user.depcard(x,y,z);
                break;
            case 3:
                cout<<"请输入取款(元,角,分):"<<endl;
                cin>>x>>y>>z;
                user.takecard(x,y,z);
                break;
            case 0:
                return 0;
        }
    }
    return 0;
}

成员函数的重载

  • 同一个类里面的成员函数可以发生重载。
  • 但是不同类,即使名字相同,也不能重载。
  • 通常把类的定义放在头文件中。
class A
{
    void print (int i);
    void print (int i,int j);
}

类与内存

  • 类不占内存,它只是一个类型,对象或者变量才占内存。
  • 在类里面占内存的是它的数据成员。

构造函数:

  • 作用:给对象开辟空间并初始化。
  • 定义:属于类里特殊的成员函数,函数名等于类名,没有返回值,也没有返回值类型,在定义对象的时候被系统自动调用。
  • 类里面存在构造函数的话系统将不会提供默认的构造函数

构造函数的种类:

  • 默认构造的函数
    ①当没有写构造函数的时候系统提供一个默认的
    ②自己写的不带参数的构造函数
class A
{
    private:
        int a,b;
    public:
        A()
        {
            a=1;
            b=2;
        }
        void Print()
        {
            cout<<a<<b<<endl;
        }
};
int main()
{
    A a;
    a.Print();
    return 0;
}
  • 带参数的构造函数
    因此构造函数也可以发生函数的重载尽量要写个不带参数的构造函数。
A(int i)
A(int i,int j)
class A{
    private:
        int a,b;
    public:
        A()
        {
            cout<<"1using"<<endl;
        }
        A(int i,int j)
        {
            a=i;
            b=j;
            cout<<"2using";
        }
        void Print()
        {
            cout<<a<<"  "<<b<<endl;
        }
};
int main()
{
    A a(1,2);
    a.Print();
    return 0;
}

  • 拷贝构造函数/默认拷贝构造函数
AA  &a)
{
}
class A{
    private:
        int a,b;
    public:
        A()
        {
            cout<<"1using"<<endl;
        }
        A(int i,int j)
        {
            a=i;
            b=j;
            cout<<"2using"<<endl;
        }
        A(A &c)
        {
            a=c.a;
            b=c.b;
            cout<<"3using"<<endl;
        }
        void Print()
        {
            cout<<a<<"  "<<b<<endl;
        }
};
int main()
{
    A a(1,2);
    A b(a); //A b=a;
    b.Print();
    return 0;
}

深拷贝和浅拷贝:

  • 浅拷贝的时候简单的把值拷贝进去。
  • 深拷贝连同资源一块拷贝

析构函数:

  • 作用 :负责释放对象的空间,也是类的特殊的成员函数,也是由系统自动调用,没有返回值,包括返回值类型,也没有参数,所以不能发生函数的重载
  • 定义:~类名(){}
//例:析构函数
~A()
{
}
  • 析构顺序:先构造的对象后析构,即析构函数的调用顺序与构造相反
class A{
    private:
        int a;
    public:
        A(){}
        A(int j)
        {
            a=j;
            cout<<a<<"use A"<<endl;
        }
        void Print()
        {
            cout<<a<<"----"<<endl;
        }
        ~A()
        {
            cout<<a<<"use ~A"<<endl;
        }
};
int main()
{
    A a(1);
    A b(2);
    return 0;
}


类中的static

  • 静态数据成员:
    在成员前面间static,静态数据成员是属于类的,是所有对象公用的,在使用它之前需要类外初始化。
    类名::静态数据变量名”来应用它
class A{
    private:
        static float money;
    public:
        void Save(int a)
        {
            money+=a;
        }
        float Show()
        {
            return money;
        }
};
float A::money(10);

int main()
{
    A h,w;
    h.Save(1000);
    w.Save(1000);
    cout<<h.Show()<<endl;
    return 0;
}
//从右向左运算
class A{
private:
    int a,b,c;
    static int sum;
public:
    A(int i,int j,int k)
    {
        a=i;
        b=j;
        c=k;
        sum+=a+b+c;
    }
    void Show()
    {
        cout<<a<<" "<<b<<" "<<c<<endl;
    }
    int Resum()
    {
        return sum+=1;
    }
};
int A::sum(0);

int main()
{
    A m(1,2,3);
    A n(4,5,6);
    cout<<n.Resum()<<"   "<<m.Resum()<<endl;//计算从右到左
    cout<<n.Resum()<<"   "<<m.Resum()<<endl;
    return 0;
}
  • 静态成员函数:
    (1) 定义一个静态成员函数,在该成员函数里面分别访问静态数据成员和普 通数据成员,探索分别有哪些访问方式。在类外调用该静态成员函数又有哪些访问方式。
    (2)在静态函数里面只能通过”对象.”的方式来访问普通数据成员,不能直接访问。访问静态数据成员,三种方式都可以。
    (3)在类外可以通过对象来访问静态成员函数,也可以通过类名作用域来访问静态成员函数
class A{
    private:
        int m;
        static int n;
    public:
        A(int a)
        {
            m=a;
            n=+a;
        }
        static void fun(A a)
        {
            cout<<"m= "<<a.m<<endl;// m , A::m , a.m
            cout<<"n= "<<n<<A::n<<a.n<<endl;
        }
};
int A::n(1);

int main()
{
    A a1(1);
    A a2(2);
    A::fun(a2);
    a2.fun(a2);
    return 0;
}

静态成员函数的一个特点:就是专门用来处理静态数据成员,可以脱离对象的存在而被调用


初始化列表:

在一个类里面有数据成员,常整型数(const int a),静态常整型数(static const int b),常整型数的引用(const int &c),试在构造函数里面分别给他们初始化,并调用成员函数 Print(),输出它们的值

常数据成员,引用数据成员的初始化放在构造函数初始化列表里进行。

class A
{
private:
    const int a;
    static const int b;
    const int &c;
public:
    A(int i):a(i),c(a)//构造函数初始化列表
    {
    }
    void Print()
    {
        cout<<a<<" "<<b<<" "<<c<<endl;
    }
};
const  int A::b(10);

int main()
{
    A a(1);
    a.Print();
    return 0;
}

在类里面定义一个普通成员函数Print,和一个同名的常成员函数Print,在主函数里定义一个该类的普通对象和常对象,分别调用Print()函数,观察实验现象,得出结论:

  • 常对象默认调用常成员函数,普通对象默认调用同名的普通成员函数
  • 普通成员的普通对象既能调用普通成员的函数,也能调用常成员函数
  • 常对象只能调用常成员函数
class A{
private:
    int a;
    const int b;
public:
    A(int i,int j):b(j)
    {
        a=i;
    }
    void Print()
    {
        cout<<a<<" "<<b<<endl;
    }
    void Print() const
    {
        cout<<a+1<<" "<<b+1<<endl;
    }
};

int main()
{
    A a(1,2);//1 2
    const A b(3,4);//4 5 常对象
    a.Print();
    b.Print();
    return 0;
}

常成员函数里面可以给数据成员赋值吗?不能。
常成员函数里面可以调用常成员函数吗?可以调用普通成员函数吗?前者可以(两个常成员函数里都不会改变数据数值),后者不可以。


分配(释放)内存[new(delete)]

int *p=new int;
int *p=new int5); 
class A {
    private :
        int a;
        int b;
        char *p;
    public:
        A (int i,int j,const char *str)
        {
            a=i;
            b=j;
            p=new char[20];
            strcpy(p,str);
        }
        A (A &C)
        {
            a=C.a;
            b=C.b;
            p=new char[20];
            strcpy(p,C.p);
        }
        ~A()
        {
            delete[] p;
        }
        void Print()
        {
            cout<<a<<"  "<<b<<"  "<<p<<endl;
        }
};

int main()
{
    A a(1,2,"qwer");
    a.Print();
    A b(a);
    b.Print();//
    return 0;
}

分别定义一个指向类里面的数据成员和函数成员的指针。通过指针访问相应的数据成员和函数成员

class A
 {
    private:
        int a;
    public:
        int c;
        A(int i)
        {
            a=i;
        }
        int fun(int b)
        //void fun(); void (A::*pfun)(); void (p->*pfun)();
        {
            return a*c+b;
        }
};
int main()
{
    A x(10);
    int A::* pc=&A::c;//pc为指向类里数据成员的指针public
    x.*pc=5;
    int (A::* pfun)(int)=&A::fun;//pfun为指向类里成员函数的指针public
    A *p=&x;
    cout<<(p->*pfun)(20)<<endl;
    return 0;
}

友元函数(friend)

  • 引入原因和目的:由于类具有封装性,类里面的私有成员不能在类外被直接访问,只能通过类的公共成员函数来访问,由于频繁的调用成员函数,函数传参,类型检查都占用时间和空间,从而降低了效率,为了提高效率,我们需要在类外的普通函数能够不用通过调用成员函数而能直接访问私有数据成员,故此,引进友元函数。
  • 它能够直接访问类里面的任何成员,包括私有成员,通过对象访问。友元函数是一个定义在类外面的普通函数,不是类的成员函数,但是在类里面被说明为类的朋友。
class A{
    friend void func(A &c);//声明这个函数是类的朋友,可放在类里面任何地方
    private:
        int a;
        int b;
    public:
        A(int i,int j)
        {
            a=i;
            b=j;
        }
        void fun()
        {
            a=10;
            b=10;
        }
        void Print()
        {
            cout<<a<<" "<<b<<endl;
        }
};
void func(A &c)//友元函数通过该类的对象来访问类的私有成员
{
    c.a=1;
    c.b=1;
}
int main()
{
    A a(11,12);
    a.Print();
    a.fun();
    a.Print();
    func(a);
    a.Print();

    return 0;
}
/******************
 * 友元函数实例
 ******************
/*有一个类Point 在该类里面有一个获得点位置的函数Getxy();有一个求两点之间距离的友元函数,double Distance(Point,Point),
是求主函数中两点间距离。*/
class Point
{
    friend double Distance(Point a,Point b);
    private:
        int x;
        int y;
    public:
        Point(int i,int j)
        {
            x=i;
            y=j;
        }
        void Getxy()
        {
            cout<<x<<" "<<y<<endl;
        }
};
double Distance(Point a,Point b)
{
    double dx=a.x-b.x;
    double dy=a.y-b.y;
    return sqrt(dx*dx+dy*dy);
}
int main()
{
    Point a(1,2);
    a.Getxy();
    Point b(2,3);
    b.Getxy();

    cout<<Distance(a,b)<<endl;
    return 0;
}

友元类

  • 友元类,即y是x的朋友,即y所有成员函数都是x的友元函数
class X{
    friend class Y;//友元类,即y是x的朋友,即y所有成员函数都是x的友元函数
    private:
        int a;
        static int b;
    public:
        void Set(int x)
        {
            a=x;
        }
        void Display()
        {
            cout<<a<<" "<<b<<endl;
        }
};
int X::b(10);

class Y{
    private:
        X a;
    public:
        void Set(int x,int y)
        {
            a.a=x;
            a.b=y;
        }
        void Display()
        {
            cout<<a.a<<" "<<a.b<<endl;
        }
};

局部类:

  • 定义一个在函数里面的类
  • 局部类的成员函数不能够在类外定义
  • 局部类作用域只限于它的函数体内
  • 局部类不能拥有静态数据成员
void  func()
{
    class A{
        private:
            int a;
        public:
            A(int i)
            {
                a=i;
            }
            void Print()
            {
                cout<<a<<endl;
            }
    };
    A a(1);
    a.Print();
}

int main()
{
    func();
    return 0;
}

this指针

  • 在cpp中当一个成员函数被调用的时候,系统会向它传递一个默认的参数,这个参数是一个指针,一个指向接收该成员函数调用的对象的指针,即指向调用该成员函数的对象的指针,叫this指针,这样就建立起了被调函数与调用对象之间的关系了
class A
{
privateint a,b;
publicvoid Set(int i ,int j)
    {
        this->a=i;
        this->b=j;
    }
}

class A{
    private:
        int a,b;
    public:
        A(int i,int j)
        {
            a=i;
            b=j;
        }
        void Copy(A &c)
        {
            if(this==&c)
            {
                return;
            }
            *this=c;
        }
        void Print()
        {
            cout<<a<<" "<<b<<endl;
        }
};
int main()
{
    A a1(1,2);
    A a2(a1);
    a2.Print();
    return 0;
}

注:当用一个含有数据成员的类,定义一个常对象时,如果没有构造函数,将不能成功,除非这个类里面,没有数据成员

class A
{
private:
    int a;
public:
    A()
    {};
    void Set(int i)
    {
        a=i;
    }
    void Print()
    {
        cout<<a<<endl;
    }
};
int main()
{
    //A a;
    const A a;
    //当用一个含有数据成员的类定义一个常对象时
    //如果没有构造函数,将不能成功,除非这个类里面,没有数据成员
    return 0;
}

无名对象

  • 用完就释放
  • 调用右边的构造函数完成左边对象的构造
class A
{
    private:
        int a,b;
    public:
        A()
        {
            cout<<"defualt"<<endl;
        }
        A(int i,int j)
        {
            a=i;
            b=j;
            cout<<"constructor"<<a<<endl;
        }
        ~A()
        {
            cout<<"deconstructor"<<a<<endl;
        }
        void Print()
        {
            cout<<a<<" "<<b<<endl;
        }
};
int main()
{
    /*
    A a1;
    a1=A(1,2);//无名对象,用完就释放
    defualt
    constructor1
    deconstructor1
    deconstructor1
    */
    A a2=A(3,4);//调用右边的构造函数完成左边对象的构造
    A a1(1,2);
    return 0;
}

class A{
    private:
        char name[20];
        int b;
    public:
        A()
        {
            cout<<"default"<<endl;
        }
        A(const char *str,int n)
        {
            strcpy(name,str);
            b=n;
            cout<<"constructor:"<<name<<endl;
        }
        ~A()
        {
            cout<<"deconstructor:"<<name<<endl;
        }
        void Get(char *s,int &n)
        {
            strcpy(s,name);
            n=b;
        }
};
int main()
{
    /*char name[20];
    int b;
    A *p;
    p=new A[5];
    p[0]=A("liu",1);
    p[1]=A("zhang",2);
    p[2]=A("wang",3);
    *(p+3)=A("li",4);
    *(p+4)=A("sun",5);
    for(int i=0;i<5;i++)
    {
        p[i].Get(name,b);
        cout<<name<<" "<<b<<endl;
    }*/
    A m[2]={A("aaa",1),A("bbb",2)};
    cout<<"   xxx  "<<endl;
    return 0;
}

class A{
    private:
        int a,b;
    public:
        A(int i,int j)
        {
            a=i;
            b=j;
        }
        void Print()
        {
            cout<<a<<" "<<b<<endl;
        }
};
int main()
{
    A a1(1,2),a2(2,3),a3(3,4),a4(4,5),a5(5,6);
    A *p[5]={&a1,&a2,&a3,&a4,&a5};
    for(int i=0;i<5;i++)
    {
        (*(p+i))->Print();
    }
    return 0;
}

/*定义类A ,B ,B含有A a1,a2;int b;int & b1;const int b2
这些数据成员,定义一个B 类的Print()函数,并输出各数据成员的值。*/
class A{
    private:
        int a;
    public:
        A()
        {
            a=0;
            cout<<"A default constructr called  "<<a<<endl;
        }
        A(int i)
        {
            a=i;
            cout<<"A constructor called  "<<a<<endl;
        }
        int Returna()
        {
            return a;
        }
};
class B{
    private:
        A a1,a2;//子对象
        int b;
        int &b1;
        const int b2;
    public:
    B():b1(b),b2(1)
    {
        b=2;
        cout<<"B defalut constructor called"<<endl;
    }
    B(int i,int j,int k,int m):a2(j),a1(i),b1(b),b2(k)
    {
        b=m;
        cout<<"B constructor called  "<<b<<endl;
    }
    void Print()
    {
        cout<<a1.Returna()<<"   "<<a2.Returna()<<endl;       cout<<b<<" "<<b1<<" "<<b2<<endl;
    }
};
int main()
{
    B b(1,2,3,4);
    b.Print();
    return 0;
}

/***************
 *模拟系统的登录
 ***************
class user
{
    private:
        const static char uid[],pwd[];
        static int counter;
        user()
        {}
    public:
        static user *login();
        void logout(user *user);
        void showusers()
        {
            cout<<" counter:"<<counter<<endl;
        }
};
const char user::uid[]="root";
const char user::pwd[]="1234";
int user::counter=0;

user *user::login()
{
    char buf[10];
    cout<<"id:";
    cin>>buf;
    if(strcmp(uid,buf))
        cout<<"no id"<<endl;
    else
    {
        cout<<"pwd:";
        cin>>buf;
        if(strcmp(pwd,buf))
            cout<<"no pwd"<<endl;
        else
        {
            counter++;
            cout<<counter<<"login success"<<endl;
            user *p=new user;
            return p;
        }
    }
    return NULL;
}
void user::logout(user *user)
{
    cout<<counter<<"exit"<<endl;
    counter--;
    delete user;
}
int main()
{
    user *people[10];
    user *user;
    int chose,counter=0;
    while(1)
    {
        cout<<"\n1-login"<<"\n2-logout"<<"\n3-counter"<<endl;
        cin>>chose;
        switch(chose)
        {
            case 1:
                user=user::login();
                if(user!=NULL)
                {
                    people[counter++]=user;
                }
                break;
            case 2:
                user->logout(people[--counter]);
                if(counter==0)
                {
                    cout<<"pro exit"<<endl;
                    return 0;
                }
                break;
            case 3:
                user->showusers();
                break;
        }
    }
    return 0;
}

类中的const

  • 当类里面有const或引用时必须手写一个构造函数,在构造函数初始化列表里实现他们的初始化
  • 当类中含有子对象的时候,会先执行子对象的构造函数,再执行自己的构造函数。如果没有写上对子对象构造函数的调用,则会调用子对象的默认构造函数
  • 如果要调用子对象带参的构造函数,则需要调用类中带参的构造函数,并在构造函数初始化列表中写上子对象并传进参数
B(int i):a(i)
{}
  • 调用子对象的构造函数的顺序与他们在类中被定义的顺序有关,与他们在构造函数初始化的列表顺序无关

构造函数情况
B中有一个含有A类的对象

  • AB中都没有写构造函数
    通过B定义一个对象调用B的默认构造函数,但是在执行之前会调用并执行子对象A 的默认构造函数
  • B中写了一个默认构造函数
    B(){}
    A中也写了一个默认构造函数
    A(){}
  • B中写了一个带参构造函数
    B(int i){}
    A中也写了一个带参构造函数和一个默认构造函数
    A(int i){}
    通过B定义对象 B b(1);
    会调用A的默认构造函数和B的带参构造函数
    通过B定义对象 B b(1):a(1);
    会调用A的带参构造函数和B的带参构造函数

继承

基类和派生类

  • 已知一个类,该类有一定的属性和行为,我们暂且称之为基类,在该类的基础上,我们增加了一些新的成员,产生了一个新类,我们可以说是基类派生出了这个新类,所以新类称为派生类,又可以说派生类继承了基类,所以我们又可以把基类称为父类,派生类称为子类,从父类派生出子类的过程,我们称之为继承。

继承的好处:

  • 代码重用,程序可以在一个已有类的基础上,快速创建一个新类,提高了代码编辑效率

继承分类:单一继承和多重继承

  • 单一继承: 派生类只有一个基类继承
  • 多重继承:派生类有两个或以上基类的继承

单一继承

  • 单一继承的格式:
class 派生类名:继承方式 基类名
{
    派生类中新成员的定义;
};
  • 继承方式:
1.公有继承:public
2.私有继承:private
3.保护继承:protected
class A {
privateint a;
};
class B :public A
{
private:
    int a;
};

1.公有继承特性:

基类的私有成员,在子类的成员函数中不能被访问,protected成员在子类中仍为protected属性,public成员仍为public属性。在类外,只能访问public属性的成员

2.保护继承特性:

基类的私有成员,在子类中不能访问,保护成员和公有成员在子类中都为保护成员。在类外,不能通过子类的对象访问基类中任何成员

3.私有继承特点:

基类中的私有成员在子类中不可访问,保护和公有成员在子类中,都为私有成员。在类外,不能通过子类的对象访问基类中任何成员
class A
{
    private:
        int a1;
    protected:
        int a2;
    public:
        int a3;
};
class B:public A //B称为C的直接基类,A是C的间接基类
{
    private:
        int b1;
    protected:
        int b2;
    public:
        int b3;
        void Set()
        {
            //a1=1;
            a2=2;
            a3=3;
            b1=1;
            b2=2;
            b3=3;
        }
};

class C:public B
{
    private:
        int c1;
    protected:
        int c2;
    public:
        int c3;
        void Set()
        {
            //a1=1;
            a2=2;
            a3=3;
            //b1=1;
            b2=2;
            b3=3;
            c1=1;
            c2=2;
            c3=3;
        }
};
int main()
{
    B b;
    b.Set();
    return 0;
}

多重继承

  • 多重继承与单一继承的区别只在基类的数目,一个基类的继承是单一继承,多个基类继承是多重继承。多重继承中由于基类数目的增加,就相应的带来复杂性,单一继承的特点在此基本适应
  • 定义多重继承的派生类时,应指出它的所有基类的基类名和继承方式,格式:class 派生类名:继承方式1 基类名1 ..
class D :public: B1,public B2
{
   //派生类D中包含基类B1,B2的所有成员,还包含自定义的成员
};
  • 多重继承派生类的构造函数:
与单一继承派生类的构造函数的区别主要是多重继承构造函数的成员初始化列表中应包含所有基类的构造函数。
  • 格式:
派生类构造函数名(参数表):基类名1(参数表1...
{
   派生类构造函数体;
};
  • 多重继承派生类构造函数的执行顺序与单一继承派生类构造函数执行顺序相似。
构造函数执行顺序:先执行所有直接类的构造函数,在多个基类的构造函数中派生类被定时的基类顺序先后依次执行,与成员初始化表中给定的基类顺序无关,在执行子对象类的构造函数,最后执行派生类的构造函数体。
class B1
{
private:
    int b1;
public:
    B1(int i)
    {
        b1=i;
        cout<<"constructor B1.b1="<<b1<<endl;
    }
    void Print()
    {
        cout<<b1<<endl;
    }
    ~B1()
    {
        cout<<"destructor B1.n1="<<b1<<endl;
    }
};
class B2{
private:
    int b2;
public:
    B2(int i)
    {
        b2=i;
        cout<<"constructor B2.b2="<<b2<<endl;
    }
    void Print()
    {
        cout<<b2<<endl;
    }
    ~B2()
    {
        cout<<"destructor B2.n2="<<b2<<endl;
    }
};
class B3{
private:
    int b3;
public:
    B3(int i)
    {
        b3=i;
        cout<<"constructor B3.b3="<<b3<<endl;
    }
    void Print()
    {
        cout<<b3<<endl;
    }
    ~B3()
    {
        cout<<"destructor B3.n3="<<b3<<endl;
    }
};
class B4{
private:
    int b4;
public:
    B4(int i)
    {
        b4=i;
        cout<<"constructor B4.b4="<<b4<<endl;
    }
    void Print()
    {
        cout<<b4<<endl;
    }
    ~B4()
    {
        cout<<"destructor B4.n4="<<b4<<endl;
    }
};
class D:public B3,public B1,public B4
{
    private:
        int d;
        B2 b2;
    public:
        D(int d1,int d2,int d3,int d4,int d5):B1(d1),B3(d3),B4(d4),b2(d2),d(d5)
    {}
        void Print()
        {
            B1::Print();
            b2.Print();
            B3::Print();
            B4::Print();
            cout<<d<<endl;
        }
};
int main()
{
    D d(11,12,13,14,15);
    d.Print();
    return 0;
}

构造函数执行顺序类的赋值规则

执行顺序

构造函数中各项执行的顺序:先调用基类的构造函数,子对象的构造函数,构造函数初始化列表中的其他项,子类构造函数的函数体。析构顺序与构造的顺序相反。
class A
{
    private:
        int a;
    public:
        A()
        {
            a=0;
            cout<<"default constructor called a="<<a<<endl;
        }
        A(int i)
        {
            a=i;
            cout<<"constructor called a="<<a<<endl;
        }
        ~A()
        {
            cout<<"A deconstructor called"<<endl;
        }
};
class B:public A
{
    private:
    int b;
    A aa;
    public:
    B()
    {
        b=0;
        cout<<"default constructor called b="<<b<<endl;
    }
    B(int i,int j,int k):aa(j),A(i),b(k)
    {
        cout<<"constructor called b="<<b<<endl;
    }
    ~B()
    {
        cout<<"B deconstructor called"<<endl;
    }
};
int main()
{
    B b(1,2,3);
    return 0;
}

把指向子类对象的指针赋给指向基类的对象的指针,那么指向基类对象的指针指向的对象即为该子类对象中的基类部分

class M
{
    private:
        int m;
    public:
        M()
        {
            m=0;
        }
        M(int i)
        {
            m=i;
        }
        void Print()
        {
            cout<<m<<endl;
        }
        int Getm()
        {
            return m;
        }
};
class N:public M
{
    private:
        int n;
    public:
        N()
        {
            n=0;
        }
        N(int i,int j):M(i),n(j)
        {
        }
        void Print()
        {
            M::Print();
            cout<<n<<endl;
        }

};
void fun(M &p)
{
//    cout<<p.Getm()<<",";
    p.Print();
}
int main()
{
    M m(17),q;
    N n(13,18);
    n.Print();
    M *pm=new M(16);
    N *pn=new N(15,9);
    pm=pn;//把指向子类对象的指针赋给指向基类的对象的指正,那么指向基类对象的指针指向的对象即为该子类对象中的基类部分
    pm->Print();
    fun(*pn);

    N n1(21,34);
    M &rm=n1;
    n1.Print();//通过子类对象调用与基类中同名的成员函数时,默认调用的是子类的同名函数
    rm.Print();
    return 0;
}

赋值规则:

  • 同一类的对象互相间可以赋值,不同类的对象在某种条件下也可以赋值,赋值兼容规则是指在公有继承情况下,派生类的对象可以作为,基类的对象来使用。

赋值规则:

  • 派生类的对象可以赋值给基类的对象
D类公有继承B类
D d;
B b;
b=d;
  • 派生类的对象可以用来初始化基类对象的引用
D d;
B &rb=d;
  • 派生类对象的地址值可以用来指向基类对象的指针赋值
D d;
B *pb=&d;

多态

c++中的virtual

  • 因为类AB中都有f()他们都是C类的对象可访问的成员,于是就无法确定是A,B类中的f(),这就是二义性,消除二义性的方法:c.A::f();c.B::f();
    BC类中都有g()但是不会产生二义性,因为一个是基类的,一个是子类中的,规定,派生类中的成员函数可支配基类中的同名函数,所以通过派生类调用同名成员会直接调用派生类中的
class A
{
    public:
    void f()
    {
        cout<<"A f()"<<endl;
    }
};
class B{
    public:
    void f()
    {
        cout<<"B f()"<<endl;
    }
    void g()
    {
        cout<<"B g()"<<endl;
    }
};
class C :public A ,public B
{
    public:
        void  g()
        {
            cout<<"C g()"<<endl;
        }
        void h()
        {
            A::f();
            B::f();
            cout<<"C h()"<<endl;
        }
};
int main()
{
    C c;
    //c.f()
    c.g();
    return 0;
}

虚基类和虚继承

虚基类:

  • 派生类C 中包含了两个类A 的成员,即出现了公共基类,不只给程序带来了二义性,而且还在创建C 类的对象时两次调用了A 的构造函数,为了解决问题避免可能出现的二义性,和使用公共基类只产生一个实例,则可将这个公共基类说明为虚拟的基类,简称虚基类
    虚基类的说明方法
virtual  继承方式 基类名
class A {};
class B :virtual public A{};
class C :virtual public A{};
class D :public B public C {};

引进虚基类后,派生类D 中只有一个A类,于是避免了二义性,A构造一次

  • 虚基类的派生类的构造函数
为了实现对公共基类只创建一次,则需要在派生类的构造函数的成员初始化表中列出虚基类构造函数一项。派生类构造函数名(。。。):若干个直接基类的构造函数,子对象类的构造函数,虚基类的构造函数
{
    ...
}
  • 规定:在派生类构造函数的成员初始化表中列出的虚基类构造函数,优先于非虚类构造函数的调用。只在创建派生类对象的派生类构造函数中调用虚基类构造函数,而派生类的直接基类的构造函数中不在调用虚基类的构造函数,这就保证了虚基类只构造一次
class A{
    private:
        int a;
    public:
        A (int i)
        {
            a=i;
            cout<<"constructor called A"<<endl;
        }
        void Print()
        {
            cout<<a<<endl;
        }
        ~A()
        {
            cout<<"destructor called A"<<endl;
        }
};
class B1:virtual public A
{
    private:
        int b1;
    public:
        B1(int i,int j):A(i)
    {
        b1=j;
        cout<<b1<<endl;
    }
        ~B1()
        {
            cout<<"destructor called B1"<<endl;
        }
};
class B2:virtual public A
{
    private:
        int b2;
    public:
        B2(int i,int j):A(i)
    {
        b2=j;
        cout<<b2<<endl;
    }
        void Print()
        {
            A::Print();
            cout<<b2<<endl;
        }
        ~B2()
        {
            cout<<"destructor called B2"<<endl;
        }
};
//C中a被构建了两次
class C:public B1,public B2
{
    private:
        int c;
    public:
        C(int i,int j,int k,int l,int m,int n):B1(i,j),B2(k,l),c(m),A(n)
    {
        cout<<"construct called C"<<endl;
    }
        void Print()
        {
            B1::Print();
            B2::Print();
            cout<<c<<endl;
        }
        ~C()
        {
            cout<<"destructor called C"<<endl;
        }
};
int main()
{
    C c(1,2,3,4,5,6);
    c.Print();
    return 0;
}
/*
某公司四类人员,月收入计算如下
总经理月工资固定8500
技术员每小时150计算
销售员销售额的7%提取
销售经理每月固定4000,按部门每月销售额的0.5%提取
面向对象的思想,计算各类人员的月收入*/


#include <iostream>
using namespace std;

class Tmag
{
    private:
        float pay;
    public:
        Tmag()
        {
            pay=8500;
        }
        void Print()
        {
            cout<<"总经理工资:"<<pay<<endl;
        }
};

class Tea
{
    private:
        float pay;
    public:
        Tea()
        {
            pay=0;
        }
        Tea(int i)
        {
            pay=150*i;
        }
        void Print()
        {
            cout<<"技术员工资:"<<pay<<endl;
        }
};

class Sale
{
    private:
        float pay;
    public:
        float value;
        static float values;
        Sale()
        {
            pay=0;
            value=0;
        }
        Sale(int i)
        {
            value=i;
            values+=value;
            pay=value*0.07;
        }
        void Print()
        {
            cout<<"销售员工资:"<<pay<<"  销售额:"<<value<<endl;
        }
};

class Tsale:public Sale
{
    private:
        float pay;
    public:
        Tsale()
        {
            pay=4000+(values)*0.005;
        }
        void Print()
        {
            cout<<"销售经理工资:"<<pay<<endl;
        }
};

int main()
{
    while(1)
    {
    cout<<"1 总经理工资\n"<<"2 技术员工资\n"<<"3 销售员工资\n"<<"4 销售经理工资\n";
    int key;
    cin>>key;
    int i;
    float v;
    Tmag a;
    Tea b;
    Sale c;
    Tsale d;
    switch(key)
    {
        case 1:
            a.Print();
            break;
        case 2:
            cout<<"技术员工作的时间:"<<endl;
            cin>>i;
            b=Tea(i);
            b.Print();
            break;
        case 3:
            cout<<"销售员销售额:"<<endl;
            cin>>v;
            c=Sale(v);
            c.Print();
            break;
        case 4:
            d.Print();
            break;
        case 0:
            return 0;
    }
    }
    return 0;
}

为了方便阅览,标记了好久啊。。。若有帮助请给予支持!

发布了30 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/fzl_blog/article/details/68953959