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

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

     这个学期学习了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{//类的定义方式,关键词为class
private://表示一下的成员为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_;
    }
};




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





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值