类与对象学学习心得


知识点:

类的定义1.类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型

2.类的定义包括行为和属性两个部分。

3.属性以数据表示,行为通过函数实现。

定义格式:

                     class  类名

{

   public://用来与外界交互的数据和函数

           公有数据成员和成员函数;

   protected:

           保护数据成员和成员函数;

   private: //可以保护的数据,除了自己类的函数可以使用,其他的函数不能对其修改

           私有数据成员和成员函数;

};

例子:class Student        //定义学生类Student

{

public:               //声明类成员

                         void Getinfo(string pname, string pid

            ,char Sex, inta,double s);//函数用来获得学生的信息

                         void modify(float s);

                         void display();//用来展示被保护的数据

private:

          string name;//这里的姓名等为私有数据

      string id;

      char sex;

          int age;

          double score;

};//最重要的一点是不要忘了这个分号

注意事项:类的成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。

没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。

就是说把私有成员放在开头不声明类型时,认为为私有类。

 

各种不同访问性质的成员再说明段在类定义中没有顺序规定,也类定义中没有顺序规定,也可以重复出现。为了方便了解接口,往往把共有段成员放在开始位置。

成员函数在类外定义使用作用域区分符进行说明,形式为:

返回类型  类名::参数名(参数表)

其中作用域用::表示,用来表示那一个类的函数。

 

 

而成员函数的作用为:一是操作数据成员,包括访和修改数据成员。二是用于协同不同的数据成员操作,成为传递信息。

 

对象:对象类类型的变量,也想当于类的实体。

类与对象的关系,如同C++基本数据类型和变量的关系。像是自己定义了一种类,自己控制着种类的内容,来满足自己的需要。

而类的数据成员可也是基本的类型外,还可以是数组,结构,类等自定义的数据类型。

甚至可以是自己定义的类的对象.这种形式称为类的包含。

classPoint    //定义一个point类用来记录一个点的特征

{float P1_x,P1_y;

public:  

       void InitPoint(float PointA_x, floatPointA_y);

       void Move(float New_x, float New_y);

       float GetPointx();

       float GetPointy();

         

};

intmain()

{

      Point p1,p2;//p1 p2 为具有这种类的特征的两个点

}

类成员的访问形式

包括 圆点和指针这两种形式

圆点S.display();

指针   对象指针变量名—》公有成员

 

指针:当一个对象调用类的成员函数时,对象的地址被传给this指针,this指针指向被调用成员。This是隐含指针不能显式使用。当this指针一旦被初始化,就不能在进行修改和赋值,以保证不会指向其他的成员。

 

#include<iostream.h>

class  ptr_access   {

public:                          

 void setvalue(float a, float b) { x=a; y=b; }

 float Getx() {return x;}

 float Gety() {return y;}

 void print()

{

        cout<<"x="<<x<<endl;

        cout<<"y="<<y<<endl;

 }

private:                          //私有数据成员

        float x,y;

};

intmain()

{

        float a1,a2;

        ptr_access *ptr=new ptr_access;

           ptr->setvalue(2,8);     

      //通过指针访问公有成员函数

        ptr->print();

        a1=(*ptr).Getx();    

      //通过公有成员函数访问私有数据成员

        a2=(*ptr).Gety();

        cout<<"a1="<<a1<<endl;

        cout<<"a2="<<a2<<endl;

        return 0;

}

写一个程序,输入矩形的长和宽,输出面积和周长

分析:将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。

              长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数”

 

classCRectangle

{

       public:

              int w,h;

              int Area() { 

                     return w * h;     

              }

              int Perimeter()    {

                     return 2 * ( w + h); 

              }

              void Init( int w_,int h_ ) {

                     w = w_;  h = h_;

              }

};

intmain( )

{

        int w,h;

        CRectangle r; //r是一个对象

        cin >> w >> h;

        r.Init( w,h);

        cout << r.Area() << endl <<r. Perimeter();

        return 0;

}

  1. 在类的定义中不能对数据成员进行初始化。
  2. 类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
  3. 类中的数据成员可以是C++语法规定的任意数据类型。
  4. 类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
  5. 类定义必须以分号“;”结束
  6. class与struct的不同:

   class中,成员缺省情况是private。

   struct中,成员缺省情况是public。

 

 

知识点:内敛函数

作用    减少频繁调用小子程序的时间开支。

机制    编译器在编译时,,将内联函数的调用以相应代码代替。

声明    inline 函数原型

注意内联函数在函数原型作一次说明。

适用于只有1-5行的小函数

不能含有复杂结构控制结构语句不能递归调用

在整理这个知识点时,才意识到之前自己的做法是错误的,自己以前经常在类函数内进行大量的操作。

 

classCoord{

    public:

        void setCoord(int a,int b)

        { x=a; y=b;}

        int getx()

        { return x;}

        int gety()

        { retrun y;}

   private:

        int x,y;

   };

 

函数重载:函数名相同,但是参数不同(类型不同,或者个数不同)的一组函数。

函数重载用于处理不同数据类型的类似任务

实例:

#include<iostream>

usingnamespace std ;

int  abs ( int a ) ;

double  abs ( double f ) ;//对于相同的函数 不同的参量,调用不同的函数

intmain ()

  {  cout<< abs ( -5 )  << endl;

      cout << abs ( -7.8 )<< endl ;

  }

int  abs ( int a )

  { return  a <  0 ?  -a  :  a;  }

double  abs ( double f )

  { return  f < 0  ? -f  :  f ;  }

这里有一个重载解决小问题的函数

#include<iostream>

usingnamespace std ;

int  max ( int a , int b ) ;

int  max ( int a , int b,  int c ) ;

intmain ()

 { cout << max ( 5, 3 ) <<endl ;

    cout << max (4, 8, 2 )<< endl ;

 }//对于不同个数比较要求 调用不同的函数

int  max ( int a , int  b )

 { return a > b ? a : b ;  }

int  max ( int a , int  b,  int  c)

 { int  t;

    t = max ( a ,  b ) ;

    return max ( t ,  c ) ;

 }

 

构造函数和析构函数

构造函数是用于创建对象的特殊成员函数

当创建对象时,系统自动调用构造函数

作用 为对象分配空间,对数据成员赋值,请求其他资源

没有用户定义的构造函数时,系统提供缺省版本呢的构造函数。

构造函数名与类名相同:类名

构造函数可以重载

构造函数可以有任意类型的参数,但没有返回类型。

同时,构造函数是可以重载的。

 

与构造函数对应的,是析构函数。对于析构函数的使用,后面也有详细的介绍和使用。

析构函数用于取消对象的成员函数。当一个对象作用域结束时,系统自动调用析构函数。

当没有用户定义的析构函数时,系统提供省却版本的析构函数。

析构函数名为~类名。

析构函数没有参数,也没有返回类型。

如果类中没有定义的构造函数,系统将自动生成一个默认形式的构造函数,用于创造对象,默认构造函数形式。

类名::类名(){}

默认构造函数时是一个空函数。

构造函数和析构函数不应该定义在私有部分。因为对象必须在类说明外创建和撤销。

#include<iostream.h>

classDate {

    public:

          Date();     // 无参构造函数

          Date(int y,int m,int d);

          void showDate();

    private:

            int year, month, day;

    };

Date::Date()    // 构造函数的实现

{  year=0; month=0; day=0; }

Date::Date(inty,int m,int d)

{year=y; month=m; day=d; }

inlinevoid Date::showDate()

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

intmain()

{

     Date a_date,bDate(2014,3,25);  

     a_date.showDate();

     b_date.showDate();

     return 0;

}

        (1) 利用构造函数直接创建对象.其一般形式为:

             类名  对象名[(实参表)];

          这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数。

 

 

intmain()

{

          Date *date1;

            date1=new Date(1998,4,28);    

              // 以上两条语句可合写成:Date *date1=new Date(1998,4,28);

          cout<<"Date1 output1:"<<endl;

          date1->showDate();         

          delete date1;

             return 0;

}

 利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:

      类名 *指针变量 = new 类名[(实参表)];

 例如:

      Date *date1=new Date(1998,4,28);

就创建了对象(*date1)。

带参数的构造函数在建立一个对象时,可以用指定的数据初始化对象的数据成员

调用参数化的构造函数,创建了两个对象。构造函数与成员函数,的作用和调用时机不同,例如,我们呢可以用setDate重复对一个成员进行赋值操作,但是构造函数,只是在创建对象时,调用和初始化赋值。而且构造函数不能显示使用。另外注意,构造函数和析构函数的次序时相反的,即先创建的函数最后析构

当对象是有new运算动态符所创建的,delete运算会自动调用析构函数

当new动态运算符创建的对象如果不用delete释放,即使建立对象的函数执行结束,系统也不会调用析构函数,这样会导致内存泄漏

构造函数的初始化列表————数据成员的初始化

有两种方法:

1.     使用构造函数的函数体进行初始化

2.     使用构造函数的初始化列表进行初始化

3.     class Date//使用构造函数的函数体进行初始化

4.     {

5.      int d, m, y;

6.     public:

7.      Date(int dd, int mm, int yy)

8.      {

9.             d=dd;//在函数体内进行赋值。

10.          m=mm;

11.          y=yy;

12.  }

13.  Date(int dd, int mm)

14.  {

15.          d=dd;

16.          m=mm;

17.  }

18.  }

19.  格式:/使用初始化列表进行初始化

20.  funname(参数列表):初始化列表

21.  {  函数体,可以是空函数体  }

22.  初始化列表的形式:

23.  成员名1(形参名1),成员名2(形参名2),成员名n(形参名n)

 

 

 

classDate

{

       int d, m, y;

public:

       Date(int dd, int mm, int yy):d(dd),m(mm),y(yy)//初始化列表的形式

       {                    }

       Date(int dd, int mm): d(dd),m(mm)

       {               }

}

必须使用参数初始化列表对数据成员进行初始化的几种情况:

  1. 数据成员为常量
  2. 数据成员为引用类型
  3. 数据成员为没有无参构造函数的类的对象

  

