slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
C++ 基础 —— 类和对象基本概念 PowerPoint Presentation
Download Presentation
C++ 基础 —— 类和对象基本概念

Loading in 2 Seconds...

play fullscreen
1 / 55

C++ 基础 —— 类和对象基本概念 - PowerPoint PPT Presentation


  • 237 Views
  • Uploaded on

C++ 基础 —— 类和对象基本概念. 类的概念、对象的概念 构造函数、析构函数、 拷贝构造函数、成员函数及其重载 this 指针 类的静态成员 友元. 1. 类与对象的基本概念. 类是 对象 的 抽象及描述 ,它是具有 统一属性 和 方法 的一类对象的统一描述体,是用来定义某类对象 共有属性 和 方法 的模板。 类是用来 创建对象 实例的模板,它包含了该类对象的 属性 声明和 方法 定义。类是一个型,而对象则是这个型的一个实例。 在面向对象程序中, 类 是 静态 概念,而 对象 则是一个 动态 概念,因为只有在运行时才为对象分配空间,对象才真正存在. 类的定义.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

C++ 基础 —— 类和对象基本概念


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide1
C++基础——类和对象基本概念

类的概念、对象的概念

构造函数、析构函数、 拷贝构造函数、成员函数及其重载

this指针

类的静态成员

友元

1

slide2
类与对象的基本概念

类是对象的抽象及描述,它是具有统一属性和方法的一类对象的统一描述体,是用来定义某类对象共有属性和方法的模板。

类是用来创建对象实例的模板,它包含了该类对象的属性声明和方法定义。类是一个型,而对象则是这个型的一个实例。

在面向对象程序中,类是静态概念,而对象则是一个动态概念,因为只有在运行时才为对象分配空间,对象才真正存在

slide3
类的定义
  • 假设一个学生有四门课程成绩和平均成绩,有学号、姓名等属性。在标准C中,将学生作为一种复杂数据时,采用结构描述,则其存储结构为:

struct Student

{

int Number;

char Name[20];

double Score[5];

};

slide4

标准C的结构存在的问题是:针对数据的操作和数据没有明确的联系,它们是相对独立的。标准C的结构存在的问题是:针对数据的操作和数据没有明确的联系,它们是相对独立的。

  • 所以,在加工数据时,必须将数据的实例作为参数传递给操作函数,数据是被动的。
  • 从应用的角度来看,数据应该是主动的,而操作是作为主体的数据主动发起的,也就是说,操作是被动的。

数据处理方法的描述为:

void InitStudent(Student *p,int num, char *name,double *score);

//对学生数据类型进行初始化

void Stu_Average(Student *p);//求学生的课程平均分

void Modify_Score(Student *p) ;//修改学生的课程成绩

void Print_Stu(Student *p) ;//输出学生的课程成绩单

……

slide5
类的定义

C++的解决方案是:将操作归入数据结构中。

struct Student{

int Number;

char Name[20];

double Score[5];

void InitStudent(Student *p,int num, char *name,double *score);

void Stu_Average(Student *p);

void Modify_Score(Student *p) ;

void Print_Stu(Student *p) ;

……

};

slide6

经过这样的改造后,数据成为能动的主体,它不仅包含解决问题的数据,同时也包含加工数据的操作,从而数据与操作紧密地结合在一起。经过这样的改造后,数据成为能动的主体,它不仅包含解决问题的数据,同时也包含加工数据的操作,从而数据与操作紧密地结合在一起。

  • 这种数据结构真正地成为独立的单元。
  • 另外一方面,数据结构内部的变化不会反射到它的外部。这就是说,数据内部结构的改变不会对使用数据结构的程序带来影响。

这就是数据封装的一般概念。

slide7

一般地,C++是用类来实现封装机制的:

class className

{

attributes; // data members;

operations; // member functions;

};//注意这个分号的存在

定义了一个新的数据类型:className

className是类的名字,也是该类型的类型名。

slide8
类的定义

C++语言提供了三种不同的信息隐藏程度,分别用三种不同的关键字private、protected和public表示外界对它们的访问程度。

可以被外部访问的部分被定义在public段中。这些成员被称为公有成员。

不能被外部访问的部分被定义在private段中,称为私有成员。

保护(protected)成员只能被该类的成员或派生类的成员访问,类外不可访问,但是派生类可以访问其父类的保护段成员。

slide9

私有段

保护段

公有段

接口

数据封装是相对的概念,只是对于类外而言;

而对于类内部,所有的成员都是相互可见的。

9

slide10
类的定义

较小的函数可在类内部直接给出其实现,自动成为内联函数

