slide1
Download
Skip this Video
Download Presentation
第六章进一步使用成员函数

Loading in 2 Seconds...

play fullscreen
1 / 47

第六章进一步使用成员函数 - PowerPoint PPT Presentation


  • 91 Views
  • Uploaded on

第六章进一步使用成员函数. 静态成员 友元函数 const 对象和 volatile 对象 转换函数 指向类成员的指针 数组和类. 静态成员. 静态成员:使用关键字 static 修饰的类的数据成员和成员函数 格式: static 数据类型 数据成员名; 举例: class MyClass{ public: static int count; // 引用性说明 static void func(); int x; };. 静态数据成员. 特点:.

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

PowerPoint Slideshow about ' 第六章进一步使用成员函数' - nijole


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

第六章进一步使用成员函数

静态成员

友元函数

const对象和volatile对象

转换函数

指向类成员的指针

数组和类

slide2
静态成员
  • 静态成员:使用关键字static修饰的类的数据成员和成员函数
  • 格式:static 数据类型 数据成员名;
  • 举例:
    • class MyClass{

public:

static int count; //引用性说明

static void func();

int x;

};

slide3

静态数据成员

  • 特点:
  • 无论建立多少个类的对象,都只有一个静态数据的拷贝,对多个对象来说,静态数据成员只存储在一个地方,静态数据成员的值对每个对象都一样。如果在某个对象中对静态成员的值进行了更新,则所有对象都存取到更新后的相同的值。
  • 例:Test b,c;
  • b.x=25;
  • cout<<c.x;
  • 在类中仅对静态数据成员进行引用性说明,必须在文件作 用域的某个地方进行定义性说明,且只能说明一次,说明时可以初始化。
  • 例:int Test::x;
  • 静态数据成员属于类,使用 类名::静态数据成员访问
slide4

静态数据成员

  • 静态数据成员可以在程序内部不依赖于任何对象被访问
  • 静态数据成员不能在类中进行初始化。在main()开始前,类的声明后为它提供定义或初始化,而且在数据成员名前面不加static。数据类型 类名::静态数据成员=值
  • 访问静态数据成员的方法:
      • 类名::静态成员名
      • 对象名.静态成员名
      • 对象指针->静态成员名

小结:一般统计总数、平均数用静态成员

slide5

应用举例

class Myclass{

int i;

static int j;

public:

Myclass();

void Print();

~Myclass();

};

int Myclass::j=0;

Myclass::Myclass(){j+=10;}

void Myclass::Print()

{ cout<<j;}

void main()

{ Myclass first,second;

first.Print();

second.Print();}

slide6

例题分析

class CStatic{

public:

CStatic(){val++;}

static int val;

};

int CStatic::val=0;

void main()

{

cout<< “CStatic::val=”<<CStatic::val<<endl;

CStatic cs1;

cout<< “cs1.val=”<<cs1.val<<endl;

CStatic cs2;

cout<< “cs2.val=”<<cs2.val<<endl;

CStatic cs3,cs4;

cout<< “cs1.val=”<<cs1.val<<endl;

cout<< “cs2.val=”<<cs2.val<<endl;

}

Cstatic::val=0

cs1.val=1

cs2.val=2

cs1.val=4

cs2.val=4

slide7

静态成员函数

  • 静态成员函数的作用在建立任何对象前处理静态数据成员
  • 用关键字static声明的成员函数
      • static 返回值类型 成员函数名(参数表);
  • 静态成员函数可以内嵌,也可在类外定义,此时不要用static前缀
  • 静态成员函数不能被说明为虚函数
  • 静态成员函数属于类,使用 类名::静态成员函数 访问
  • 静态成员函数访问的对象
    • 直接引用:
      • 类中的静态数据成员;
      • 全局数据变量;
      • const常量
    • 间接引用:类中的非静态数据成员
slide8

静态成员函数

int ex=6;

const int cx=7;

class MyClass{

public:

static int f_1(MyClass m); //静态成员函数

int x; //非静态数据成员

static int y; //静态数据成员

};

int MyClass::y=8;

int MyClass::f_1(MyClass m)

{

m.x=9;

return y+ex+cx+m.x;

}

slide9

静态成员函数

#include <iostream.h>

class X{

int member_int;

public:

static void func(int i,X *ptr);

};

void X::func(int i,X *ptr)

{ ptr->member_int=i;}

void main()

{

X obj;

X::func(1,&obj);

obj.func(1,&obj);

}

slide10

静态成员函数

无论是公有派生和私有派生,派生类都可以使用基类的静态成员,使用时唯一的要求是,必须用“类名::成员名”代表这个静态成员。

class Base{

public:

static void a();

void b();…};

class Derived:private Base{…};

class DD:public Derived{

void func();…};

void DD::func()

{ Base::a();

a();

b();}

slide11

例题分析

找出程序中的错误

class MyClass{

public:

static void f();

private:

static int Globe;

};

void MyClass::f()

{ Globe=100;}

  • 没有为静态成员变量初始化
slide12

例题分析

找出程序中的错误

class MyClass{

public:

static void f(MyClass my);

private:

int Globe;

};

void MyClass::f()

{ cout<<Globe;}

  • 成员函数f的定义与实现的接口不一致
  • 成员函数的实现中对私有成员Globe的引用不正确,对对象的引用采用间接方式。
slide13

友元函数

设向量类Vector和矩阵类Matrix,向量和矩阵元素都是float类型。在类中各自说明一个公有成员函数elem,用于存取向量和矩阵的私有数据元素V[i]和M[i][j]

float &Vector::elem(int i)

{return V[i];}

float &Matrix::elem(int i,int j)

{return M[i][j];}

设一个矩阵乘向量的函数,因为向量和矩阵的元素都是私有的,必须通过公有成员函数elem访问它们。

Vector multiply(Matrix &m,Vector &v)

{ Vector r;

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

{ r.elem(i)=0;

for(int j=0;j<5;j++)

r.elem(i)+=m.elem(i,j)*v.elem(j);}

return r;}

slide14

友元函数

  • 友元函数:不是当前类的成员函数,而是独立于当前类的外部函数,但它可访问该类的所有对象的成员。

class girl{

char *name;

int age;

public:

girl(char *n,int d)

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

strcpy(name,n);

age=d; }

friend void disp(girl &);

~girl( ) {delete name;}};

