c++ 吕凤翥 第五章 类对象一

一   类的声明和实现

 1. class tdate   //声明部分

  {

    public:

      void setdate(int y,int m,int d);

      int isleapyear();

      void print();

    private:

      int year,month,day;

  }

 void tdate::setdate(int y,int m,int d)//外联函数

  {

    year=y;

    month=m;

    day=d;

  }

 int tdate::isleapyear()

  {

    return(year%4==0&&y ear%100!=0)||(year%400==0);

  }

 void tdate::print()

  {

    cout<<year<<"."<<month<<"."<<day<<endl;

  }

  ::为作用域运算符    标识某个成员函数是属于哪个类的

  

声明方式二

  class tdate

  {

    public:

      void setdate(int y,int m,int d)  //内联函数   适合函数体比较小

      {

      year=y;

      month=m;

      day=d;

      }

    int isleapyear(){....}  

    private:

      int year,month,day;

  }//内联函数的声明方法    将成员函数的实现 都写在类体内  ;如果函数的实现定义在类体外,则需要在函数头前面加上该函数所属的类的标识,使用作用域运算符 ::

2:  注意事项

  1.类体中不允许对所定义的数据成员进行初始化     需要使用构造函数

  2.类中的数据成员的类型可以是任意的,其它类的对象可以作为该类的成员,但是自身类的对象不可以,但是自身类的指针或者引用可以。

    另一个类的对象作为这个类的成员时,如果另一个类的定义在后,则需要提前说明,这种说明成为引用性说明。

  class N;

  class M

  {

    public:  ...

    private:

      N n;//n是N类的对象       N类在前面说明

  }

  class N

  {

    public:

      void f(M m);  //m是M类的对象    M类已在前面说明

      ...

  };

  M类中使用了N类的对象n,而N类的定义又在后面,所以,对N类要提前说明

  3.先声明public  公有成员,后声明私有private 私有成员   一般按照数据成员的类型大小   由小到大来说明   

  4.将类定义的说明部分或整个实现部分放到一个头文件中 ;也可以将声明放在一个头文件,实现放在另一个文件中

 

二.对象的定义

  类名  对象名

  tdate t1,t2,t3,* pdate,date[31],&rdate=t1;  //其中多个对象用逗号隔开    pdate为指向tdate对象的指针,date为一个对象数组, rdate 为一个对象的引用。

  对象成员的表示方法

  对象名.成员名

  对象的指针名->成员名 

  对象名.成员名(参数表)

  对象的指针名->成员名(参数表)

   (* 指针).成员名

  同一个类所创建的对象的数据结构是相同的,类中的成员函数是共享的。

三.对象的初始化

  1.构造函数和析构函数

  都是特殊的成员函数:构造函数初始化正在创建的对象;析构函数的功能是用来释放一个对象

  class tdate//类声明

  {

    public:

      tdate(int y,int m,int d);  //构造函数  非默认

      ~tdate();      //析构函数   非默认

    void print();      //普通的成员函数

    private:        //数据成员变量

      int year,month,day;

  }

  tdate::tdate(int y,int m,int d)  //构造函数的实现

  {

    year=y;

    month=m;

    day=d;

    cout<<"constructor called. ";

  }

  tdate::~tdate()        //析构函数的实现

  {

    cout<<"destructor called. ";

  }

  void tdate::print()        //普通成员函数的实现

  {

    cout<<year<<"."<<month<<"."<<day<<endl;

  }

  构造函数特点:

    a  构造函数可以写在类体内(内联),也可以写在类体外  (外联)

    b  构造函数为特殊的成员函数  名称同类名,不指定类型,有隐含的返回值,此值由系统内部使用。该函数可以没有参数,也可以有多个参数

    c  构造函数可以重载,根据参数个数不同

    d  程序不直接调用构造函数,创建对象时,系统自动调用构造函数

  析构函数特点:

    a  同构造函数

    b  类名前加~    用来与构造函数加以区别  不指定数据类型,无参数

    c  只能有一个析构函数     这一点不同于构造函数

    d  通常系统自动调用  下面的两种情况

        1.函数体内定义的对象   当函数结束时,系统将调用析构函数释放此对象

        2.一个对象使用new运算符被动态创建时,则使用delete运算符(需要手动)释放,delete将自动调用析构函数

  源代码如下:

    #include <iostream.h>

    #include "tdate.h"

    void main()

    {

      tdate today(1998,4,9),tomorrow(1998,4,10);

      cout<<"today is";

      today.print();

      cout<<"tomorrow is";

      tomorrow.print();

    } 

    执行结果如下:

    constructor called.

    constructor called.

    today is 1998.4.9

    tomorrow is 1998.4.10

    destructor called. 

    destructor called.