较复杂的函数可以在类外给出定义,使得类的结构更加清晰

在类外定义成员函数时,必须加上::作用域运算符,用来说明该函数属于哪个类所有,其一般形式如下:

Datatype class_name::func_name(<arg_list>)

{

func_body;

}

10

slide11

class Person

{

private:

char Name[20], ID[19];

unsigned Age;

char Sex;

public:

void Register(char *, char *, unsigned,char);

void GetName(char *);

void GetID(char *);

unsigned GetAge(void){return Age;}

char Getsex(void){return Sex ? 'M':'F';}

……

};

slide12

void Person::Register( char *name, char *id, unsigned age, char sex)

{ strcpy(Name, name);

strcpy(ID, id);

Age = age;

if(sex == 'M' || sex == 'm') Sex = 1;

else if(sex == 'F' || sex == 'f') Sex = 0;

else{cout<<"The sex should be M or F!"<<endl; exit(0); }

}

void Person::GetName(char *name){ strcpy(name, Name);}

void Person::GetID(char *id){ strcpy(id, ID);}

slide13

类只是一种形式化定义。要使用类提供的功能,必须使用类的实例——对象。类只是一种形式化定义。要使用类提供的功能,必须使用类的实例——对象。

  • 对象要占据一定的内存空间。
  • 类和对象的关系就像整型和变量的关系。

int i;

Person d;

    • 变量i和d分别是数据类型int和Person的对象,占据一定的内存空间;
    • int或Person是数据类型,是对整数和人的抽象描述,并不分配内存空间。
slide14
类与结构

C++的class是C语言的struct的扩展

在标准C中,结构仅有数据成员,没有成员函数

在C++中,结构可以有成员函数,与类的功能类似

结构的成员默认是公有的,而类的默认成员是私有的

一般说来,在使用struct类型时,为了与标准C的struct兼容,仅定义数据成员,不定义成员函数。

slide15
构造函数

定义普通变量时,可以进行初始化,例如:

int a = 10, b = a;

/*这里用常量10初始化变量a,用变量a的值初始化变量b*/

一个类的对象是否也可以初始化呢?

C++为类设计了构造函数(constructor)机制,它可以达到初始化数据成员的目的。

15

slide16

类的构造函数是类的一个特殊成员函数,它没有返回类型(void也不行),可以有参数,函数名和类名一样。类的构造函数是类的一个特殊成员函数,它没有返回类型(void也不行),可以有参数,函数名和类名一样。

    • 当创建类的一个新对象时,自动调用构造函数,完成初始化工作(需要注意构造函数是否有参数,以及参数的个数、类型)。
  • 构造函数的作用为:
    • 分配一个对象的数据成员的存储空间;
    • 执行构造函数(体),一般是初始化一个对象的部分或全体数据成员。

16

slide17

构造函数的参数可以是缺省的

  • 定义类对象时给构造函数提供参数:

1)仅仅只有一个参数:

类名 对象名 = 参数;

2)有一个或多个参数:

类名 对象名(参数列表);

  • 构造函数的定义例:

class Date

{

private:

int year, month, day;

public:

Date(int mon, int day, int y=2011);

};

17

slide18

构造函数有两种方式初始化数据成员:

1)在构造函数体内用赋值语句的方式

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

{ year = y;

month = m;

day = d;

}

2)构造函数的初始化列表的方式

Date::Date(int y,int m,int d):year(y), month(m),day(d)

{}

18

slide19

缺省的构造函数

用户定义的类类型中,可以没有构造函数。

编译器会自动给该类类型生成一个没有参数的构造函数,该函数不作任何初始化工作。

这种构造函数称为缺省的构造函数。

注意: 一个类如果有显式定义的构造函数,编译器就不会生成缺省构造函数了。

  • 重载构造函数

一个类可以提供多个构造函数,用于在不同场合进行类对象的初始化工作。

构造函数的重载,它们的参数表必须互不相同。

19

slide20

class Date { int year, month, day;

public:

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

Date(int d, int m);

Date(int d);

Date(const char * dateStr);

…};

Date today(8, 11, 2011);

Date day1(9, 5);

Date day2(8);

Date Christmas(“Dec 25, 2008”);

Date now; // 错,无缺省构造函数

20

slide21
析构函数
  • 与构造函数对应的是析构函数。C++通过析构函数来处理对象的善后工作。
  • 析构函数没有返回类型,没有参数,函数名是类名前加“~”。
  • 析构函数的作用为:

(1) 执行析构函数(一般没有具体的工作);

