C++ 类与封装不完全指北

来源:互联网 发布:湖首大学知乎 编辑:程序博客网 时间:2024/06/06 09:44

    以下内容是个人平时常用的内容,在此进行总结。都是个人的见解,如果有不对或者不赞同的地方,请大家指正,互相学习!

     这个学期学习了C++的有关知识,希望能够在这里与大家一起分享。我不想和其他 教科书一样,一步一步一点一点介绍 C++的知识。这样讲了后面忘了前面。在我的学习过程中,我更喜欢那例子说话。所以我通过几个题目和例子来为大家讲解C++类与封装的基本知识。

     有关C++的背景啥的我就不在此一一介绍。高手们都说C++不是很好用,因为他的输出具有不确定性等等的原因。但是作为小白倒是觉得C++很多时候用起来比C语言要顺手的多。也许这就是和高手的差距吧。C++是一门面向对象的程序语言,是通过不同的类以及类与类之间的关系来实现不同对象的要求 和特征。因此类是C++中最基础的组成部分。

     一.首先来看一下C++中的类与C语言中的结构体

     C++中的类与C语言中的结构体十分相似。但是也有不同点。当然,定义时C++是class而C语言是stract,这是最直观的不同。其次,在C语言的结构体中,数据成员和成员函数的默认属性为public,也就是公开,任何结构体外的函数都可以访问到结构体中的成员。而在C++的类中,如果不特别说明,类中的数据成员和成员函数均为protected,也就是处于保护,只有public中的成员函数可以访问并修改protected中的数据成员,其他的函数均不能访问。这样增加了数据成员的安全性。这是二者最显著的区别。


  二.通过例子来认识一个简单的类

class Data{//类的定义方式,关键词为classprivate://表示一下的成员为protected类型    int x_;public://表示一下的成员为public类型    Data(int x)//构造函数①    {        x_ = x;        cout<<"Data "<<x_<<" is created."<<endl;    }    Data()    {        x_ = 0;        cout<<"Data's default constructor."<<endl;    }    ~Data()//析构函数②    {        cout<<"Data "<<x_<<" is erased."<<endl;    }
    void setValue(int a)//如果函数不需要return的话就写为void类型,需要有return的话,需要返回什么类型的数据就讲函数定义为什么类型    {        x_ = a;    }    int getValue()    {        return x_;    }    void showValue()//用来输出类的数据成员    {        cout<<x_<<endl;    }};


  ①构造函数:构造函数是在创建新的类时调用,可以用来初始化类中的数据成员。


                           构造函数没有函数类型,因此定义的时候前面不加void,int等等,而且函数名与类名一直。

                          构造函数是可以重载的,可以满足多种情况下的定义方式。比如:

class Data{protected:    int a;    int b;public:    Data() { a = 0; b = 0; }//当创建类时没有传入参数,调用该构造函数    Data(int aa) { a = aa; b = 0; }//当创建类时传入一个参数,调用该构造函数    Data(int aa,int bb) { a = aa; b = bb; }//当创建类时传入两个参数,调用该函数
};


在main函数中不同的Data定义方式会调用不同的构造函数。比如:


    Data data1;//调用Data()    Data data2(2);//调用Data(int aa)
    Data data3(10,11);//调用Data(int aa,int bb)


                         构造函数还可以用参数表的方式来写,我们将上面的Data类中的构造函数改写为初始化列表的形式


class Data{protected:    int a;    int b;public:    Data():a(0),b(0) {  }    Data(int aa):a(aa),b(0) {  }    Data(int aa,int bb):a(aa),b(bb) {  }};


                      当一个类没有编写构造函数时,编译器在编译的过程中会自动生成构造函数,这个自动生成的构造函数会把所有的数据成员初始化为0.


②析构函数:析构函数是在一个类生命周期结束后,删除这个类时调用的。

                        构造函数前面不带void,int等任何函数类型,函数名为波浪线+类名,比如Data类的析构函数的函数名为~Data。

                        一般在类中new出来的空间,需要在析构函数中delete掉。(关于new和delete,请参照其他博文)

                       如果没有在类中编写析构函数,编译器会自动生成析构函数。

       在这里总结一下析构函数的调用时机:1.默认构造函数引用时。 2.返回值类的对象时。  3.用类的对象初始化另一个对象时。   4.函数里的参数是类的对象时。
        一个简单的类就是有构造函数,析构函数和其他的函数比如show函数构成的。这样就可以构成一个能实现简单功能的类。但是这样的类并不能实现所有的问题,真正实际应用的类要比这个麻烦的多~~~

    



 三.通过例子来看复杂一些的类

class Time{private:  int hour_,minute_,second_;  friend class DateTime;public:    Time(int h,int m,int s):hour_(h),minute_(m),second_(s) { cout<<"CREATE Time : ("<<hour_<<", "<<minute_<<", "<<second_<<")"<<endl;}//构造函数    Time():hour_ (0),minute_(0),second_(0) {cout<<"CREATE Time : ("<<hour_<<", "<<minute_<<", "<<second_<<")"<<endl; }//无参构造函数    Time(const Time&tt)//拷贝函数①  const常量②    {        hour_ = tt.hour_;        minute_ = tt.minute_;        second_ = tt.second_;        cout<<"COPY   Time : ("<<hour_<<", "<<minute_<<", "<<second_<<")"<<endl;    }    Time& setTime(int hour,int minut,int second)//this指针与引用符&不在本节讲    {        hour_ = hour;        minute_ = minut;        second_ = second;        return *this;    }    void showTime()    {        cout<<setw(2)<<setfill('0')<<hour_<<":"<<setw(2)<<minute_<<":"<<setw(2)<<second_;    }};


①拷贝函数:拷贝函数负责将传入的类中的数据拷贝至现有的类中。

