• C++结构和类


     

     1. 结构和类的区别

    一个结构如下:

    struct Student
    
    {
    
    private:
    
           int num;
    
           char name[20];
    
           char sex;
    
    public:
    
           void display()
    
           {…}
    
    };
    
    Student stud1,stud2;

    可以说结构只是类的一部分,struct声明的结构体类型实际上也是类类型。区别是,用struct声明的结构体类型,默认是public的,而用class定义的类,默认是private的。

     

    2. 成员函数在类之外定义

    void Student::display()

    {…}

     

    3. inline声明

    在类内定义的简单成员函数会被系统默认声明为inline,在类外定义的函数,如果想将指定为内置函数,应当与inline作显式声明,如:

    class Student
    
    {
    
    public:
    
           inline void display();
    
    private:
    
           int num;
    
           char sex;
    
    };
    
    inline void Student::display()
    
    {…}

    4. 对象成员的引用 

    stud1.num = 10101;
    
    stud1.display();
    
    用指针:
    
    class Time
    
    {
    
    public:
    
           int hour;
    
           int minute;
    
    };
    
    Time t, *p;
    
    p=&t;
    
    cout<<p->hour; //也可以用(*p).hour

    5.综合例子

    int main()
    
    {
    
           //带默认参数的函数声明
    
           void set_time(Time&, int hour=0, int minute=0, int sec=0);
    
           void show_time(Time&);
    
     
    
           Time t1;
    
           show_time(t1); //使用默认参数,输出0:0:0
    
     
    
           Time t2;
    
           set_time(t2,8,30,16);
    
           show_time(t2); //输出8:30:16
    
           return 0;
    
    }

    6.构造函数

    用于类对象的初始化,有下面几种形式:

    形式一:在内部定义并初始化(无参数形式)

    class Student
    
    {
    
    public:
    
           Student() //定义无参数的构造函数,并赋初值
    
           {
    
                  num=10101;
    
                  name="Wang xiao";
    
                  sex='f';
    
           }
    
           void display();
    
    private:
    
           int num;
    
           string name;
    
           char sex;
    
    };

    形式二:在内部声明,在外部定义并初始化(无参数形式)

    class Student
    
    {
    
    public:
    
           Student(); //声明无参数的构造函数
    
           void display();
    
    private:
    
           int num;
    
           string name;
    
           char sex;
    
    };
    
    Student::Student() //定义构造函数,并初始化
    
    {
    
           num=10101;
    
           name="Wang xiao";
    
           sex='f';
    
    }

    注:“无参数的构造函数”的特点是:一次性的初始化,以后不能再调用构造函数重新初始化。

     

    形式三:在内部声明,在外部定义,不初始化

    class Student
    
    {
    
    public:
    
           Student(int ,string, char); //声明带3个参数的构造函数
    
           void display();
    
    private:
    
           int num;
    
           string name;
    
           char sex;
    
    };
    
    Student::Student(int n, string s, char c)
    
    {
    
           num=n; 
    
           name=s;
    
           sex=c;
    
    }
    
    然后,在主函数中新建对象并初始化:
    
    Student stud(10101, "Wang xiao", 'f');

    形式四:在外部采用“初始化表”的形式来定义,不初始化

    class 同形式三;
    
    Student::Student(int n, string s, char c):num(n), name(s), sex(c){}

    形式五:形式一和二的带参数和默认值的形式

    类似于形式三和四,只需要在声明时写作:

    Student(int=10102 ,string=”Wang xiao”, char=’f’);

    即可。注:和“无参数的构造函数”相比,有参数的构造函数可以多次调用构造函数来进行多次初始化,而无参数形式只能一次性初始化。

     

    形式六:不同个数参数的构造函数(既构造函数的重载)

    可以说是形式一到五的一般形式:

    class classexample
    
    {
    
    public:
    
           classexample()//构造函数1,无参数,即形式一的形式
    
           {
    
                  i=0;
    
                  d=0.0;
    
           }
    
           classexample(int numi) //构造函数2,只有一个参数,注意:此时另一个必须指定默认值
    
           {
    
                  i=numi; d=0.0; //d指定默认值为0.0
    
           }
    
           classexample(int numi, double numd) //构造函数3,满参数,类似于形式三
    
           {
    
                  i=numi;
    
                  d=numd;
    
           }
    
    private:
    
           int i;
    
           double d;
    
    }; 
    
    void main()
    
    {
    
           //系统会根据参数的个数自动判断调用哪一个构造函数
    
           classexample A; //无参数,调用构造函数1
    
           classexample B(3);  //一个参数,调用构造函数2
    
           classexample C(3, 2.2);  //两个参数,调用构造函数3
    
    }

    7.多文件类和构造函数的例子

    一个三个文件student.h, student.cpp和main.cpp,内容如下:

    #include <string>
    
    using namespace std;
    
    class Student
    
    {
    
    public:
    
           Student(int, string, char);//定义构造函数
    
           void display();
    
    private:
    
           int num;
    
           string name;
    
           char sex;
    
    };
    #include<iostream>
    
    #include"student.h"
    
    void Student::display()
    
    {
    
           cout<<"num:"<<num<<endl;
    
           cout<<"name:"<<name<<endl;
    
           cout<<"sex:"<<sex<<endl;
    
    }
    
    Student::Student(int n, string s, char c):num(n), name(s), sex(c){}
    #include"student.h"
    
    using namespace std;
    
    int main()
    
    {
    
           Student stud(10102, "Wang xiao", 'f');
    
           stud.display();
    
           return 0;
    
    }

    多文件编译时要把3个文件都放入同一个工程文件中(其中.h文件会自动包含进来,但是.cpp文件需手动加入),具体方法:先打开main.cpp文件然后编译它(Compile),先不忙建立连接(Build),打开student.cpp文件编译,然后再连接main.cpp文件,最后执行main.cpp就OK了。该构造函数采用了3个参数的形式。

     

    8.相关延伸

    a. 对象的复制,拷贝(复制)构造函数

    例如:Box box2(box1); //利用box1克隆出一个新对象box2

    实际上这个函数的形式是:

    Box::Box(const Box& b) // 调用构造函数
    
    {
    
           height=b.height;
    
           width=b.width;
    
           length=b.length;
    
    }

    C++还提供了另一种复制形式:

    Box box2=box1;

    系统会根据实参的类型决定调用普通构造函数还是复制构造函数。例如:

    void fun(Box b)
    
    {}
    
    int main()
    
    {Box box1(12,15,19);
    
     fun(box1); //调用fun()
    
    }

    例2:

    Box f()
    
    {Box box1(12,15,18); return box1;}
    
     
    
    int main()
    
    {Box box2;
    
     box2=f(); //调用f()
    
    }

    b. 析构函数

    当对象的生命周期结束后会自动调用析构函数。析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作。它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”,例如输出有关信息。例如:

    class Student
    
    {public:
    
           Student(int n, string nam, char s){…} //定义构造函数
    
           ~Student() //定义析构函数
    
           {cout<<”Destructor called.”<<endl;}
    
           ……
    
    }

    c. 类对象数组

    定义方式:

    Student stud[3]={ //定义一个一维对象数组并调用构造函数初始化
    
           Student(1001,18,87);
    
           Student(1001,18,87);
    
           Student(1001,18,87);
    
    };

    d. 对象指针

    1.指向对象的指针:

    Time *pt;
    
    Time t1;
    
    pt=&t1;
    
    pt->get_time();

    2.指向数据成员的指针:

    int *p1;
    
    p1=&t1.hour;
    
    cout<<*p1;

    3.指向成员函数的指针:

    void (Time::*p2)();
    
    p2=&Time::get_time; //可以合并成一行:void (Time::*p2)()=&Time::get_time;
    
    Time t1;
    
    (t1.*p2)(); //记住这种调用方式!

    4.this指针

    this指针的作用:如果对同一个类定义了n个对象,这有n组同样大小的空间存放n个对象中得数据成员,而不同的对象所调用的都是同一个函数代码段,当不同对象的成员函数引用数据成员时,如何能保证引用的是所指定的对象的数据成员呢,这就是this指针的作用。

    例如调用t1.volume();实际上系统隐含转换成t1.volume(&t1);虽然volume并没有参数,但是为了识别出t1的数据成员,会把t1当做参数传递,内部用this指针指向t1的具体参数。即把函数定义处理为:

    int Box::volume(Box *this)
    
    {
    
           return((this->height)*(this->width)*(this->length))
    
    }

    这样调用t1.volume()时,即调用t1.volume(&t1),把&t1赋值给this指针,即this是指向对象的指针。

     

    e. 常对象,对象的常引用

    1.常对象:

    Time const t1(12,34,46); 或者 const Time t1(12,34,46);
    
    //即在用构造函数初始化时多加一个关键字“const”。

    2.常数据成员:

    const int hour;

    这样定义后hour就是常数据成员了,它不能被赋值,如果要初始化,应该用初始化表的形式,即:

    Time::Time(int h):hour(h){}

    3.常成员函数

    void get_time() const; //把const放在最后

    4.指向对象的常指针

    Time t1(10,12,15);
    
    Time * const p1=&t1; //在*与指针名之间加const,以后指针就不能改变指向了

    也可以分两行写:

    Time * const p1;

    p1=&t1;

    5.指向常对象的指针

    例如:

    const int *p1; //指向常int类型数据的指针,可改变指向,但是指针的值不能被修改
    
    const Time *p=&t1; //注意与上面的Time * const p1=&t1;的区别

    指向常对象的指针最常用于函数的形参,目的是保护所指的对象不被修改,例如:

    int main()
    
    {void fun(const Time *p);
    
     Time t1(10,13,56);
    
     fun(&t1); //此时传过去的值不能被修改。
    
    }

    6.对象的常引用

    一个变量的引用其实就是变量的别名。例:

    #include<iostream>
    
    using namespace std;
    
    class Time
    
    {public:
    
           Time(int, int, int);
    
           int hour;
    
           int minute;
    
           int sec;
    
    };
    
    Time::Time(int h, int m, int s):hour(h), minute(m), sec(s){}
    
     
    
    void fun(Time &t) //如果规定不修改实参t1的值,可以声明为const Time &t,此时赋值语句t.hour=18就是非法
    
    {t.hour=18;}
    
     
    
    int main()
    
    {
    
           Time t1(10,13,56);
    
           fun(t1);
    
           cout<<t1.hour<<endl;
    
           return 0;
    
    }

    f. 对象的动态建立和释放

    例如:

    Box *pt;

    pt=new Box;

    在程序中就可以通过pt访问这个新建的对象了:

    cout<<pt->height;

    C++还允许在执行new时,对新建立的对象进行初始化:

    Box *pt= new Box(12,15,18); //推荐形式

    用delete运算符予以释放:

    delete pt; //在释放内存空间之前,自动调用析构函数完成有关善后清理工作

    其实,之所以称之为“动态”,是因为这样开辟的内存空间是没有名称的,如果不用new运算符,必须写作:

    Box t(12,15,18);

    Box *pt=&t;

    此时的内存空间是对象t,而用new时就没有名字。

    一,静态成员

    1. 静态数据成员

           类的对象数据成员都是各自调用各自的,不能为所有对象所共享。使用全局变量又有到处都可以修改全局变量的值的弊端(静态数据成员的值也是可以被修改的!之所以称为“静态”,是因为修改后不复原)。因此实际工作中很少使用全局变量。如果想在同类的多个对象之间实现数据共享,也不要使用全局变量,可以使用静态的数据成员。例如:

    class Box
    
    {
    
    public:
    
           int volume();
    
    private:
    
           static int height;
    
           int width;
    
           int length;
    
    };

    静态数据成员不只属于某个对象,所有对象都可以引用它,即使不定义对象,也为静态数据成员分配空间,它也可以被引用!(它可以通过对象名引用,也可以直接通过类名来引用),例如:

    cout<<a.height<<endl;  //通过对象a引用
    
    cout<<b.height<<endl;  //通过对象b引用
    
    cout<<Box::height<<endl;  //通过类名引用

    静态数据成员可以初始化,但只能在类外。如:

    int Box::height=10;  //注意,初始化时前面要加类型,比如int

    不能用参数初始化表对它进行初始化。如:

    Box(int h, int w, int len):height(h){} //是错误的

    如果未对静态数据成员赋初值,则编译系统会自动赋初值0。

     

    2. 静态成员函数

    和静态数据成员的定义和调用类似:

    static int volume();

    要注意的是:静态成员函数没有this指针,由此决定了静态成员函数不能直接访问本类中的非静态数据成员(可以用传参数的形式访问),只能直接引用本类中的静态数据成员。例如:

    #include<iostream>
    
    using namespace std;
    
    class Student
    
    {
    
    public:
    
           Student(int n, int a, float s):num(n),age(a),score(s){}
    
           void total();
    
           static float average();
    
    private:
    
           int num;
    
           int age;
    
           float score;
    
           static float sum;
    
           static int count;
    
    };
    
    void Student::total()
    
    {
    
           sum+=score;
    
           count++;
    
    }
    
    float Student::average() //只访问静态数据成员
    
    {
    
           return(sum/count);
    
    }
    
    float Student::sum=0; //初始化静态数据成员
    
    int Student::count=0;
    
     
    
    int main()
    
    {
    
           Student stud[3]={
    
                  Student(10001,18,70),
    
                  Student(1002,19,78),
    
                  Student(1003,20,82)
    
           };
    
     
    
           stud[0].total();
    
           stud[1].total();
    
           stud[2].total();
    
           cout<<"The average score of the students is "<<Student::average()<<endl;
    
           return 0;
    
    }

    如要使静态函数static float average()访问非静态数据成员,例如访问stud[0].score,这应该用函数传参数的形式,例如:

    声明:

    static float average(const Student &);

    定义:

    float Student::average(const Student &stu)

    {

           cout<<stu.score<<endl;

           return(sum/count);

    }

    引用:

    cout<<Student::average(stud[2]);

    二、友元

    friend函数可以是普通非成员函数或者另一个类中的成员函数。

    1.普通函数声明为友元函数

    直接在public中加入一句声明:

    friend void display(Time &);

    在类外定义的友元函数display()内容如下:

    void display(Time &t)

    {cout<<t.hour<<”: ”<<t.minute<<endl;}

    注意,display是一个在类外定义的且未用类Time作限定的函数,它是非成员函数,不属于任何类。用friend声明后就可以应用Time类中的私有成员。

     

    2.另一个类的成员函数声明为友元函数

    #include<iostream>
    
    using namespace std;
    
    class Date;  //对Date类的提前引用声明
    
    class Time
    
    {
    
    public:
    
           Time(int, int, int);
    
           void display(Date &);
    
    private:
    
           int hour;
    
           int minute;
    
           int sec;
    
    };
    
    class Date
    
    {
    
    public:
    
           Date(int, int, int);
    
           friend void Time::display(Date &); //将Time类中的成员函数声明为本类的友元函数
    
    private:
    
           int month;
    
           int day;
    
           int year;
    
    };
    
     
    
    void Time::display(Date &d)
    
    {……}
    
    ……
    
    int main()
    
    {
    
           Time t1(…);
    
           Date d1(…);
    
           t1.display(d1); //t1的成员函数调用对象d1
    
           return 0;
    
    }

    3.友元类

    好比一个家庭不仅允许一个好朋友可以进入他们的家庭,还允许他全家的人都可以进入他们的卧室。声明友元类的方式:

    friend 类名;

    在实际工作中,除非必要,一般不把整个类声明为友元类。

     

    三、类模板

    函数模板是针对于功能相同而数据类型不同的一些函数。同理,类模板也是如此。

    声明类模板:

    template <class 类型参数名>

  • 相关阅读:
    Delphi2007下cxComboBox乱码.
    DelphiIOCP 学习笔记<六>=====IO内存池和扩展套接字(ClientContext)
    DelphiIOCP学习笔记<三>====工作线程和Listener
    DelphiIOCP学习笔记<二>====IOCP基本函数介绍和理解
    word比较两个文件
    SQLite3 简记
    DXP_protel2004_原理图设计基础_新建和添加原理图库文件_元件编辑范例
    DXP_protel2004_原理图设计基础_新建和添加原理图库文件
    网页表格颜色搭配
    excle密码破解
  • 原文地址:https://www.cnblogs.com/zollty/p/2879301.html
Copyright © 2020-2023  润新知