(2) 释放对象的存储空间(该功能由系统自动完成)注:析构函数需要负责释放new申请的空间。

  • 可以显式地调用析构函数;若没有显式调用,则在一个对象的作用域结束时,系统自动调用析构函数。
  • 系统自动调用构造函数和自动调用析构函数的顺序是相反的。

21

slide22

例:设计一个数组类,数组的大小在定义时初始化,而且其大小在运行时可以改变。例:设计一个数组类,数组的大小在定义时初始化,而且其大小在运行时可以改变。

class Array

{

int * p; int size;

public:

Array(int num)

{

size=num;

p=new int[size];

}

~Array( ) {delete [] p; }

...};

slide23
拷贝构造函数

拷贝构造函数定义

构造函数的参数可以是任何类型参数,甚至可以将自己类对象的(常量)引用作为参数,称它为拷贝构造函数。

拷贝构造函数有两个含义:

首先,它是一个构造函数,当创建一个新对象时,系统自动调用它;

其次,它将一个已经定义过的对象(参数代表的对象)的数据成员逐一对应地复制给新对象。

23

slide24
拷贝构造函数

拷贝构造函数的用途:

  • 创建一个新对象,并将一个已存在的对象复制到这个新对象
  • 对象本身做参数(将实参对象复制给形参对象)
  • 函数返回对象(复制返回的对象给一个临时对象)

如果一个类没有显式定义拷贝构造函数,C++编译器可以为该类产生一个缺省的拷贝构造函数。

缺省的拷贝构造函数,也将复制对象的各个数据成员复制给被复制对象的各个数据成员。

这样一来,两个对象的内存映像是一模一样的。

24

slide25

void main( )

{

A obj1(10,20); //调用A(int,int)

A obj2(obj1); //调用A(const A &);

A obj3 = obj2; //调用A(const A &);

}

class A{

int x;int y;

public:

A(int,int); //一般构造函数

A(constA &objA) // 拷贝构造函数

{x=objA.x;y=objA.y;}

};

25

slide26

class student{

private:

char *name;

float score;

public:

student(char *na, float sc);

~student();

};

student::student(char *na,float sc){

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

if(name!=NULL) {

strcpy(name, na);

score=sc;

}

}

student::~student( )

{

cout<<"析构函数运行,学生"<<name<<"占用的内存释放!"<<endl;

delete name;

}

void main(void)

{

student s1(“Tom", 83.5);

student s2(s1);

}

slide28

student::student(const student &w)

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

if(name!=NULL)

{

strcpy(name, w.name);

score = w.score;

}

}

slide29
类的对象

可以创建不同形式的类对象:

  • 命名的自动对象:

每次进入该对象的作用域,都调用构造函数;

每次退出该对象的作用域,都调用析构函数。

  • 自由对象(动态对象)

使用new创建对象(实际上调用构造函数);

使用delete释放对象(实际上调用析构函数);

当delete释放对象后,该对象就不能再被使用。

如果构造函数有参数,也必须给出实参。

slide30

class Table {…};

void f(int a)

{

Table aa; //aa的生命期到f函数返回

Table bb; //bb的生命期到f函数返回

if (a>0) {

Table cc; //cc的生命期到if语句结束

… }

Table dd; //dd的生命期到f函数返回

};

//若调用函数f(10),

则调用构造函数的顺序是:aa、bb、cc、dd

调用析构函数的顺序是:cc、dd、bb、aa

slide31

对象的初始化:

  • 对象的初始化有许多表示法,C++语言允许下述三种表示方法。
  • C风格的初始值表的方法
  • 赋值表达式的方法
  • 表达式表的方法
    • Class_Name Object(…);
    • Class_Name Object = …;
slide32
this指针

C++为所有非静态成员函数提供了一个称为this的指针,因此,常常称成员函数拥有this指针。

  • this是一个隐含的指针,不能被显式声明
  • 它只是一个形参,一个局部变量
  • 在任何一个非静态成员函数里都存在
  • 它局部于某一对象。
  • this指针是一个常指针,可以表示为(但不能显式声明): X * const this;
  • 可以使用const将this声明为指向常量的常指针。
  • this 指针主要用在运算符重载、自引用等场合。
slide33

void main( ) {

INT mainObj;

mainObj.set(1);

mainObj.out( );

mainObj.fun(mainObj);

mainObj.out( ); }

则程序输出:

1 10 20 30 40 50 60 40

class INT

{ int num;

public:

void set(int x) { num=x;}

void out( ) {cout<<num<<‘ ’;}

void fun( INT obj) {

INT funObj;

num=10; obj.num=20; funObj.num=30;

out( ); obj.out( ); funObj.out( );

set(40);obj.set(50); funObj.set(60);

out( ); obj.out( ); funObj.out( );

}

}; //类定义结束