                        拷贝函数的函数名和类名一致。比如Data类的拷贝函数,函数名为Data。

                        拷贝函数不能用初始化列表的形式来写。

                       需要注意,拷贝函数以成员按位复制(bit-by-bit)的方式实现成员的复制,按位复制就是把一个对象个数据成员的值原样复制到目标对象中。在没有涉及指针类型的数据成员时,默认复制构造函数能够很好的工作。但当一个类有指针类型的数据成员时,默认拷贝构造函数会造成指针悬挂问题。所以如果类具有指针类型的数据成员,就该为他提供拷贝构造函数。而不是用默认的拷贝构造函数。

②const常量:详情请看本博客文章《C++中const常量用法总结》

                        

三.通过例子来理解静态成员

using namespace std;class Student{private:    int num;    int id;    int *a;    static int s;//静态成员    string name;public:    Student(string name_,int *a_,int num_):name(name_),num(num_)    {        s++;        id = s;        a = new int[num];        for(int i = 0;i < num;i++)        {            a[i] = a_[i];        }        cout<<"A student whose name is \""<<name<<"\" and id is "<<id<<" is created!"<<endl;    }    ~Student()    {        cout<<"A student whose name is \""<<name<<"\" and id is "<<id<<" is erased!"<<endl;        delete[]a;//new出来的变量一定要在析构函数中delete掉,否则会造成内存泄漏。    }    void showStudent()    {        cout<<"This student is \""<<name<<"\" whose id is "<<id<<"."<<endl;;        cout<<"This student's scores are:";        for(int i=0;i<num;i++)        {            cout<<" "<<a[i];        }        cout<<endl;    }};int Student::s = 0;//静态成员的初始化

   静态成员。顾名思义,就是不变的静态量。其关键词为static,静态常量定义方式为 static type a ,type为数据类型,a为变量名,在函数前加上static表示将函数定义为静态成员。静态常量只能通过静态函数来读取。并且要在所在类的类外,紧跟着进行初始化。初始化为0或者其他的数。静态成员同样遵循public,private和protected访问限定的限定规则。

   静态成员函数是属于整个类的,它只能访问属于该类的静态成员(包括静态数据成员和静态成员函数),不能访问非静态成员(包括非静态数据成员和成员函数)。

   静态数据成员时属于整个类的,整个类只有一份拷贝,相当于类的全局变量,供该类所有对象共用,能够被该类的所有对象访问;非静态数据成员的是属于对象的,每个对象都有非静态数据成员的一份拷贝。为该对象所用。

   下面通过一个例子来理解静态数据成员的特点:

#include <iostream>using namespace std;class Data{private:    static int sum;    int a;public:    Data(int aa):a(aa) { sum++; }    static  int getsum() { return sum; }};int Data::sum = 0;int main(){    Data data1(10);    Data data2(11);}


   在刚刚的例子中,data1中的数据成员a的值为10,data2中的数据成员a的值为11,但是data1和data2中的sum都为2。这就是静态数据成员最大的特点。根据这个特点,静态数据成员一般可以用来计算某个类同时存在的个数。只需要在调用构造函数时+1,在调用析构函数时-1即可~

四.通过例子理解this指针

       首先了解一下this指针的概念。this指针是用于标识一个对象自引用的阴式指针,代表对象自身的地址。由于this指针是在不知晓的情况下,由编译器添加到成员函数参数表中的隐含参数。所以它也称为隐式指针。说明:1.尽管this指针是一个隐式指针,但在类的成员函数中可以显式地使用它 。   2.在类X的非const成员函数里,this的类型就是X*。然而this并不是一个常规变量,不能给他赋值,但可以通过他修改数据成员的值。在类X的const成员函数里,this被设置成const X*类型,不能通过它修改对象的数据成员值。   3.静态成员函数没有this指针,因此静态成员函数中不可以访问对象的非静态成员函数。

        通过this返回对象地址或自引用的成员函数,通过例子来说明。

class Date{private:    int year_,month_,day_;    friend class DateTime;public:    Date(int y,int m,int d):year_(y),month_(m),day_(d) { cout<<"CREATE Date : ("<<year_<<", " <<month_<<", "<<day_<<")"<<endl;}    Date():year_(1),month_(1),day_(1) { cout<<"CREATE Date : ("<<year_<<", " <<month_<<", "<<day_<<")"<<endl; }    Date& setDate(int year,int month,int day)    {        year_ = year;        month_ = month;        day_ = day;        return *this;//this指针的调用,返回的是传入的Data类,不会创建新的Data类    }    Date(const Date& dd)    {        year_ = dd.year_;        month_ = dd.month_;        day_ = dd.day_;        cout<<"COPY   Date : ("<<year_<<", "<<month_<<", "<<day_<<")"<<endl;    }    void showDate()    {        cout<<setw(4)<<setfill('0')<<year_<<"-"<<setw(2)<<setfill('0')<<month_<<"-"<<setw(2)<<day_;    }};




以上就是对于类与封装方面的总结,希望能够给大家提供帮助。也欢迎大家积极留言,相互学习~~