#include<iostream>

usingnamespace std;

classA{

public:

       A(int i):x(i),rx(x),pi(3.14)//对常量pi进行列表初始化,对引用的rx进行初始化列表初始化

       {}

       void display()

       {cout<<"x="<<x<<"rx="<<rx<<"pi="<<pi<<endl;}

private:

       int x,&rx;//其中rx为引用的形式

       const float pi;//其中pi为常量

};

intmain(){

       A aa(10);

       aa.display();

       return 0;

}

#include<iostream>

usingnamespace std ;

classA

{public :

    A ( int x ) : a ( x ) { }

    int a ;

} ;

 

 

 

 

 

classB

{public :

    B( int x, int y ) : aa( x ),  b( y ) { }

    void out()

      { cout << "aa = "<< aa.a << endl << "b = " << b << endl; }

  private :

      int b ;

      A aa ;//数据成员为没有无参构造函数的类的对象

} ;

intmain ()

  { B objB ( 3, 5 ) ;

     objB . out ( ) ;

  }

类成员的初始化的顺序:

 

按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关

#include<iostream>

usingnamespace std;

classCMyClass{

public:

       CMyClass(int x, int y):m_y(x),m_x(m_y)//运行结果为m-x=随机大数m-y=15

       {

              cout<<"m_x="<<m_x<<endl;

              cout<<"m_y="<<m_y<<endl;

       }

private:

       int m_x,m_y;

};

intmain()

{

       CMyClass mc(15,10);

       return 0;

}

 

                   构造函数的重载( overload)

                                               classBox{

public:

                                               Box();

                                               Box(int,int , int);

                                               intvolume();

private:

                                               intheight,width, length;

};

 

Box::Box()

{                                              height=10;     width=10;    length=10;}//赋予初值

Box::Box(inth, int w,int l):height(h),width(w),length(l)

{}

intBox::volume()

{                                              returnwidth*length*height;}

int main(){

                                               Boxbox1;

                                               cout<<"Thevolume is "<<box1.volume();

                                               Boxbox2(12,30,25);

                                               cout<<"Thevolume is "<<box2.volume();

                                               return0;

}

带默认值的构造函数

#include<iostream>

usingnamespace std;

class Box

{

public:

     Box(); //定义了全部带默认值的构造函数,不能再定义无参构造函数

                                               Box(inth=10,int w=10 , int l=10); //只能在声明时指定默认值

                                               intvolume();

private:

                                               intheight,width, length;

};

Box::Box(inth, int w,int l):height(h),width(w),length(l)

{}

intBox::volume(){

                                               returnwidth*length*height;

}

int main()

{

                                               Boxbox1;

                                               cout<<"Thevolume is "<<box1.volume();

                                               Boxbox2(12,30,25);

                                               cout<<"Thevolume is "<<box2.volume();

    Box box3(30,25);

                                               cout<<"Thevolume is "<<box3.volume();

                                               return0;

}

构造函数的调用

在生成对象时调用构造函数

 

例子:

class Person

{

  public:

    void setAge( unsigned n ){ age = n; }

    unsigned getAge( ) { return age; }

  private:

    unsigned age;

};

int main()