slide34
类的静态成员

一个类的例子:

class Circle {

private:

int x, y; //圆心的X、Y坐标

float fRadius; //半径

public:

void SetXY(int a,int b);

void SetRadius(float r);

void Move(int newx,int newy);

… //其它成员 };

slide35

C1

C2

C3

x

x

x

y

y

y

fRadius

fRadius

fRadius

SetXY()

SetRadius()

Move()

Circle c1, c2, c3;

类Circle的三个对象及其存储空间:

slide36

静态数据成员:该数据成员为该类的所有对象所共有。静态数据成员:该数据成员为该类的所有对象所共有。

在数据成员的说明前面加上关键字“static”,则该成员成为静态数据成员。

静态数据成员被该类的所有对象共享。无论建立多少个该类的对象,都只有一个静态数据的存储空间。

静态数据成员属于类,而不属于对象。

静态函数成员:成员函数也能被说明为静态的。

静态数据成员一般通过静态成员函数来访问。

slide37

假设类ClassA包括一个普通数据成员x和一个静态成员static_x,假设定义了ClassA的三个对象:a,b,c,则其存储空间示意图为:假设类ClassA包括一个普通数据成员x和一个静态成员static_x,假设定义了ClassA的三个对象:a,b,c,则其存储空间示意图为:

slide38

静态数据成员的存储空间必须在类外定义,并初始化,具体的语法如下:静态数据成员的存储空间必须在类外定义,并初始化,具体的语法如下:

类型名 类名::静态数据成员 [ = 常量表达式 ];

  • 静态数据成员也分为公有和私有的,公有的静态数据成员访问方式为:className::staticMember
  • 与静态数据成员一样,静态成员函数属于类而不是某个类对象(没有this指针)。 因此,在类外调用一个公有静态成员函数,不需要指明对象或指向对象的指针。其访问方式为:

类名::静态公有成员函数名(参数列表)

slide39

例:仓库管理中,可以使用static数据表示总重量。例:仓库管理中,可以使用static数据表示总重量。

#include <iostream>

using namespace std;

class Goods {

private:

float fWeight;

static float fTotalWeight;

public:

void AddGoods(float weight)

{ fWeight = weight;

fTotalWeight += weight;

}

void RemoveGoods( )

{ fTotalWeight -= fWeight; }

void ShowWeight( )

{ cout << fWeight << endl; }

slide40

void ShowTotalWeight( ) // static void ShowTotalWeight( )

{ cout << fTotalWeight << endl; }

};//类定义结束

float Goods::fTotalWeight = 0.0;

//static 成员的定义及初始化

void main( )

{ Goods goods1, goods2, goods3;

goods1.AddGoods(100);

goods2.AddGoods(70);

goods3.AddGoods(150);

goods2.ShowWeight( );

goods2.ShowTotalWeight( );

goods1.ShowTotalWeight( );

//Goods::ShowTotalWeight( );

}

输出结果为?

slide41

class Point{

private:

int x, y;

static int nPtCount;

public:

Point(int intx = 0, int inty = 0);

~Point( );

//Point(const Point &p);

static void PrintCount(const char *msg);

};

统计一个类在程序运行过程中一共实例化出多少个对象。可以在类中加入一个静态变量,并在该类的构造函数中自增该变量,然后在析构函数中减计数。

slide42

Point::Point(int intx = 0, int inty = 0)

{ x = intx; y = inty;

nPtCount++;

PrintCount("In constructor...");

}

Point:: ~Point( )

{ nPtCount--;

PrintCount("After object destroyed...");

}

/* Point::Point(const Point &p)

{ x = p.x; y = p.y;

nPtCount++;

PrintCount("In Copy constructor...");

}*/

slide43

static void Point::PrintCount(const char *msg)

{

cout << msg << endl;

cout <<"Point count = "<<nPtCount << endl;

}

int Point::nPtCount = 0;

Point fun(Point p)

{ Point::PrintCount("In fun()..."); return p; }

void main( )

{ Point pt1, pt2;

pt2 = fun(pt1); // pt2 = pt1;

pt2.PrintCount("After point2 created...");

}

slide44
友元
  • 一个对象的私有数据,只能通过成员函数进行访问,这是一堵不透明的墙。
  • 这种限制性的用法给两个类必须共享同一函数的情形带来了较大的开销。
  • 出于效率(而非技术上必须)考虑,C++提供了一种辅助手段,允许外面的类或函数去访问一个类的私有数据。
  • 类X的友元可以是一个函数(或是一个类)

它不是X的成员函数,

但能访问X的私有成员和保护段的成员。

slide45

class INTEGER

{ private:

int num;

public:

void set(int n){ num=n;}

};

void Print( INTEGER obj)

{ cout<<obj.num;} // 错误

void mian( )

{ INTEGER INTobj;

INTobj.set(100);

Print(INTobj);

…}

slide46

class INTEGER

{

private: int num;

public: void set(int n){ num=n;}

friend void Print(INTEGER ); //友元函数

};

void Print( INTEGER obj)

{ cout<<obj.num;} // ok

void mian( )

{ INTEGER INTobj;

INTobj.set(100);

Print(INTobj);

…}

slide47

友元类

class Printer;

class INTEGER {

private:

int num1;