void disp(girl &x)

{ cout<<"girl\\'s name is:"<<x.name<<",age:"<<x.age<<"\n";}

void main()

{ girl e("Chen Xingwei",18);

disp(e); }

slide15

友元函数

  • friend 函数名 例:friend void disp(girl &)
  • 友元函数带有一个该类的入口参数 例:void disp(girl &)
  • 访问类的对象的成员时,必须使用对象名
  • 不必在函数名前加“类名::”
  • 友元可以是不属于任何类的一般函数
  • 友元可以是整个的一个类
slide16

友元函数

  • 友元可以是另一个类的成员函数

class two;

class one{

int a;

public:

void f(two &);

};

class two{

int b;

friend void one::f(two &);

};

void f(two &r)

{a=r.b;}

slide17

友元函数

  • 可以把类声明成另一个类的友元

class C2{

friend class C1;

};

  • 友元不具有传递性。例如:X是Y的友元,Y是Z的友元
  • 友元不具有交换性。例如:X是Y的友元
slide18

友元和派生类

  • 派生类的友元可以访问基类的公有和保护成员。当友元访问基类的静态保护成员时,不能使用成员名限定方法。

class one{

protected:

int a;

public:

one(){a=20;}};

class two:public one{

int b;

friend void f(two &t)

{cout<<\'a\'<<t.a<<\'b\'<<t.b;}

public:

two(){b=10;}};

void main()

{ two t1;

f(t1);}

slide19

友元和派生类