2.默认构造函数和默认析构函数

  默认构造函数: 无参的构造函数  称为默认构造函数

      a  系统自动提供的       程序员未定义      则系统自动生成

      b  程序员定义的  

         c  系统默认构造函数 创建对象,外部类对象和静态类对象的所有数据成员默认值,自动类对象的所有数据成员为无意义值。

  默认的析构函数:系统提供  的一个空函数

      程序员定义的析构函数可以不是一个空函数,可以给出适当的函数体。

3.拷贝/复制构造函数 

  用一个已知对象来创造一个新的对象,而新创建的对象与已知对象的数据成员的值可以相同,也可以不同。

  仅有一个参数,就是已知对象的引用

  类名::复制构造函数名(类名 & 引用名)

  {

    函数体

  }

   如果程序员未定义,则系统提供默认的复制构造函数   该类的公有成员

  案例

  class tpoint

  {

    public:

      tpoint(int x,int y){X=x;Y=y;}

      tpoint(tpoint & p);

    private:

      int X,Y;

  }

  tpoint::tpoint(tpoint & p)

  {

    X=p.x;

    Y=p.y;

    cout<<"adsfalsdf. ";

  }

 调用:

   #include <iostream.h>

   #include "tpoint.h"

   void main()

   {

    tpoint p1(5,7);

    tpoint p2(p1);

    cout<<"asdfasdf"<<endl;

   }

需要调用复制构造函数来由一个对象初始化另一个对象

 a  明确表示一个对象初始化另一个对象

    tpoint p2(p1);

 b  当对象作为函数实参传递给函数形参时

    p=f(N);

 c  当一个自动存储类对象作为函数返回值时

    return R;

  

  案例代码:

  ...

  tpoint f(tpoint q);  //prototype

  void main()

  {

    tpoint m(20,35),p(0,0);   //构造函数初始化

    tpoint n(m);//复制构造函数

    p=f(n);//n对象作为参数传递给函数f

    cout<<"adsf"<<"adsfasdf"<<endl;

  }

  tpoint  f(tpoint q)

  {

    ...

    tpoint r(x,y);//调用构造函数初始化对象

    return r;//返回一个tpoint类的对象

  }

   代码中标红的位置是复制构造函数被调用的位置。

  1.tpoint n(m)         n对象被复制构造函数初始化

  2.f函数   实参n传递给形参q时     会调用复制构造函数

  3.f函数定义的最后    return r     函数返回的对象由于是局部变量  类型为自动存储类变量  会复制到一个无名的此类的对象,然后再返回,给原函数,所以此处会调用复制构造函数

  析构函数     根据构造函数来分析   共调用6次析构函数

  1.退出f函数时,调用2次析构函数,用来释放对象r和q  一个是形参对象  另一个是返回的r对象

  2.主函数中,将f函数的值赋值给对象p后,无名对象(被r赋值的对象)被析构,退出主函数时,调用3此析构函数  分别释放n p和m