   friend Printer; //友元类

… };

//类Printer的成员函数全都是类INTEGER的友元函数;

可以访问类INTEGER的任何成员。

友元函数

友元函数不属于类,友元函数没有this 指针

这是友元函数与成员函数的主要区别。

友元函数的声明可以放在类的任何段里。

slide48

友元具有如下特性:

非传递性。即A是B的友元,B是C的友元,但A不一定是C的友元(除非将A声明 为C的友元);

非对称性。即A是B的友元,但B不一定是A的友元(除非将B声明为A的友元)。

友元是C++提供的一种破坏数据封装和数据隐藏的机制。为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。

slide49
与类和对象相关的问题

1 、类类型做参数类型

  • 由于类是一个数据类型,可以将对象作为参数传递给函数
  • 参数传递遵循传值(或传地址)的方式,这同所有其他的数据类型是相同的。
  • 类类型做形参类型,一般有3种方式:
  • 对象本身做参数(传对象值)
  • 对象引用做参数(传地址)
  • 对象指针做参数(传指针值)

49

slide50

class OBJ

{ int num;

public:

void set_num(int x) {num=x;}

void out_num( ) { cout<<num<<‘ ’;}

};

void fun(OBJ objx)

{ objx.out_num( ); objx.set_num(100);

objx.out_num( ); }

void main( )

{ OBJ obj;

obj.set_num(10);

fun(obj); obj.out_num( );

} 结果为 10 100 10

slide51

class OBJ

{ int num;

public:

void set_num(int x) {num=x;}

void out_num( ) { cout<<num<<‘ ’;}

};

void fun(OBJ &objx)

{ objx.out_num( ); objx.set_num(100);

objx.out_num( ); }

void main( )

{ OBJ obj;

obj.set_num(10);

fun(obj); obj.out_num( );

} 结果为 10 100 100

slide52

class OBJ

{ int num;

public:

void set_num(int x) {num=x;}

void out_num( ) { cout<<num<<‘ ’;}

};

void fun(OBJ *objp)

{ objp->out_num( ); objp->set_num(100);

objp->out_num( ); }

void main( )

{ OBJ obj;

obj.set_num(10);

fun(&obj); obj.out_num( );

} 结果为 10 100 100

slide53

2 、一个类的对象作为另一个类的成员

  • 一个类的对象可以作为另一个类的数据成员,简称为对象作为成员,实现的是整体和部分之间的关系(a part of),即对象的包含关系,有时也称为类的“组合”(composition)。
  • 可以通过

外部对象名.内部对象名.内部对象公有成员

的方式来直接访问内部对象的公有成员。

  • 一个复合类在构造的时候,首先调用的是内部类对象的构造函数;再调用外部类的构造函数。
  • 复合类对象析构的时候,析构函数的调用顺序正好与构造是相反。
slide54

例:考虑点和圆的关系。

圆的属性包括圆心和半径,而圆心就是一个点。

class Point { int x, y;

public: Point(int x1,int y1) { x=x1; y=y1 ; }

void setxy(…); …};

class Circle { float r; Point circle_point;

public:

Circle(float r1, int a, int b) :circle_point(a,b)

{r=r1;}

void set_point(…) { circle_point.setxy(…); }

… };

void main( )

{Circle cobj(5.6, 1, 2);

cobj. set_point(…);…}

slide55

3 、临时对象

  • 当函数的返回类型为类类型时,将调用拷贝构造函数将返回的对象保存到那个临时对象中。
  • 另外,在类对象的运算中,也可能会产生临时对象。
  • 临时对象也可以由显示构造函数的调用来创建。

4、指向对象的指针

  • 在C++中,可以直接使用对象,也可通过指向对象的指针变量来使用对象。
  • 说明指向对象的指针变量的语法与说明指向其他任何数据类型的指针变量的语法相同。
  • 可以通过:指向对象的指针变量->类的公有成员

的方式在类外访问一个类的成员。