  • 友元类的派生类可以访问基类的公有和保护成员,基类必须是派生类的直接基类。因为友元不能继承

class one{

protected:

int a;

public:

friend class two;

};

class two{

public:

void func(one &s){cout<<s.a;}

};

class three:public two{

public:

void seta(){a=10;}

};

slide20

例题分析

找出程序中的错误

class MyClass{

char my_char1;

char my_char2;

public:

friend void SetMember(MyClass &my,char);

};

void SetMember(MyClass &my,char mem1)

{ my.my_char1=mem1;}

void SetMember(MyClass &my,char mem1,char mem2)

{ my.my_char1=mem1;

my.my_char2=mem1;}

void main()

{ MyClass obj;

SetMember(obj,5);

SetMember(obj,7,9);}

const
const对象
  • const对象:在对象前面加上关键字const。该对象的任何数据成员都不能被修改
    • const Date MyBirthday(8,13,1970);
  • const写在成员函数的参数表后面,该成员函数为只读函数。注意只读成员函数不仅在类定义体内说明时需要使用关键字const,在类定义体外定义时也必须使用关键字const。
    • int GetMonth() const{return month;}
  • 一个const对象只能访问const成员函数。不能使用const对象调用非const的普通成员函数
volatile
volatile对象
  • volatile对象:在对象前面加上关键字volatile。volatile变量是程序设计的进程可以随时改变的变量。例如:时钟。
  • 一个volatile对象只能访问volatile成员函数。
slide23

使用带有this指针的成员函数

将关键字const写在函数头后,函数体前,说明函数是一个const成员函数,这时const不是指函数的返回值,而是指函数体中使用的this指针。

class ConstFun{

int val;

public:

ConstFun(int i){val=i;}

int Getval() const;

};

int ConstFun::GetVal() const{return val;}

void main()

{ ConstFun s(98);

cout<<s.Getval()<<endl;}

const ConstFun * const this;

slide24

转换函数

  • 转换函数:类中定义的一个成员函数,形式为:
  • class A{
  • operator type( );
  • };
  • 说明:
  • type是类型名(或类名),或由类型名与类型修饰符*(指针)、[ ](数组)和( )以及const或volatile等组成的表达式。
  • 该函数不能带有参数,也不能指定返回类型,其返回类型是type
  • 当A类的对象作为操作数用在表达式中,该表达式要求操作数应具有type类型时,该转换函数被自动调用,进行类型转换、将类的数据成员类型转换为type类型。
slide25

转换函数

例:class number{

int val;

public:

number(int i){val=i;}

operator int( );};

number::operator int( )

{ return val;}

void main()

{ number n(15);

int i=n;

cout<<i<<endl;

i=i+n;

cout<<i<<endl;

cout<<int(n)<<endl;(cout<<n<<endl;)

}

被解释为i=n.operator int( );

表达式中出现n,自动调用operator int()

slide26

转换函数

  • 转换函数可以被派生类继承

class num:public number{

public:

num(int i):number(i){ }

};

void main( )

{ num n(15);

int i=n;

cout<<i+n<<endl;}

slide27

转换函数

  • 转换函数可以被说明成虚函数

class A{

public:

virtual operator const char *( ){return “A”;}};

class B:public A{

public:

virtual operator const char *( ){return “B”;}};

void main()

{ A *p=new B;

cout<<*p<<endl;}

看到指针表达式,就会自动调用类型为指针类型的类型转换函数

<<运算符的定义中没有对对象的操作,只有:

Bool、char、int、 long 、float、double、(void *)、(const char *)等故需定义类型转换函数

slide28
转换函数
  • 一个类可以定义多个转换函数,只要能够从type中将它们区别开来
  • 需要进行类型转换时,编译器按这些转换函数在类中说明的顺序,选择第一个遇到的匹配最好的转换函数
  • 有两义性时,可以用强制类型转换显式确定一个转换函数。
  • 构造函数提供的类型转换方向与转换函数的类型转换方向相反

number n(15.0):将15.0转换成val所具有的类型

operator int( ):将val转换成int

slide29
指向类成员的指针
  • 指向类成员的指针:可以访问某个特定类的对象中给定类别的任何成员。指向类的成员,而不是指向该类的一个对象中该成员的一个实例。
slide30
指向类数据成员的指针
  • 指向类X中类型为type的数据成员的指针的说明形式:

type X::*pointer;

设类X的数据成员member的类型为type。则:

pointer=&X::member;将该成员的地址存入pointer中。