4.成员函数的特性

  a  内联函数和外联函数

    内联为定义在类体内的成员函数       执行时不转移ip执行,而是在调用内联函数处,用内联函数来替换   以节省开销,提高速度 ,类似于宏

    外联为说明在类体内,但实现在类体外的成员函数

  b  外联变内联    在外联函数前加上inline

  案例:

    class A

    {

      public:

        A(int x,int y){X=x;Y=y;}      //内联函数

        int a(){return X;}          //内联函数

        int b(){return Y;}        //内联函数

        int c();

        int d();

      private:

        int X,Y;

    };

    inline int A::c()          //内联函数

    {

      return a()+b();

    }

    inline int A::d()          //内联函数

    {

      return c();

    }

    #include <iostream.h>

    void main()

    {

      A m(3,5);      //构造函数调用

      int i=m.d();    //对象的成员函数d()再调用c()  再调用a和b

      cout<<"d() return :"<<i<<endl;

    }

   

重载性

   一般的成员函数,特殊的构造函数可重载,但是析构函数是不能重载的   注意重载函数中的参数个数不同

  案例:

    class m

    {

      public:

        m(int x,int y){X=x;Y=y;}      //构造函数 1

        m(int x){X=x;Y=x*x;}        //构造函数2  重载

        

        int add(int x,int y);        //成员函数1

        int add(int x);          //成员函数2

        int add();            //成员函数3    重载

        int xout(){return X;}        

        int yout(){return Y;}

      private:

        int X,Y;

    };

    int m::add(int x,int y)

    {

      X=x;

      Y=y;

      return X+Y;

    }

    int m::add(int x)

    {

      X=Y=x;

      return X+Y;

    }

    ....

 4.3设置参数的默认值

   成员函数和构造函数都可以设置参数的默认值

  案例:

  class n

  {

    public:

      n(int a=3,int b=5,int c=7);

      int Aout(){return A;}

      int Bout(){return B;}

      int Cout(){return C;}

    private:

      int A,B,C;

  };

  n::n(int a,int b,int c)  //构造函数  三个参数

  {

    A=a;

    B=b;

    C=c;

  }

  #include <iostream.h>

  void main()

  {

    n x,y(9,11),z(13,15,17)

    cout<<"x="<<x.Aout()<<","<<x.Bout()<<","<<x.Cout()<<endl;

    cout<<"y="<<y.Aout()<<","<<y.Bout()<<","<<y.Cout()<<endl;

    cout<<"z="<<z.Aout()<<","<<z.Bout()<<","<<z.Cout()<<endl;

  }

  程序执行输出如下:

  X=3,5,7

  Y=9,11,7

  z=13,15,17

  未提供实参的使用原类体内的给出的默认值,给实参的使用实参值

