C++类与对象初学

一.基本内容总结

(一)基础知识

1.类的定义:

类是对具有相同属性和行为的一组对象的抽象和统一描述。是用户自定义的数据类型。/类的定义包括行为和属性两部分。/属性以数据表示,行为通过函数实现。

C++类定义的说明语句一般形式为:

class<类名>
{
  public:
  公有段数据成员和成员函数;
  protected:
  保护段数据成员和成员函数;
  private:
  私有段数据成员和成员函数;
};

*分号不可掉。

除了class以外,关键字struct也可以用于定义类。用struct定义类时,若不特别指出,则所有成员是共有的。例如,一个日期类的说明如下:

class Date
{   
   public:
     void SwtDate(int y,int m,int d);
     int IsLeapYear();
     void PrintDate();
   private:
     int year,month,day;
};
     

Date类有三个私有数据成员:year,month和day。Date还有三个公有成员函数:SetDate用于获取对象的值,设置日期;IsLeapYear用于判断是否是闰年;PrintDate函数用于输出日期。

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

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

{  

   函数体    }

*注意事项

1.类的成员可以是其他人的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。2.类定义必须以分号“;”结束。3.类与结构体的区别:没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。

2.访问对象成员:

*对象是类的实例或实体;类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。

对象的定义(格式如下):

类名     对象1,对象2,.....,对象名n;

定义对象时应注意,必须在定义了类之后,才可以定义类的对象。

例如,访问对象的公有成员:

#include<iostream>
using namespace std;
class Tclass
{ public:
  int x,y;
  void print()
{ cout<<x<<","<<y;};
};
int main()
{   Tclass test;
    test.x=100;
    test.y=200;
    test.print();
}

*类成员的访问,对象成员的访问包括:

圆点访问形式:对象名.公有成员指针访问形式(对象指针变量名->公有成员

例如:

#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;
};
int main()
{
	 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;
}

3.内联函数:

内联函数作用:减少频繁调用小子程序的运行时间的开销;

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

内联函数声明:inline函数原型;

注:内联函数仅在函数原型做一次声明;适用于只有1-5行的小函数;不能含有复杂结构控制语句,不能递归调用。


(二).简单构造函数和析构函数

1.原型:

类名::类名(参数表);

类名::~类名();

例如,为类Date建立一个构造函数:

#include <iostream.h>
class Date {
    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(int y,int m,int d) 
{ year=y; month=m; day=d; }
inline void Date::showDate()
{  cout<<year<<"."<<month<<"."<<day<<endl; } 

2.带参数的构造函数:

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



3.重载构造函数:

构造函数与普通函数一样,允许重载。如果Date类具有多个构造函数,创建对象时,将根据参数匹配调用其中一个。例如:

class Date
{ public:
     Date();
     Date(int);
     Date(int,int);
     Date(int,int,int);
     ~Date();
    //......
};
//......
void f()
{  Date d;  //调用 Date();
   Date d1(2000);  //调用  Date(int);
   Date d1(2000,1);  //调用  Date(int,int);
   Date d1(2000,1,1);  //调用  Date(int,int,int);
}

像所以C++函数一样,构造函数可以具有默认参数。但是,定义默认参数构造函数时,要注意调用时可能产生二义性。例如:

class X
{ public:
    X();
    X(int i=0);
    //...
};
void f()
{   X one(10);    //正确
    X two;        //错误
}

上述程序在说明X类对象one时,调用构造函数X::X(int i=0);但在说明two时,系统无法判断调用X::X()还是X::X(int i=0),因此会出现匹配二义性。

4.复制构造函数:

创造对象时,有时希望用一个已有的同类型对象的数据对它进行初始化。C++可以完成类对象数据的简单复制。用户自定义的复制构造函数用于完成更为复杂的操作。


复制构造函数要求有一个类类型的引用函数:

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

为了保证所引用的对象不被修改,通常把引用参数说明为const参数。例如:

class A
{  public:
      A(int);
      A(const A&,int=1);  //复制构造函数
  //......
};
//......
A a(1);  //创建对象a,调用A(int)
A b(a,0); //创建对象b,调用A (const A&,int=1)
A c=b; //创建对象c,调用A (const A&,int=1)

第一条语句创建对象a,调用一般的构造函数A(int)。第二条和第三条语句都调用了复制构造函数,复制对象a创建b,复制对象b创建c,展示了调用复制构造函数的两种典型方法。

*调用复制构造函数的时机:

当说明语句建立对象时,可以调用复制构造函数进行数据初始化。另外,当函数具类类型传值数或者函数返回类类型值时,都需要调用复制构造函数,完成局部对象的初始化工作。

如果函数具有类类型传值参数,那么,调用该函数时,首先要调用复制构造函数,用实际参数对象的值初始化形式参数对象。

5.析构函数:

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

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

析构函数自动调用(隐式调用),析构函数没有返回值,不能有参数,也不能重载。

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

类名::~类名()

{      

    函数语句   

      }

*析构函数有以下特点:

1.析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);  2. 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只有一个析构函数;  3.当撤销对象时,编译系统会自动的调用析构函数。