  • 若访问某个对象中pointer所指向的成员,使用特殊的运算符“.*”和“->*”。
slide31

指向类数据成员的指针

例:class A{

public:

int a,b,c;

void fa(void){ }

void fb(void){ }

};

void main( )

{ int A::*p;

p=&A::b;

A x;

A *px=new A;

x.*p=1;

p=&A::c;

px->*p=2;

px->b=8;}

.*运算符的左操作数必须是类的对象

->*的左操作数是指向类的对象的指针

slide32

指向成员函数的指针

type (X::pointer)(list);

指向X类中参数类型列表为list,返回类型为type的成员函数的指针

void (A::*pfan)(void);

定义一个变量pfan,指向任何无参数和无返回值的A成员函数

pfan=A::fa; 指向类A成员函数fa()的指针pfan,将该函数的地址置给指针pfan

A x;

A *px=new A;

(x.*pfan)(); 指向类A的对象x的成员函数fa

(px->*pfan)(); 指向类A对象指针的成员函数fa

slide33

指向成员函数的指针

#include <iostream.h>

class A{

private:

int val;

public:

A(int i){val=i;}

int value(int a){return val+a;}

};

void main()

{ int(A:: *pfun)(int);

pfun=A::value;

A obj(10);

cout<<(obj.*pfun)(15)<<endl;

A *pc=&obj;

cout<<(pc->*pfun)(15)<<endl;}

slide34

指向成员函数的指针

  • 指向类成员的指针指向一个虚函数,并且通过指向对象的指针(或引用)访问该虚函数,发生多态性。

#include <iostream.h>

class base{

public:

virtual void print(){cout<<“In Base”<<endl;}

};

class derived:public base{

public:

void print(){cout<<“In Derived”<<endl;}

};

void display(base *pb,void(base::*pf)())

{ (pb->*pf)();}

void main()

{ derived d;

display(&d,base::print);}

base::此处的::不是成员名限定,而是指向类的成员函数的指针的一种表示方法。可以读作pf是指向base类的无参数无返回类型的成员函数。如果该成员函数是虚函数,则该指针指向基类还是派生类的同名成员函数由运行时动态决定。

slide35
指向类的静态成员的指针

#include <iostream.h>

class A{

public:

static int num;

};

int A::num;

void main()

{ int *p=&A::num;

*p=56;

cout<<A::num<<endl;

cout<<*p<<endl;

cout<<p<<endl;

A a,b;

cout<<a.num<<endl;

cout<<b.num<<endl;

}

静态成员不需要对象

slide36
数组和类
  • 类对象数组
  • 类对象指针数组
  • 对象数据成员数组
  • 类对象数据成员指针数组
  • 类成员函数指针数组
  • 静态数据成员指针数组
slide37
类对象数组

例:建立类exam的对象数组,将0~3之间的值赋给每一个元素x。

class exam{

int x;

public:

void set_x(int n) {x=n;}

int get_x( ) {return x;}

};

main()

{ exam ob[4]; //定义含4个元素的对象数组,每个对象属exam类

int i;

for(i=0;i<4;i++) ob[i].set_x(i);

for(i=0;i<4;i++) cout<<ob[i].get_x()<<;

cout<<\n ;

return 0;

}

  • 每一数组元素都是对象的数组
slide38

类对象数组

  • 如果类中含有用户定义的构造函数,且构造函数带有参数, 则定义对象数组时,可通过初始值表进行赋值

#include <iostream.h>

class exam{

int x;

public:

exam(int n)

{x=n;}

int get_x()

{return x;}

};

main()

{ exam ob[4]={11,22,33,44};

int i;

for(i=0;i<4;i++) cout<<ob[i].get_x()<<\' \';

cout<<\'\n\';

return 0;

}

slide39

如果类中含有构造函数,则定义对象数组时,也可通过不带参数的构造函数或带有缺省参数的构造函数给对象数组元素赋值如果类中含有构造函数,则定义对象数组时,也可通过不带参数的构造函数或带有缺省参数的构造函数给对象数组元素赋值

调用带1个参数的构造函数

op1[0].x=1,op1[1].x=4

op1[2].x=5,op1[2].y=5

调用带2个参数的构造函数

op1[0].x=2,op1[0].y=3

op1[1].x=3,op1[1].y=4

op2[0].x=5,op2[1].y=5

class point{

int x,y;

public:

point() {x=5;y=5;}

point(int a,int b) {x=a;y=b;}

point(int a) {x=a;}

int get_x() {return x;}

int get_y() {return y;}};

void main()

{ point op(3,4);

point op1[3]={1,4};

point op2[4]={point(2,3),point(3,4)};

}

slide40
类对象数组
  • 完成初始化工作的构造函数不同,说明对象数组的格式有差别
    • 使用缺省构造函数(无参数或全部参数采用缺省值)完成初始化工作,使得组成数组的每个对象都具有相同的初始状态

类名 数组名[数组长度];