五 静态成员

  全局变量或对象和局部的静态变量或对象能解决数据共享问题

  案例1:

  #include <iostream.h>

  int g=5;        //全局

  void f1(),f2();      //函数原型

  void main()

  {

    g=10;      // 此时g是全局变量

    f1();       //函数调用 

    f2();        //函数调用

    cout<<g<<endl;    //输出变量g

  }

  void f1()

  {

    g=15;    //修改了全局变量

  }

  void f2()

  {

    g=20;    //修改了全局变量

  }

  输出结果为20,函数间共享变量g,   全局变量或对象在任何位置都可以被修改。  不安全

 1.静态成员包含静态数据成员和静态成员函数。它们都属于类,也属于类的所有对象。

 2.静态成员可以用对象引用,也可以用类来引用

 3.静态数据成员和静态成员函数在没有对象时就已经存在,并可以用类来引用。

 A  静态数据成员

    实现多个对象之间的数据共享,并使用静态数据成员还不会破坏隐藏的原则,保证安全性。

    静态数据成员是类的所有对象共享的成员,而不是某个对象的成员。

    只存储一处,供所有对象公用。可以由共享它的任一个对象修改更新。

    1.静态数据成员在定义或说明时前面加上关键字static

    private:

      int a,b,c;

      static int s;

    其中a,b和c是非静态数据成员,而s是静态数据成员

    2.静态数据成员的初始化与一般数据成员不同

    在类体外进行

    数据类型  类名::静态数据成员名=初值

      a  初始化在类体外进行,前面不加static,以免与一般静态变量或对象相混淆

      b  作用域运算符来标明它所属的类,静态数据成员属于类不属于某个对象

    3.静态数据成员被存放在静态存储区

     静态数据成员是被放在静态存储区的,必须进行初始化。

     案例:

    class nclass

    {

      ...

      private:

        static int a;      //静态成员变量  声明

      ...

    };

    int nclass::a=5;        //必须初始化

      ...

  4.引用静态数据成员的格式

    类名::静态成员名

   案例:

   class myclass

  {

    public:

      myclass(int a,int b,int c);    //构造函数

      ..

    private:

      int A,B,C;

      static int Sum;    //静态成员变量声明

  };

  int myclass::sum=0;      //初始化

   myclass::myclass(int a,int b,int c)  //构造函数的实现

  {

    A=a;

    B=b;

    C=c;

    sum+=A+B+C;

  }

   

  void main()

  {

    myclass m(3,7,10),n(14,9,11);

    cout<<m.A<<m.B<<m.C<<endl;

    cout<<n.A<<n.B<<n.C<<endl;

    cout<<m.sum<<endl;

    cout<<n.sum<<endl;

  }

  执行结果为 

    3,7,10

    14,9,11

    54

    54

 将m初始化时  三个数的和赋值给了sum,值为20,之后n初始化又为34   sum的总值为54;可见静态数据成员是多个对象共享的。

 B  静态成员函数

  同静态成员变量一样,函数也是属于类,而不是具体的对象,所以使用时可以用类名来引用静态成员函数。

    静态成员函数实现的函数体中可以直接引用类中说明的静态成员,而不可以直接引用类中说明的非静态成员

    静态成员函数中要引用非静态成员时,可通过对象来引用

    

#include <iostream>
#include <math.h>

using namespace std;

class point
{
public:
point(double _x,double _y)
{
x=_x;y=_y;
}
void getxy();
friend double distance(point &a,point &b); //友元函数的声明
private:
double x,y;
};
void point::getxy()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
double distance(point &a,point &b) //友元函数的实现  注意参数和声明的方式
{
double dx=a.y-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}

int main()
{
point p1(3.0,4.0),p2(6.0,8.0);
p1.getxy();
p2.getxy();
double d=distance(p1,p2);
cout<<"distance is"<<d<<endl;
}

 执行结果为:

  (3.0,4.0)       (6.0,8.0)     distance is 5

 案例2:

#include <iostream>
#include <math.h>

using namespace std;       //命名空间

class time      //类 声明
{
public:
time(int _h,int _m)    //内联函数
{
h=_h;m=_m;
}
friend void time12(time t);  //友元
friend void time24(time t);
private:
int h,m;
};

void time12(time t)  //友元实现
{
if(t.h>12)
{
t.h-=12;
cout<<t.h<<":"<<t.m<<"PM"<<endl;
}
else
cout<<t.h<<":"<<t.m<<"AM"<<endl;
}
void time24(time t)  //友元实现
{
cout<<t.h<<":"<<t.m<<endl;
}

int main()    //主函数
{
time t1(20,30),t2(10,45);      //调用构造  初始化对象
time12(t1);    //友元调用
time24(t1);
time12(t2);
time24(t2);

return 0;
}

执行结果:

  8:30 PM

  20:30

  10:45AM  

  10:45

如果形参改为引用  则函数内部的实现 修改的是实参对象的值

  8:30 PM

  8:30

  10:45AM  

  10:45

  

友元类

  一个类作为另一个类的友元,这个类的所有成员函数都是另一个类的友元函数,

   class x

  {

    friend class y;

    public:...

    private:

      int a;

      ...

    }

  class y

  {

    public:

      void display();    

    private:

    ...

  }

  y类作为x类的友元类     y中的成员函数display 是x类的友元函数  可以访问x的私有成员变量

  案例:

  

#include <iostream>
#include <math.h>

using namespace std;