{

    person a;

    person b[60]

   …….

 

当在生成类的对象时,会调用构造函数,当声明的对象不带有参数时,没有无参构造函数时,将会出错,因为没有对应类型的构造函数。

class Person

{

  public:

    Person (unsigned x):age(x){}//只是定义的一个带有参数的构造函数。

    void setAge( unsigned n ){ age = n; }

    unsigned getAge( ) { return age; }

  private:

    unsigned age;

};

int main()

{

    Person a(20);

    Person b[60];//此时没有带参数的对象因为没有对应的构造函数将会出错

   …….

构造函数一般被定义为公有成员。因为函数作为与外界的接口如果定义为私有函数,在外界是不能调用的。

class Date

{

private:

int  d, m, y;

Date( )//此时的函数定义为私有函数,在main函数里不能直接调用。

{

                                 d = 11;

                                                 m= 11;

                                                 y= 2011;

                                               }

};

int main( )

{

                                               Datetoday;  // error C2248: “Date::Date”: 无法访问 private 成员(在“Date”类中声明)

                                               return0;

}

例子:

设计一个point 类

数据成员

                                               点的坐标x,y

成员函数

                                               无参构造函数 

                           有参构造函数

                                               set函数(设置x,y的值)

                                               get_X函数//分别用来获取坐标值

                           get_Y函数

                           display    函数//用来展示x,y 的坐标

#include <iostream>

#include <cmath>

using namespace std;

class Point{

                                               intx,y;//私有成员,点坐标。

public:

       Point(){}

                                               Point(int a,int b)

                                               {x=a,y=b;      }

                                               voidsetx(int xx)    {      xx=x;     }//设置坐标x的值

                                               voidsety(int yy)    {      yy=y;     }

                                               intgetx() {      returnx; }//获取坐标值

                                               intgety() {      returny; }

};

 

 

#include <iostream>

#include <cmath>

using namespace std;

class Point{

                                               intx,y;

public:

                                               Point(int a=0,int b=0)//基于默认初始值

                                               {x=a,y=b;      }

                                               voidsetx(int x)      {      this->x=x;    }//利用this来指向成员函数

                                               voidsety(int y)      {      this->y=y;    }

                                               intgetx() {      returnx; }

                                               intgety() {      returny; }

};

 

 

析构函数:

对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间

析构函数可以完成上述工作。

析构函数自动调用(隐式调用)

析构函数没有返回值,不能有参数也不能重载

定义格式如下(类外实现):

类名::~类名()

{

       函数语句

}

析构函数具有一些特点:

    析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);

 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;

   当撤消对象时,编译系统会自动地调用析构函数。

 

 

默认析构函数

若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。

系统自动生成的默认构造函数形式如下:

类名::~类名(){}

一般情况下,可以不定义析构函数

但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收

#include <iostream>

#include <string>

using namespace std;

class Student{

public://定义一个具有 序号 姓名 性别的类

                                               Student(intn, string a_name, char s) {

                                                 num=n;

                                                 name=a_name;

                                                 sex=s;    cout<<"Constructorcalled."<<endl;

                                               }

                                               ~Student()//当析构函数执行时 会打印 提示语句

                                               { cout<<"Destructorcalled."<<endl; }

                                               voiddisplay()

                                             {cout<<name<<endl;cout<<num<<endl;cout<<sex<<endl;                                               }

private:

                                               intnum;        stringname; char sex;

};

int main()

{

                                               Studentstud1(10001,"Wang_li",'f');

                                               stud1.display();

                                               Studentstud2(10002,"Zhao_Wu",'s');

                                               stud2.display();

                                               return0;

}

 

 

显示结果:

Constructor called.

Wang_li

10001

f

Constructor called.

Zhao_Wu

10002

s

Destructor called.//此时当对象的调用结束后将对其清空 释放空间

Destructor called.

Press any key to continue

 

 

对于这个问题,有不同方式解决

#include <cstring>

#include <iostream>

 using namespace std;

class Student{

public:

                                               Student(intn, char * a_name, char s) {

                                               num=n;name=new char[strlen(a_name)+1];//利用指针 获取长度并指向下一空间  临时创建空间

                                                 strcpy(name,a_name); //复制

                                               sex=s;

                                                 cout<<"Constructorcalled."<<endl;

                                               }

                                               ~Student(){

                                                 cout<<"Destructorcalled."<<endl;

                                                 delete[]name;

                                               }

                                               voiddisplay()

                                             {cout<<name<<endl;cout<<num<<endl;cout<<sex<<endl;                                               }

private:

                                               intnum; char *name; char sex;

};

int main()

{

                                               Studentstud1(10001,"Wang_li",'f');

                                               stud1.display();

                                               Studentstud2(10002,"Zhao_Wu",'s');

                                               stud2.display();

                                               return0;

}

 

用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配存储单元

对象的储存

#include <iostream>

using namespace std;

class Time

{

public:

                                                 int hour;

                                                 int minute;

                                                 int sec;

                                                 void setHour( ){   hour=10;}//初始化小时的值为10

};

int main( )

{                                          cout<<sizeof(Time)<<endl;   return 0; }

类的成员函数如何区分不同的实例对象的数据成员呢?

C++为此专门设立了一个名为this的指针,用来指向不同的对象。

一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。

 

需要显式引用this指针的三种情况:

(1)在类的非静态成员函数中返回类对象本身对象的引用的时候,直接使用return *this,返回本对象的地址时,return this。

(2当参数与成员变量名相同时,如this->x = x,不能写成x= x。

(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。

 

函数返回对象的引用:

 

#include <iostream>

#include <string>

using namespace std;

class Person{

public:

                                               Person(stringn, int a)   {

                                                 name= n;    //这里的 name 等价于this->name

                                                 age= a;      //这里的 age 等价于this->age

                                               }

                                               intget_age(void) const{  return age;  }

                                               Person& add_age(int i) {age += i;         return *this;        }

private:

                                               stringname;

                                               intage;

};

 

int main()

{

                                               PersonLi("Li", 20);

                                               cout<<"Liage = "<< Li.get_age()<<endl;

                                               cout<<"Liadd age = "<<Li.add_age(1).get_age()<<endl; 

                                                  //增加1岁的同时,可以对新的年龄直接输出;

                                               return0;

}

程序执行结果为:

                                               Liage = 20

                                               Liadd age = 21

 

参数与成员变量名相同。

 

#include <iostream>

using namespace std;

class Point

{

public:

                                               intx;

                                               Point():x(0){}

                                               Point(int a){  x=a;  }

                                               voidprint(){   cout<<"x ="<<x<<endl;  }

                                               voidset_x(int x)x = x;  }

};

int main(){//两个程序进行对比,当不使用this指针时,和使用指针时

                                               Point pt(5);

                                               pt.set_x(10);

                                               pt.print();

                                               return0;

}

程序执行结果为://可以知道 当使用this指针时,用来区分两个变量

x = 5

若将set_x函数改为:

                                               voidset_x(int x)    {      this->x = x;  }

程序执行结果为:

                                               x = 10

当为了避免对同一个对象进行赋值操作:

#include <iostream>

using namespace std;

class Location

{

    int X,Y;  //默认为私有的

 public:

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

    void assign(Location& pointer);

    int GetX(){ return X; }

    int GetY(){ return Y; }

};

void Location::assign(Location&pointer)

{

   if(&pointer!=this) //同一对象之间的赋值没有意义,所以要保证pointer不等于this

   {       X=pointer.X;       Y=pointer.Y;    }

}

 int main(){

    Location x;

    x.init(5,4);

    Location y;

                                            y.assign(x);

                                            cout<<"x.X= "<< x.GetX()<<" x.Y = "<<x.GetY();

                                            cout<<"y.X= "<< y.GetX()<<" y.Y = "<<y.GetY();

                                            return0;

}

void Location::assign(Location&pointer)

{

   if(&pointer!=this) //同一对象之间的赋值没有意义,所以要保证pointer不等于this

   {       X=pointer.X;        Y=pointer.Y;    }

}

 int main(){

    Location x;

    x.init(5,4);

    Location y;

                                            y.assign(x);

                                            cout<<"x.X= "<< x.GetX()<<" x.Y = "<<x.GetY();

                                            cout<<"y.X= "<< y.GetX()<<" y.Y = "<<y.GetY();

                                            return0;

}

 

 

复制构造函数:

复制构造函数用一个已有同类对象创建新对象进行数据初始化

C++为类提供默认版本的复制构造函数

程序员可以定义用户版本的复制构造函数

 语法形式

                                               类名 :: 类名(const  类名  &  引用名  ,  …);

//const用来保护实参对象只读

 

复制构造函数的特点:

  1. 复制构造函数名与类名相同,并且也没有返回值类型。
  2.  复制构造函数可写在类中,也可以写在类外。
  3.  复制构造函数要求有一个类类型的引用参数。
  4.  如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。

调用时机:当建立对象时,调用构造函数,也可以调用复制构造函数进行数据初始化。

复制构造函数的基本工作方式

class Point                             

{

public:                                   

     Point(float PointA_x=0, float PointA_y=0);

void Move(float New_x, float New_y);

float GetPointx();

float GetPointy();

private:

  float P1_x,P1_y;

};

void main(){

     Point  a(5,10),b(a);

}

此时a与b的对应的成员函数相同

 

#include<iostream>

using namespacestd;

class Box{

public:

                                               Box(int=10, int =10, int =10);

                                               Box(constBox & b)

                                               {height=2*b.height;                                               width=2*b.width;length=2*b.length;}

                                               intvolume();

private:

                                               intlength, height, width;

};

 

Box::Box(inth, int w, int l):length(l),width(w),height(h){  }

intBox::volume (){                  returnwidth*height*length;}

int main(){

                                               Boxbox1(1,2,3),box2(box1),box3=box2;

                                               cout<<"Box1'svolume is "<<box1.volume()<<endl;

                                               cout<<"Box2'svolume is "<<box2.volume()<<endl;

                                               cout<<"Box3'svolume is "<<box3.volume()<<endl;

                                               return0;

}

复制构造函数的调用:

 

以下三种情况下由编译系统自动调用:

  1. 声明语句中用类的一个已知对象初始化该类的另一个对象时。
  2. 当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
  3. 当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。

 

浅复制与深复制

 

关于浅复制:

●在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。

即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容

●默认复制构造函数所进行的是简单数据复制,即浅复制

浅复制存在的问题:

#include<iostream>

#include<string.h>

usingnamespace std;

class Person

{

public:

                                               Person(char*name1,int a,double s);   

                                               voiddisplay(){cout<<name<<"\t"<<age<<"\t"<<salary<<endl;}

                                               ~Person(){deletename;}                          //析构函数的声明

private: 

                                                char* name;

                                               intage;

                                               doublesalary;

};

Person::Person(char*name1,int a,double s)          

{

                                               name=newchar[strlen(name1)+1];

                                               strcpy(name,name1);

                                               age=a;

                                               salary=s;

}

int main()

{

Person *P1=newPerson("WangWei ",8,3880); //调用构造函数创建对象P1

P1->display();

 Person P2(*P1);  //调用复制构造函数,用P1的数据初始化对象P2

                                                delete P1;

 P2.display();

return 0;

}

而运行结果为:

关于深复制:

●通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。

●自定义复制构造函数所进行的复制是浅复制。

 

定义支持深复制的复制构造函数

  1. 深复制构造函数必须显式定义
  2. 深复制构造函数的特点

   定义:类名::类名([const] 类名 &对象名);

   成员变量的处理:对复杂类型的成员变量,使用new操作符进行空间的申请,然后进行相关的复制操作

 

Person::Person(constPerson& P0) //复制构造函数的实现

{

                                              name=new char[strlen(P0.name)+1];

                                               strcpy(name,P0.name);

                                               age=P0.age;

                                               salary=P0.salary;

cout<<"ff"<<endl;

}

 

 

 

类的其他成员:

Ø  类定义中除了一般指定访问权限的成员,还可以定义各种特殊用途的成员。

                                               常成员

                                               静态成员

                                               友元

 

常成员:

Ø  常数据成员是指数据成员在实例化被初始化后,其值不能改变。

Ø  在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数

 

由构造函数直接用常量初始化常数据成员

#include<iostream>

usingnamespace std;

class Mclass

{ public :

       int k;

       const int M;//说明常数据成员

       Mclass() : M(5) { } //用初始式对常数据成员赋值

       void testFun()

        { //M++;//错误,不能在成员函数中修改常数据成员

            k++;//可以修改一般数据成员

        }

 } ;

1. 常数据成员

       使用const说明的数据成员称为常数据成员。

        如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。

#include<iostream>

usingnamespace std;

class Mclass

{ public :

      int k;

      const int M;                   //说明常数据成员

      Mclass() : M(5) { }        //用初始式对常数据成员赋值

      void testFun()

       { //M++;                     //错误,不能在成员函数中修改常数据成员

           k++;                      //可以修改一般数据成员

       }

 } ;

int main()

{ Mclass t1,t2;

   t1.k=100;                          

   //t1.M=123;                      //错误,不能在类外修改常数据成员

  cout<<"t1.k="<<t1.k<<'\t'<<"t1.M="<<t1.M<<endl;

   t2.k=200;  t2.testFun();

  cout<<"&t1.M="<<&t1.M<<endl;

  cout<<"t2.k="<<t2.k<<'\t'<<"t2.M="<<t2.M<<endl;

   cout<<"&t2.M="<<&t2.M<<endl;

}

#include<iostream>

#include<cstring>

usingnamespace std;

struct Date {int year, month, day ; }; //结构

class Student

{ public:

    Student (int y,int m,int d, int num=0,sring name="no name");

    void PrintStudent()const;  //常成员函数

  private:  

    const int code;                         //常数据成员

    string name;

    Date birthday;                          //结构数据成员

};

 

//用带参构造函数完成数据成员初始化

Student::Student(int y, int m, int d, int num, string name ) : code( num )

{this->name=name;

  birthday.year=y;

  birthday.month=m;

  birthday.day=d;

}

int main()

{ Studentstu1( 1990, 3, 21, 1001, "陈春“ );

  stu1.PrintStudent();

  Student stu2(1985, 10, 1, 1002, "张庆华“);

  stu2.PrintStudent();

}

 

常对象:

 

如果在说明对象时用const修饰,则被说明的对象为常对象。

    常对象的说明形式如下:

      类名 const 对象名[(参数表)];

  或者

      const 类名 对象名[(参数表)];

  在定义常对象时必须进行初始化,而且不能被更新。

    说明:

   (1)C++不允许直接或间接更改常对象的数据成员。

   (2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。

 

3.     常成员函数

 

       在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:

       类型说明符 函数名(参数表) const;

      const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。

     常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)

 

Ø  4.类成员冠以static声明时,称为静态成员。

Ø   静态数据成员为同类对象共享。

Ø   静态成员函数与静态数据成员协同操作。 

 

静态成员函数

•       静态成员不属于某一个单独的对象,而是为类的所有对象所共有

•       静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员

 

静态数据成员声明及初始化

在类外进行静态数据成员的声明

类型 类名::静态数据成员[=初始化值];   //必须进行声明

不能在成员初始化列表中进行初始化

如果未进行初始化,则编译器自动赋初值(默认值是0

初始化时不能使用访问权限

 

除静态数据成员以外,一个类还可以有静态成员函数。

静态函数仅可以访问静态成员,

或是静态成员函数或是静态数据成员。

静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

静态成员函数没有this指针,只能对静态数据操作

 

定义静态成员函数的格式如下:

    static 返回类型 静态成员函数名(参数表);

    与静态数据成员类似,调用公有静态成员函数的一般格式有如下几种:

   类名::静态成员函数名(实参表)

   对象. 静态成员函数名(实参表)

   对象指针->静态成员函数名(实参表)

 

说明:

(1)静态成员函数在类外定义时不用static前缀。

(2)静态成员函数主要用来访问同一类中的静态数据成员。

 (3)   私有静态成员函数不能在类外部或用对象访问。

(4)可以在建立对象之前处理静态数据成员。

(5)编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。

(6)静态成员函数中是没有this指针的。

(7)静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。

 

 

代码:根据老师的代码改变。

#include<iostream>

#include<string>

#include<algorithm>

#include<cstdio>

using namespace std;

int caidan(int x);

class student

{

   string name,phone,xingbie;

    intno;

    intscore[3];

   float average;

    intorder;

  public:

  student(int id,string na,int x,int y,int z,string phone1,stringxingbie1):name(na),no(id),phone(phone1),xingbie(xingbie1)

  {score[0]=x,score[1]=y,score[2]=z;

  order=1;average=(x+y+z)/3;//phone=phome1;

   }

  student()

   {

      score[1]=score[0]=score[2]=0;

      order=1;average=0;

   }

   intgetNo(){return no;}

  float getAverage(){return average;}

   voidsetAverage(int avg){average=avg;}

   voidsetOrder(int x){order=x;}

   intgetQrder(){return average;}

  string getName(string name){return name;}

   voidsetName(string name){this->name=name;}

   voiddisplay();

   voidchangename(){string x;cin>>x;name=x;}

   voidchangegaoshu(){int x;cin>>x;score[0]=x;}

   voidchangeyingyu(){int x;cin>>x;score[1]=x;}

   voidchangec(){int x;cin>>x;score[2]=x;}

   voidchangephone(){string x;cin>>x;phone=x;}

   voidchangexingbie(){string x;cin>>x;xingbie=x;}

};

void student::display()

{

   cout<<name<<"\t"<<no<<"\t"<<score[0]<<"\t"<<score[1]<<"\t"<<score[2]<<"\t"<<average<<"\t\t"<<order<<"\t"<<phone<<"\t"<<xingbie<<endl;

}

bool cmp1(student stu1,student stu2)

{

   if(stu1.getAverage()-stu2.getAverage()>=1e-9)return 1;

   else return 0;

}

bool cmp2(student stu1,student stu2)

{

   return stu1.getNo()<stu2.getNo();

}

class student_list

{

   student list[60];

    intn;

   public:

   student_list():n(0){};

   void add();

   void deleteStu();

   void query();

   void change();

   void display(int flag);

   void menu();

    intsearch(int no);

   void sortList();

   void change2();

 

};

void student_list::add()

{cout<<"list add\t";

    intno,x,y,z,ji=0;

   string name;

   string phone,xingbie;

   system("cls");

   cout<<"\t\t\t\t";

   cout<<"输入姓名高数 英语 C++ 成绩 电话 性别 -1为退出"<<endl;

   while((cin>>no)&&no!=-1)

   {ji++;

       cin>>name>>x>>y>>z>>phone>>xingbie;

       student s(no,name,x,y,z,phone,xingbie);

       list[n++]=s;

       if(ji!=1)sortList();//bengkui hanshu

    }

//int ji1=0;cout<<endl<<"ruturnto the caidan putin -1";cin>>ji1;

   //if(ji1==-1)caidan(0);

}

void student_list::sortList()

{cout<<"list sortlist\n";

   sort(list,list+n,cmp1);

    inti;

   for(i=0;i<n;i++)

   list[i].setOrder(i+1);

   //int ji=0;cout<<endl<<"ruturn to the caidan putin-1";cin>>ji;

   //if(ji==-1)caidan(0);

}

void student_list::display(int flag)

{cout<<"list display\n";intji=0;

   if(flag)sort(list,list+n,cmp2);

   else sort(list,list+n,cmp1);

   

   for(int i=0;i<n;i++)

   list[i].display();

   //cout<<endl<<"ruturn to the caidan putin-1";cin>>ji;

   //if(ji==-1)caidan(0);

}

int student_list::search(int no)

{cout<<"list search\n";

    inti;

   for(int i=0;i<n;i++)

   if(list[i].getNo()==no)

   return i;

   return -1;

}

void student_list::query()

{cout<<"list query\n";

    intno,i;

   system("cls");

   cout<<"\n\t\t\t";

   

   while(cin>>no&&no!=-1)

    {

       i=search(no);

       if(i!=-1)

       {

              cout<<"����"<<"\tѧ��"<<"\t��ѧ"<<"\tӢ��"<<"\tc++"<<"\tƽ��ɼ�"<<"\t���"<<endl;

           list[i].display();

                

       }

       else cout<<""<<endl;

 

   }//int ji=0;cout<<endl<<"ruturn to the caidan putin-1";cin>>ji;

   //if(ji==-1)caidan(0);

}

void student_list::change()

{int no,ji,i;

   cout<<"which ment do you want to change first putinno:"<<endl;

cin>>no;

i=search(no);

   

           list[i].display();

   cout<<"1>>姓名"<<endl;

   cout<<"2>>高数"<<endl;

    cout<<"3>>英语"<<endl;

   cout<<"4>>C++"<<endl;

   cout<<"5>>电话"<<endl;

   cout<<"6>>性别"<<endl;

   cin>>ji;

   switch(ji)

{

   case 1:list[i].changename();break;

    case 2:list[i].changegaoshu();break;

    case 3:list[i].changeyingyu();break;

    case 4:list[i].changec();break;

    case 5:list[i].changephone();break;

    case 6:list[i].changexingbie();break;

}

cout<<endl;

cout<<"afterchange"<<endl;list[i].display();

}

void student_list::deleteStu()

{

 

}

int main()

{//int ji=0;

   //student_list c;

   /*c.add();

   c.display(0);

   c.query();

   c.display(0);*/

    intji=0,no=-1;student_list c;//system("cls");

   while(1){//int ji;//cin>>ji;

  switch(ji)

{case 0://system("cls");

cout<<"请选择服务 输入服务"<<endl;

   cout<<"1>>添加"<<endl;

   cout<<"2>>查询"<<endl;

   cout<<"3>>更改"<<endl;

   cout<<"4>>删除"<<endl;

   cout<<"5>>退出"<<endl;//getchar();

   while(ji==0){cin>>ji;}

   break;

   case 1:cout<<"添加"<<endl;c.add();ji=0;break;

    case 2:system("cls");cout<<"查询号码>>";c.query();ji=0;break;

    case 3:system("cls");cout<<"更改"<<endl;c.change();ji=0;break;

    case 4:system("cls");cout<<"删除"<<endl;c.deleteStu();ji=0;break;

    //case 5:return -1;

    default: ji=0;

}

}//}

   return 0;

}

int caidan(int x)

{int ji=x,no=-1;student_list c;system("cls");

 

switch(ji)

{case 0:cout<<"请选择服务 输入服务"<<endl;

   cout<<"1>>添加"<<endl;

   cout<<"2>>查询"<<endl;

   cout<<"3>>更改"<<endl;

   cout<<"4>>删除"<<endl;

   cout<<"5>>退出"<<endl;//getchar();

   //cin>>ji;

   cout<<"   "<<endl;break;

   case 1:cout<<"添加"<<endl;c.add();break;

    case 2:system("cls");cout<<"查询号码>>";cin>>no;c.query();break;

    case 3:system("cls");cout<<"更改"<<endl;c.change();break;

    case 4:system("cls");cout<<"删除"<<endl;c.deleteStu();break;

    case 5:return -1;

}

}

自己做的代码:

#include<iostream>

#include<algorithm>

#include<string>

#include<cstdio>

#include<stdlib.h>

using namespace std;

class student//姓名 成绩 学号 手机号 地址

{string name,phone,where;

float chengji[4];

int no,id;

public:

void putin(int id1,string name1,float x,floaty,float z,string phone1,stringwhere1)//:name(name1),phone(phone1),where(where1)

   {id=id1;name=name1;phone=phone1;where=where1;

       chengji[0]=x;chengji[1]=y;chengji[2]=z;chengji[3]=(x+y+z)/3;

    }

voidputin(){chengji[0]=chengji[1]=chengji[2]=chengji[3]=9;}

void disName(){cout<<"姓名"<<name<<endl;}

void disCj(){cout<<"三科成绩"<<chengji[0]<<"  "<<chengji[1]<<"  "<<chengji[2]<<"  "<<chengji[3]<<endl;}

void disPerson(){cout<<"详细情况"<<where<<"  "<<phone<<endl;}

void disAll(){cout<<"姓名"<<name<<endl;cout<<"三科成绩"<<chengji[0]<<"  "<<chengji[1]<<"  "<<chengji[2]<<"  "<<chengji[3]<<endl;cout<<"详细情况"<<where<<" "<<phone<<endl;}

voidchange(){system("cls");cout<<"Now you are chengge  the information"<<endl;int ji=0;

cout<<"1>>高数  2>>英语  3>>c++ 4>>电话  5>>地址"<<endl;cin>>ji;int i=0;string i1;

switch(ji)

{case 1:cout<<"putin the tureinformation:";cin>>i;chengji[0]=i;break;

case 2:cout<<"putin the tureinformation:";cin>>i;chengji[1]=i;break;

case 3:cout<<"putin the tureinformation:";cin>>i;chengji[2]=i;break;

case 4:cout<<"putin the tureinformation:";cin>>i1;phone=i1;break;

case 5:cout<<"putin the tureinformation:";cin>>i1;where=i1;break;

}

}

 

};

int main()

{int bi=0;

   student stu[60],s;

   string name1,phone,where;int x,y,z;

    intn=1;

   while(1)

    {

       int kai,id;system("cls");

       cout<<"welcome to the system"<<endl;

       cout<<"1>>add  2>>change 3>>look all the information"<<endl;

       cin>>kai;

       switch(kai)

       {

           case 1:cout<<"输入学号 姓名 三科成绩 手机号 地址"<<endl;

   cin>>id>>name1>>x>>y>>z>>phone>>where;

   stu[n].putin(id,name1,x,y,z,phone,where);n++;break ;

   case 2:cout<<"putin theid:";cin>>n;stu[n].change();stu[n].disAll();system("pause");break;//未做查找函数

   case3:for(int i=1;i<=n;i++)stu[i].disAll();system("pause");break;

   default :break;

       }

    }

 

   //cout<<"输入姓名 三科成绩 手机号 地址"<<endl;

   //cin>>name1>>x>>y>>z>>phone>>where;

   //s.putin(name1,x,y,z,phone,where);

   //s.disAll();//getchar();

   //cout<<"该成绩输入1 ";

   //cin>>bi;

   //s.change();

   //s.disAll();

}

猜你喜欢

转载自blog.csdn.net/qq_41549459/article/details/80061725