• C++——类与对象


    1、抽象

      是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述的过程。

      1.1 先注意问题的本质描述,其次是实现过程和细节;

      1.2 数据抽象:描述某类对象的属性或状态(对象相互区别的物理量);

      1.3 代码抽象:描述某类对象共有的行为特征和具体功能;

      1.4 抽象的实现:类的声明。

    例如:

      钟表抽象:

           数据抽象 int hour; int minute; int second;

                     代码抽象 settime(); showtime();

                     抽象实现

     class clock{//钟表类
    
    public://在外界可以访问的,相当于钟表外面可以操作的部分,即接口
      void settime(int newh, int newm, int news);
      void showtime();//成员函数
    private://在类外不能直接操作的,封装的部分,相当于钟表的内部
      int hour, int minute, int second;//成员数据};

    2、封装:

      将抽象出来的数据成员、代码成员相结合,将它们视为一个整体。

      2.1 目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限来使用类的成员。

      2.2 实现封装:类声明中的{}。

    3、继承与派生:

      允许程序员在保持原有类的特性的基础上进行更具体的说明,通过声明派生类来实现。

    4、类的多态性:

      同一名称不同功能,达到行为标识的统一,减少程序中标识符的个数,通过重载函数和虚函数来实现。

    5、类的声明:

    class 类名称{
    
    public: 公有成员(外部接口),任何外部函数都可以访问公有类型的数据和函数;
    
    private:私有成员,只允许本类中的函数访问;
    
    protected:保护性成员,与私有成员类似,差别在于继承与派生时对派生类的影响不同。}
    
    #include<iostream> 
    using namespace std;
    class Clock //时钟类的声明                                
    
    {
    public: //外部接口
    
    //公有成员函数,在类中声明原型,可以在类外给出实现,并在函数名前使用类名加以限制,也可在类内给出,形成内联成员函数,
    //允许声明重载函数和带默认形参值的函数
    
     void SetTime(int NewH=0, int NewM=0, int NewS=0);
     void ShowTime();
    private: //私有数据成员,成员数据与一般变量声明相同,但需要放在类的声明体中
     int Hour,Minute,Second;
    };
    //时钟类成员函数的具体实现
    void Clock::SetTime(int NewH, int NewM, int NewS)
    {
     Hour=NewH;
     Minute=NewM;
     Second=NewS;
    }
    
    inline void Clock::ShowTime()
    {
     cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
    }
    //主函数
    int main()
    {
     Clock myClock; //定义类对象即类类型的变量:myClock,
     cout<<"First time set and output:"<<endl;
     myClock.SetTime(); //设置时间为默认值,在类外访问public成员使用 对象员.成员名
     myClock.ShowTime(); //显示时间
     cout<<"Second time set and output:"<<endl;
     myClock.SetTime(8,30,30); //设置时间为8:30:30
     myClock.ShowTime(); //显示时间
    }
    

      

    6、构造函数:

      构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态。在对象创建时由系统自动调用。如果在程序中没有声明构造函数,则系统自动产生一个默认形式的构造函数,构造函数可以使内联函数、重载函数和带默认形参值的函数,构造函数名与类名一致,没有返回值且不用void。

    思考:

      当我们声明整型数据时,int i=0;我们给i赋初值0。同样我们声明类的对象的时候也应该可以赋给初值,构造函数就是类对象赋初值的规则。

    class Clock
    {
    public:
     Clock(int NewH,int NewM,int NewS);//构造函数
     void SetTime(int NewH,int NewM,int NewS);
     void ShowTime();
    private:
     int Hour,Minute,Second;
    };
    构造函数的实现:
    Clock::Clock(int NewH, int NewM, int NewS)
    {
     Hour= NewH;
     Minute= NewM;
     Second= NewS;
    }

    建立对象时构造函数的作用:

    int main()
    {
      Clock c(0,0,0); //隐含调用构造函数,将初始值作为实参。
      c.ShowTime();
    }

     

    7、拷贝构造函数和析构函数:

    析构函数

    析构函数是一种特殊的成员函数,除具有一般成员函数的特性外,还具有如下特性:

    1)、析构函数的函数名必须与其类名相同,不能指定返回类型,也不能使用void。为了能与构造函数相区别,要在函数名前加~(波浪符);

    2)、析构函数没有参数,一个类中只能拥有一个析构函数,所以析构函数不能重载。定义格式为: ~类名(){ 函数体 } ;

    3)、如果程序员没有定义类的析构函数,系统会自动为类创建一个默认析构函数,形式为: ~类名(){ };

    4)、通常为public类型,系统在撤消对象时,自动调用析构函数。也允许显式调用;

    5)、用delete运算符删除对象时也会自动调用析构函数 。

    当对象要被系统释放时,析构函数被调用,在析构函数中可以做一些善后处理处理的工作,如释放内存。
    拷贝构造函数

    概念:用一个已有的对象来初始化一个被创建的同类对象,是一种特殊的成员函数。

    声明的一般格式: 类名(类名 & 对象名);

    利用拷贝构造函数可以实现同类对象的数据传递, 即对象的“克隆”。(注意:与对象赋值的不同) (相对于拷贝构造函数,前面介绍的构造函数被称为普通构造函数)

    拷贝构造函数的性质 :

    1)、函数名必须与类名相同,并且不指定返回类型;

    2)、只有一个参数,是同类的对象的引用;

    3)、每一个类中都必须有一个拷贝构造函数。如果类中没有声明拷贝构造函数,编译器就会自动生成一个具有上述形式的公有的拷贝构造函数。

    拷贝构造函数被调用的场景:

    1)、声明时赋值 Student s1 = s;

    2)、作为参数传入,实参给形参赋值 func(s);

    3)、作为函数返回值返回,给变量初始化赋值 Student s1 = func(s);

    浅拷贝与深拷贝

    如果在类的定义里没有提供拷贝构造函数,C++会提供一个默认的拷贝构造函数; 默认拷贝构造函数的拷贝方式是各成员逐一复制,这称为是浅拷贝; 在某些情况下,浅拷贝会产生问题,例如:当一个类在它的构造函数动态分配了内存资源,浅拷贝只会复制该资源的地址,而不会为新建的对象重新分配一个资源,因此,会出现多个对象指向同一个堆空间的地址的现象。

    应用举例

      一圆形游泳池如图所示,现在需在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。

    4、类和对象

    #include <iostream>
    using namespace std;
    const float PI = (float)3.14159; //给出p的值
    const float FencePrice = 35.; //栅栏的单价
    const float ConcretePrice = 20.; //过道水泥单价
    class Circle //声明类Circle 及其数据和方法
    {
    public: //外部接口
        Circle(float r); //构造函数
        float Circumference(); //计算圆周长
        float Area(); //计算圆面积
    private: //私有数据成员
        float   radius;
    };
    // 类的实现
    Circle::Circle(float r) { radius=r; } // 构造函数初始化数据成员radius
    float Circle::Circumference() // 计算圆的周长
    {
        return 2 * PI * radius;
    }
    float Circle::Area() // 计算圆的面积
    {
        return PI * radius * radius;
    }
    //主函数实现
    void main ()
    {
     float radius;
     float FenceCost, ConcreteCost;
     cout << "Enter the radius of the pool: ";  // 提示用户输入半径
     cin >> radius;
     Circle Pool(radius);   // 声明Circle 对象
     Circle PoolRim(radius + 3);
        // 计算栅栏造价并输出
        FenceCost = PoolRim.Circumference() * FencePrice;
        cout << "Fencing Cost is ¥" << FenceCost << endl;
        //  计算过道造价并输出
        ConcreteCost = (PoolRim.Area() - Pool.Area())*ConcretePrice;
        cout << "Concrete Cost is ¥" << ConcreteCost << endl;
    }
    运行结果
    Enter the radius of the pool: 10
    Fencing Cost is ¥2858.85
    Concrete Cost is ¥4335.39
    

      

    8、组合类中的成员数据时另一个类的对象,可以在已有抽象的基础上实现更复杂的抽象。

    类的数据成员可以是数据,当然可以是类的对象,这就是类的组合。

    class Point
    { private:
       float x,y; //点的坐标
      public:
       Point(float h,float v); //构造函数
       float GetX(void); //取X坐标
       float GetY(void); //取Y坐标
       void Draw(void); //在(x,y)处画点
    };
    //...函数的实现略
    class Line
    {
      private:
        Point  p1,p2; //线段的两个端点,用点类的对象作为线类的数据成员。
      public:
        Line(Point a,Point b); //构造函数,其初始值是点类的对象。
        Void Draw(void); //画出线段
    };
    //...函数的实现略
    

      

    类组合构造函数不仅需要对本类的基本类型成员数据赋初值,也要对对象成员初始化,声明形式:

    类名::类名(对象成员所需形参,本类成员形参):对象1(参数),对象2(参数),……{本类初始化}

    class Part  //部件类
    {
        public:
            Part();
            Part(int i);
            ~Part();
            void Print();
        private:
            int val;
    };
    class Whole
    {
        public:
           Whole();
           Whole(int i,int j,int k);
           ~Whole();
           void Print();
        private:
           Part one;
           Part two;
           int date;
    };
    Whole::Whole()
    {
      date=0;
    }
    Whole::Whole(int i,int j,int k):
                two(i),one(j),date(k)
    {}
    

      

    类要先声明后使用,但是如果要在声明之前引用一个类(当两个类之间相互引用的时候),就需要事先说明。比如说:

    class B;  //前向引用声明class A
    {  public:
          void f(B b);
    };
    class B
    {  public:
          void g(A a);
    };
    
     class Fred;//前向引用声明 ,仅仅声明B是一个类,并没有声明其任何对象,所以也不能再完整声明B之前使用其对象
     class Barney {
    
     public:
      void method()
      {
        x->yabbaDabbaDo(); //错误:Fred类的对象在定义之前被使用
      }
     private:
      Fred* x;//正确,经过前向引用声明,可以声明Fred类的对象指针
     };
     class Fred {
     public:
       void yabbaDabbaDo();
     private:
       Barney* y;
     };
    

      

     
  • 相关阅读:
    DataGrid通过程序的方式锁定任意指定的行
    DataGrid 风格管理类测试版源码
    C#一个Ini操作类
    关于回收站的疑问
    Google启动视频搜索服务(zz)
    用脚本实现“修复连接”的功能
    FC4之初体验
    常见笔试/面试题目(zz)
    微软新图形工具Acrylic公测(zz)
    纪念访问量突破百万:)
  • 原文地址:https://www.cnblogs.com/lemaden/p/10237894.html
Copyright © 2020-2023  润新知