6.this指针:

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

#include <iostream>
using namespace std;
class Time
{
public:
		int hour;
		int minute;
		int sec;
		void setHour( ){	hour=10;}
};
int main( )
{ 	cout<<sizeof(Time)<<endl;	return 0; }

7.深复制和浅复制:

当类的数据成员是简单的数据类型时,创建对象时的数据复制系统机制工作得很好。但是,如果数据成员资源是由指针指示的堆,系统复制对象数据时只进行指针(地址值)复制,而不会重新分配内存空间。这样,程序运行时会产生对象操作异常;另外,当对象作用域结束后,又会错误地重复释放堆。这种情况下,需要使用用户自定义的复制构造函数。

*浅复制:

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

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

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

*深复制:

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

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

(三)类的其他成员

1.常成员:

在类中,定义常成员用const约束。常数据成员是指数据成员在实例化被初始化后约束为只读;常成员函数是指成员函数的this指针被约束为指向常量的常指针,在函数体不能修改数据成员的值。

(1)常数据成员:

在C++类定义中,const可以约束基本类型的数据成员为常数据成员。因为类对象要通过执行构造函数才能建立存储空间,所以,用构造函数实现数据成员值的初始化是必需的。在C++语言中,使用构造函数参数初始化对常数据成员进行初始化。

**数据成员可以在构造函数中直接用常量进行初始化,这样,每个对象建立的常数据成员都有相同的值。

**外一种对常数据成员进行初始化的方法是,使用带参数的构造函数,创建对象时,用实际参数对常数成员赋值。这样,每个对象的常数据成员就可以有不同的初始值。

(2)常对象:

若在定义对象的说明语句以const作前缀,则该对象称为常对象。这个对象的全部数据成员作用域中约束为只读。

(3)常成员函数:

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

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

*const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const;常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数和构造函数除外)。

#include<iostream>
using namespace std ;
class  Simple
{   int  x, y ;
  public :
     void setXY ( int a, int b) { x = a ;  y = b ; }
     void printXY() { cout << x << "," << y << endl ; } 
     void  constFun ( ) const
         { x ++ ; y ++ ; }//非法
};

2.静态成员:

(1).静态数据成员:

静态数据成员在定义或说明时前面加关键字static,如:

class A
{
  	int n;
	static int s;
};

(2).静态成员函数:

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

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

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

3.友元:

*如果在本类(类A)以外的其他地方定义了一个函数(函数B);
*这个函数可以是不属于任何类的非成员函数;
*也可以是其他类的成员函数;
*在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数;
*友元函数(函数B)可以访问这个类(类A)中的私有成员。
*用友元函数计算两点间距离:
#include<iostream>
using namespace std ;
#include<math.h>
class Point
{ public:
      Point(double xi, double yi) { X = xi ; Y = yi ;}
      double GetX() { return X ; }
      double GetY() { return Y ; }
      friend double Distance ( Point & a, Point & b ) ;
  private:  double X, Y ;
} ;
double Distance(Point & a, Point & b )	
  { double dx = a.X - b.X ;
     double dy = a.Y - b.Y ;
     return sqrt ( dx * dx + dy * dy ) ;
  }
int main()
{ Point  p1( 3.0, 5.0 ) ,  p2( 4.0, 6.0 ) ;
  double  d = Distance ( p1, p2 ) ;
  cout << "This distance is " << d << endl ;
} 

4.类的包含:

*类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类“抄”进来;
*当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式;

*构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;

*出现成员对象时,该类的构造函数要包含对象成员的初始化。如果构造函数的成员初始化列表没有对成员对象初始化时,则使用成员对象的无参(缺省构造函数。建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数。

*用类包含计算两点间距离:

#include<cmath>
#include<iostream>
using namespace std;
class  Point 
{ public:
       Point( int xi=0, int yi=0 ) { x = xi; y = yi; }
       int GetX()  {  return x;  }
       int GetY()  {  return y;  }
  private:  int x;  int y;
};
class Distance 
{ public:
      Distance( Point xp1, Point xp2 );
      double GetDis()  {  return dist;  }
  private:
     Point p1, p2;
     double dist;
};

二.例题总结

如下为student类的一个实例,第一次尝试,还有些许错误和待优化的地方:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
class student
{
    string name;
    int no;
    int score[3];
    float average;
    int order;
    public:
    student(int id,string na,int x,int y,int z):name(na),no(id)
    {
    score[0]=x;
    score[1]=y;
    score[2]=z;
    order=1,average=(score[0]+score[1]+score[2])/3;
    }
    student()
    {
        score[0]=score[1]=score[2]=0;
        order=1,average=0;
    }
    int getNo(){return no;}
    float getAverage(){return average;}
    void setAverage(int avg){average=avg;}
    void setOrder(int x){order=x;}
    int getOrder(){return order;}
    string getName(){return name;}
    void setName(string name){this->name=name;}
    void display();
};
void student::display()
{
    cout<<name<<"\t"<<no<<"\t"<<score[0]<<"\t"<<score[1]<<"\t"<<score[2]<<"\t"<<average<<"\t"<<order<<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];
    int n;
    public:
    student_list():n(0){};
    void add();
    void deleteStu();
    void query();
    void change();
    void display(int flag);
    void menu();
    int search(int no);
    void sortList();
};
void student_list::add()
{
    int no,x,y,z;
    string name;
    system("cls");
    cout<<"按学号·姓名·数学·英语·c++顺序输入学生信息,学号输入-1表示结束"<<endl;
    while((cin>>no)&&no!=-1)
    {
        cin>>name>>x>>y>>z;
        student s(no,name,x,y,z);
        list[n++]=s;
        sortList();
    }
}
void student_list::sortList()
{
    sort(list,list+n,cmp1);
    int i;
    for(i=0;i<n;i++)
    list[i].setOrder(i+1);
}
void student_list::display(int flag)
{
    if(flag)sort(list,list+n,cmp2);
    else sort(list,list+n,cmp1);
    cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<"名次"<<endl;
    for(int i=0;i<n;i++)
    list[i].display();
}
int student_list::search(int no)
{
    int i;
    for(i=0;i,n;i++)
    if(list[i].getNo()==no)
    return -1;
}
void student_list::query()
{
    int no,i;
    system("cls");
    cout<<"请输入要查询同学的学号,按-1结束查询";
    while(cin>>no&&no!=-1)
    {
        i=search(no);
        if(i!=-1)
        {
            cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<"名次"<<endl;
            list[i].display();
            cout<<"请输入要查询同学的学号,按-1结束查询";
        }
        else
        cout<<"学号输入有误,请重输,输入-1结束查询"<<endl;
    }
}
int main()
{
    student s(1,"ff",66,77,88);
    s.display();
    cout<<s.getAverage()<<endl;
    s.setName("方法");
    s.display();
    student_list c;
    c.add();
    c.display(1);
    c.query();
    c.display(0);
    c.change();
    c.display(1);
    c.deleteStu();
    c.display(1);
    return 0;
}

三.学习心得

学习本章内容,主要是思维的转变,由之前的面向过程编译到面向对象设计。本章主要是对设计管理系统的初步认知,即通过对方需求来找到系统设计的初步框架,再从框架中的各个分类来细化需求,为需求分类。当然,最重要的是把各类操作与需求转化成数据并加以修饰,这是本章的关键;最后,加以调试和修改,使之具备增添和删减的灵活性,以完成思维的转变。随着学习的深入,最后的目的是对软件的设计,相信会越来越难。

猜你喜欢

转载自blog.csdn.net/fouram_godv/article/details/80038660