    • 使用带参数构造函数初始化数组:必须在说明对象数组的同时给出初值表

类名 数组名[长度]={(初值1…),(初值2…)…};

注意:在初值表中没有对全部的数据成员初始化,则必须定义缺省构造函数

slide41
析构函数和对象数组
  • 当对象数组的生命期结束之后,C++系统为对象数组的每个元素调用一次析构函数。

#include <iostream.h>

class test{

int num;

public:

test(){num=0;}

test(int n){num=n;}

~test(){cout<<"Destructor"<<endl;}

};

void main()

{test a[5];}

slide42

类对象数组

p和ob[0]同一地址

p指向ob[1]

  • 用对象指针引用对象数组

注意:数组名代表第一个元素的地址

class exe{

int x;

public:

void set_a(int a) {x=a;}

void show_a() {cout<<x<<endl;}

};

main()

{ exe ob[2],*p;

ob[0].set_a(10);

ob[1].set_a(20);

p=ob;

p->show_a();

p++;

p->show_a();

return 0;

}

slide43
例题分析

class base{

int x,y;

public:

base(int m,int n){x=m;y=n;}

void print();

void print() const;

};

void base::print()

{cout<<x<<","<<y<<endl;}

void base::print() const

{cout<<x<<","<<y<<" const"<<endl;}

void main()

{base a(55,84);

a.print();

const base b(32,25);

b.print();

}

55,84

32,25 const

slide44

例题分析

class base{

const int a;

static const int b;

public:

base(int i);

void print();

const int &r;};

const int base::b=128;

base::base(int i):a(i),r(a){}

void base::print()

{cout<<a<<","<<b<<","<<r<<endl;}

void main()

{base a1(125),a2(119);

a1.print();

a2.print();}

常量对象的初始化

125,128,125

119,128,119

slide45

例题分析

class test{

int n;

public:

test(int i){n=i;cout<<"constructor is called."<<endl;}

~test(){cout<<"destructor is called."<<endl;}

int getn(){return n;}

void inc(){++n;}

};

void main()

{ static test a(3); //静态类对象

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

{a.inc();

cout<<a.getn()<<endl;

}

cout<<"Exit main()"<<endl;

}

Constructor is called

4

5

6

Exit main()

Destructor is called

slide46

例题分析

class test{

private:

int n;

public:

test(int i){n=i;cout<<"constructor is called."<<endl;}

~test(){cout<<"destructor is called."<<endl;}

int getn(){return n;}

void inc(){++n;}

};

void main()

{ for(int i=0;i<3;i++)

{static test a(3);

a.inc();

cout<<a.getn()<<endl;

}

cout<<"Exit main()"<<endl;

}

  • 静态类对象的构造(析构)函数
  • 构造函数在代码执行过程中,第一次遇到它的变量定义时被调用,但直到整个程序结束之前仅调用一次。
  • 析构函数在整个程序退出前被调用,调用一次。

Constructor is called

4

5

6

Exit main()

Destructor is called

slide47

例题分析

class test{

private:

int n;

public:

test(int i){n=i;cout<<"constructor is called."<<endl;}

~test(){cout<<"destructor is called."<<endl;}

int getn(){return n;}

void inc(){++n;}

};

void main()

{ for(int i=0;i<3;i++)

{ test a(3);

a.inc();

cout<<a.getn()<<endl;

}

cout<<"Exit main()"<<endl;

}

constructor is called.

4

destructor is called.

constructor is called.

4

destructor is called.

constructor is called.

4

destructor is called.

Exit main()

Press any key to continue

  • a定义在for语句中是局部对象,只能存在循环体内,结束循环体本次循环时,需要调用一次析构函数。
ad