class X
{
  friend class Y;    //声明友元类
  public:
  void set(int i)
  {
    x=i;
  }
void display()
  {
    cout<<"x="<<x<<",";
    cout<<"y="<<y<<endl;
  }
private:
  int x;      //成员变量
  static int y;    //静态成员变量
};


class Y                   //类声明
{
  public:
    Y(int i,int j);    //构造函数
    void display();
  private:
    X a;      //私有成员变量为另一个类的对象
};


int X::y=1;          //静态成员变量的初始化


Y::Y(int i,int j)    //Y类中的Y构造函数

{
  a.x=i;     //x类对象a的x成员变量设置为i       此时x成员变量为非静态成员
  X::y=j;      //X类中的私有变量设置为j 因为此为构造函数,也是成员函数   即构造函数也是X类的友元函数,可以访问私有的成员变量X::y
}
void Y::display()  //Y类中的display函数的实现
{
  cout<<"X="<<a.x<<",";        //       访问X类对象a的x成员变量    对象名调用普通成员变量
  cout<<"y="<<X::y<<endl;    //  访问X类y成员变量      类名调用静态成员变量   共享
}

int main()
{
  X b;      //b对象
  b.set(5);    //构造初始化     
  b.display();   
  Y c(6,9);    //Y类的c对象 
  c.display();    
  b.display();
  return 0;
}

   x=5;//b.display()

   y=1;

   x=6;//c.display()

   y=9;

   x=5;//b.display()

   y=9;

   

注意:友元的关系不可逆     y类是x类的友元类,不能说x类是y类的友元类

5.7类的作用域

   类的作用域  简称类域,类的定义中由一对花括号括起来的部分。

   类的成员 位于类域中。

   类域中的变量不能使用auto   register  和 extern等修饰符,只能使用static修饰符;函数也不能用extern修饰符

   类域中的静态成员变量和静态成员函数都具有外部的连接属性

   类域小于文件域     大于函数域

  类A的某个成员m在下列情况下具有类A的作用域

  a  m出现在a的某个成员函数中,且成员函数未定义同名标识符

  b  a.m  表达式

  c  pa->m   表达式     pa为指向a对象的指针

  d  A::m

5.8局部类和嵌套类

  函数体内定义的类称为局部类     局部类中只能使用它的外围作用域中的对象和函数进行联系,    

  局部类中不能说明静态成员函数,所有成员函数必须定义在类体内。   不常用

  int a;

  void fun()    //函数

  {

    static int s;

    class A   //类

    {

      public:

        void init(int i){s=i;}

    };

    A m;

    m.init(10);    //A类的对象m与函数fun联系

  }

  嵌套类

    一个类中定义的类称为嵌套类    定义嵌套类的类称为外围类。

    主要目的是为了隐藏类名,减少全局的标识符。提高类的抽象能力,建立类间的主从关系

    class A    //外围类

    {

      public:

        class B    //嵌套类

        {

          public:

            void bf(){...};    //成员函数

          private:

        };

        void f();

      private:

        int a;

    }

    说明:

    a  B被隐藏在A中       B只能在A中使用,或者使用作用域运算符  A::B

    b  B的访问权限与f()函数是相同的都是public

    c  bf()函数只能在B中定义    不能在B之外定义

    d  B类中的成员不是A类的成员,反之一样;B的成员函数对A的成员变量没有访问权,反之一样。

    综上:嵌套类可以看作普通的非嵌套类来处理。    嵌套类仅仅是语法上的嵌入   不影响功能   可以如下写法:

    class A

    {

      ...

    }

    class B

    {

      ...

    }

5.9对象的生存期

  不同的存储对象生存期不同

  对象从被创建开始到被释放为止的存在时间

  a  局部对象:对象被构造函数初始化创建时开始    到函数或者函数体结束   调用析构函数  释放该对象        函数或者程序块内

  b  静态对象:程序第一次执行所定义的静态对象时,到程序结束,对象被释放                文件中

  c  全局对象:程序开始时,创建该对象      程序结束时释放对象                     定义在文件中,包含在该文件的整个程序

   案例

  

#include <iostream>
#include <string.h>
using namespace std;
class A //类声明 实现
{
public:
A(char * st);
~A();
private:
char string[50];
};
A::A(char * st)
{
strcpy(string,st);
cout<<"constructor called for"<<string<<endl;
}
A::~A()
{
cout<<"Destructor called for"<<string<<endl;
}

void fun()
{
A FunObject("FunObject"); //局部对象
static A staticObject("staticobject"); //静态对象
cout<<"in fun()."<<endl;
}
A globalobject("globalobject"); //全局对象
int main()
{
A mainobject("mainobject"); //局部对象
cout<<"in main(),before called fun ";
fun(); //函数调用
cout<<"in main(),after called fun ";
return 0;
}

练习:

一:

1.抽象类  模拟

2.class A

  {

    public:

    private:

    protected:

  }

3.4.

5.     ::   作用域运算符     标明变量的归属

选择:

1.a     2.c    3.d    4.d  5.d    6.a    7.c    8.a    9.d    10.

 二 

claas  成员缺省访问权限为私有

::   运算符用来限定成员函数所属的类

析构函数可以不为空 但是只能有一个

构造函数可以重载   析构不可重载

说明和定义对象时,前面加上类名即可,不用加上class关键字

三程序输出

1.default  constructor   called

constructor called

a=0   b=0

a=4,b=8

2. 

a=7,b=9

 #include <iostream>

using namespace std;
class B
{
public:
B();
B(int i,int j);
void printb();
private:
int a,b;
};

class A
{
public:
A();
A(int i,int j);
void printa();
private:
B c;
};

 A::A(int i,int j):c(i,j){};//¶ÔÏóAµÄ¹¹Ô캯Êý

void A::printa()
{
c.printb();
}

B::B(int i,int j)
{
a=i;
b=j;
}

void B::printb()
{
cout<<"a="<<a<<";b="<<b<<endl;
}

int main()
{
A m(7,9);//设置断点      单步进入可以查看程序执行语句的顺序    此句仅调用A的构造函数  设置B对象c的a b值

m.printa();    //此句输出a=7,  b=9     调用B的printb来输出
return 0;
}

 三   所有对象共享此静态变量     类中的静态成员函数可以直接以类名来调用       静态成员变量直接用静态成员函数来调用

104

1035     789.504

//5.6 编程要求
#include <iostream>
#include <math.h>

using namespace std;

class sum
{
friend void youyuan(sum &a);
public:
sum(); //default
sum(int _x,int _y);
sum(int _x, int _y,int _z);
void print(int i=1);
static void jingtai();
private:
int x;
int y;
int z;
static int m;

};
int sum::m=1;
sum::sum()
{
x=y=0;
cout<<"default constructor!"<<" ";
cout<<"x:"<<x<<";y:"<<y<<" ";
}
sum::sum(int _x,int _y)
{
x=_x;y=_y;
cout<<"constructor #1!"<<" ";
cout<<"x:"<<x<<";y:"<<y<<" ";
}
sum::sum(int _x,int _y,int _z)
{
x=_x;y=_y;z=_z;
cout<<"constructor #2!"<<" ";
cout<<"x="<<x<<";y="<<y<<";z="<<z<<" ";
}
void sum::print(int i)
{
if(i==1) cout<<"默认参数:"<<i<<" ";
else cout<<"非默认参数:"<<i<<" ";

}

void youyuan(sum &a)
{
cout<<"member is"<<a.x<<":"<<a.y<<" ";
}

void sum::jingtai()
{
cout<<"访问静态成员变量:"<<m<<"::fuck"<<" ";
}
int main()
{
sum A; //构造重载
sum B(3,5);
sum C(3,5,8);
B.print(5);//成员函数非默认
A.print();//默认构造函数
C.jingtai();
youyuan(A);
youyuan(B);
return 0;
}

     

  

原文地址:https://www.cnblogs.com/dongguolei/p/9765895.html