660 likes | 821 Views
高级语言程序设计 第三章 C ++语言基础-对象. 周律. 教学目标. 类对象 构造函数 析构函数 拷贝构造函数 指针的概念 指向类成员的指针 指向类静态成员的指针 对象指针、 This 指针、常指针 等 对象的其它知识:常类型,子对象,动态内存的分配. 类对象. 为了使用类,还必须说明类的对象。在定义类时,系统是不会给类分配存储空间的,只有定义类对象时才会给对象分配相应的内存空间。 对象的定义格式: < 类名 > < 对象名表 > ;
E N D
高级语言程序设计第三章 C++语言基础-对象 周律
教学目标 • 类对象 • 构造函数 • 析构函数 • 拷贝构造函数 • 指针的概念 • 指向类成员的指针 • 指向类静态成员的指针 • 对象指针、This指针、常指针等 • 对象的其它知识:常类型,子对象,动态内存的分配
类对象 为了使用类,还必须说明类的对象。在定义类时,系统是不会给类分配存储空间的,只有定义类对象时才会给对象分配相应的内存空间。 对象的定义格式: <类名> <对象名表>; 其中,<类名>是待定的对象所属的类的名字,即所定义的对象是该类的对象。<对象名表>中可以有一个或多个对象名,多个对象名用逗号分隔。在<对象名>中,可以是一般的对象名,还可以是指向对象的指针名或引用名,也可以是对象数组名。
对象成员 对象成员的表示方法 一个对象的成员就是该对象的类所定义的成员。对象成员有数据成员和成员函数。一般对象的成员表示如下: <对象名>.<成员名> 或者 <对象名>.<成员名>(<参数表>) 前者用于表示数据成员,后者用于表示成员函数。这里的“.”是一个运算符,该运算符的功能是表示对象的成员。
示例 #include <iostream.h> class myclass { public: int data1; }; void main() { myclass object1; object1.data1 = 25; cout << "\n data1的值是" << object1.data1<<"\n"; }
对象指针的成员 指向对象的指针的成员表示如下: <对象指针名>-><成员名> 或者 <对象指针名>-><成员名>(<参数表>) 同样,前者用于表示数据成员,后者用于表示成员函数。这里的“->”是一个表示成员的运算符,它与前面介绍过的“.”运算符的区别是:“->”用来表示指向对象的指针的成员,而“.”用来表示一般对象的成员。 对于数据成员和成员函数,以下两种表示方式是等价的: <对象指针名>-><成员名> 与 (*<对象指针名>).<成员名>
示例 #include <iostream.h> class myclass { public: int data1; }; void main() { myclass objact1; myclass *pObject1 = &objact1; pObject1->data1 = 25; cout << "\n data1的值是" <<pObject1->data1<<"\n"; }
构造函数 • 自动初始化对象的特殊的成员函数 • 与所属的类同名 • 可以在类中声明并定义构造函数,或在类中声明而在外部定义 • 构造函数没有返回类型 • 默认构造函数是不带任何参数的构造函数
示例1 class IMTI { public: IMTI(); //构造函数 }; IMTI() :: IMTI()() { }
示例2 class date{ int month, day, year; public: date() //默认构造函数 {day=1; month=1; year=1999;} date(int x) //仅指定日 {day=x; month=1; year=1999;} date(int x, int y, int z) //指定年月日 {day=x; month=y; year=z;} };
缺省构造函数 缺省构造函数是指不需要用户指定实参就能够被调用的构造函数,这并不意味着它不能接受实参。只意味着构造函数的每个参数都有一个缺省值与之关联。如: • Account::Account() { ... } • Stack::Stack( int size = 0 ) { ... } • Complex::Complex(double re=0.0,double im=0.0) { ... }
构造函数 编译器根据参数调用不同的构造函数 date now; date today(4); date all(23,3,1998);
析构函数 • 在对象销毁时自动调用的成员函数 • 编译器会生成对析构函数的调用 • 与类同名,但是在函数名前有个波浪号 (~) • 析构函数没有返回类型,也不带参数 • 一个类中只可能定义一个析构函数。
示例1 class imti { public: ~ imti(); //析构函数 }; IMTI() :: ~IMTI()() { }
示例2 #include<iostream.h> class Sample { public: Sample() {x = 0;} ~Sample() { cout<<"析构函数被调用!"<<endl; } int x; void Print() { cout<<"x = "<<x<<endl; } }; void main() { Sample s1; s1.Print(); s1.~Sample(); }
缺省析构函数 缺省析构函数的格式如下: • <类名>::~<缺省析构函数名>{ } • <缺省构析函数名>即为该类的类名。缺省析构函数是一个空函数
拷贝构造函数 在下列情况,会调用拷贝构造函数 • 把某个对象初始化为类的另一个对象时 • 将对象当作参数传递给函数时 • 函数返回对象时
拷贝构造函数 • 如果没有定义拷贝构造函数,编译器将自动生成一个 • 自动生成的拷贝构造函数只是将对象内容逐个字节地copy • 当数据成员有指针时,将导致两个对象的指针指向同一个地址
拷贝构造函数 定义 以对象的引用作为参数的构造函数 X::X(X &ptr) 或 X::X(const X &ptr) 使用 const 关键字,可以确保拷贝过程不会意外地更改源对象
示例1 #include<iostream.h> class TPoint { public: TPoint(int x, int y){ m_x = x; m_y = y;} TPoint(TPoint & p); ~TPoint(){cout<<"Destructor Called!\n";} int Xcoord() {return m_x;} int Ycoord() {return m_y;} private: int m_x, m_y; };
示例1(续) TPoint::TPoint(TPoint & p) { m_x = p. m_x; m_y = p. m_y; cout<<"Copy-initialization Constructor Called.\n,"; } void main() { TPoint p1(5, 7); TPoint p2(p1); cout<<"p2 = "<<p2.Xcoord()<<","<<p2.Ycoord()<<endl; }
示例2 #include<iostream.h> class TPoint { public: TPoint(int x, int y){ m_x = x; m_y = y; cout<<"now is beginning"<<endl; } TPoint(TPoint & p); ~TPoint(){cout<<"Destructor Called!\n";} int Xcoord() {return m_x;} int Ycoord() {return m_y;} private: int m_x, m_y; };
示例2(续) TPoint::TPoint(TPoint & p) { m_x = p. m_x; m_y = p. m_y; cout<<"Copy-initialization Constructor Called.\n,"; } TPoint F(TPoint q) { cout<<"ok\n"; int x, y; x = q.Xcoord() + 10; y = q.Ycoord() + 20; TPoint R(x, y); return R; } void main() { TPoint a(20, 35), b(0, 0); TPoint n(a); b=F(n); cout<<"b="<<b.Xcoord()<<","<<b.Ycoord()<<endl; }
内存 0 …... 2000 2001 2002 2003 2005 …... 指针的概念 内存中每个字节有一个编号-----地址 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象
…... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2005 变量地址(指针) 指针变量 2006 地址存入 指针变量 指向 …... 变量值 变量 指针的概念 • 指针:一个变量的地址 • 指针变量:专门存放变量地址的变量叫~ 指针 变量的内容 变量的地址 2000 指针变量
*i_pointer i_pointer 10 2000 &i_pointer i …... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2000 指针变量 2005 2006 …... 指针的概念 含义:取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 • 两者关系:互为逆运算 • 理解 &与*运算符 含义 i_pointer &i &(*i_pointer) i *i_pointer *(&i) i_pointer=&i=&(*i_pointer) i=*i_pointer =*(&i) i_pointer-----指针变量,它的内容是地址量 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址
面向对象元素与指针 指向类成员的指针 : • 指向数据成员的指针: <类型说明符><类名>::*<指针名>
示例 #include <iostream.h> class Test { private: int a; public: Test(int i) {a = i;} int c; int fun(int b){return a * c + b;} }; void main(){ int Test::*pc = &Test::c; Test object1(1); object1.*pc = 8; cout<<"object1.c is"<<object1.c<<endl; }
静态类成员指针 • 静态类成员是属于该类的全局对象和函数。 • 指向静态成员的指针,声明的方式和普通指针完全一样,只是赋值的时候,还得加上类的限定符:
示例 #include<iostream.h> class myclass { public: // 构造函数,每定义一个对象, 静态数据成员i加1 myclass() { ++i; } static int i; // 声明静态数据成员i }; int myclass::i=0; // 静态数据成员i初始化, 不必在前面加static void main() { int *count=&myclass::i; // 声明一个int型指针,指向类的静态成员 myclass ob1,ob2,ob3,ob4; cout<<"myclass::i= "<<*count<<endl; // 通过指针直接访问静态数据成员 }
指向成员函数的指针 • 指向数据成员的指针: <类型说明符>(<类名>::*<指针名>)(<参数表>)
示例 #include <iostream.h> class Test { private: int a; public: Test(int i) {a = i;} int c; int fun(){return a+ c;} }; void main(){ Test object1(1); int (Test::*pfun) (); pfun = &Test::fun; object1.c = 8; int x=1; cout<<"object1.*pfun is"<<(object1.*pfun)()<<endl; }
对象指针 1.用指针访问单个对象成员 声明对象指针的一般语法形式为: 类名* 对象指针名; 当用指向对象的指针来访问对象成员时, 要用“->”操作符。
示例 #include<iostream.h> class exe{ public: void set_a(int a){ x=a; } void show_a(){ cout<<x<<endl; } private: int x; }; int main() { exe ob,*p; // 声明类exe的对象ob和类exe的对象指针p ob.set_a(2); ob.show_a(); // 利用对象名访问对象的成员 p=&ob; // 将对象ob的地址赋给对象指针p p->show_a(); // 利用对象指针访问对象的成员 return 0; }
对象指针 2. 用对象指针访问对象数组 上例的main()改写为: int 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; }
this指针 • This 指针是指向对象本身的指针,它隐含在类成员函数中,用来指向成员函数所属类的正在被操作的对象。 • 编译器会把对象的地址赋给成员函数中的this指针。确保访问到的数据成员属于这个对象 Date::Date(int y,int m,int d) { this -> year = y; this -> month = m; this -> day = d; } 注:静态成员函数没有this 指针
this的用法 class person{ private: int age; public: void display(); }; void Person::display(){ this -> age = 25; // 与age = 25一样 cout << this -> age; // 与cout << age一样 cout << endl; }; int main(){ Person Jack; Jack.display(); return 0; }
对象数组 定义:对象数组是指数组元素为对象的数组,该数组中若干个元素必须是同一个类的若干个对象 。 对象数组定义格式如下: <类名><数组名>[<大小>] 比如:Date dates1[7];
示例 class Date { public: Date(int m, int d, int y); void Printf(); private: int m_month, m_day, m_year; }; Date dates[4]={ Date (4,22,2006),Date (4,23,2006), Date (4,24,2006),Date (4,25,2006)}
指针与数组 对于二维数组,又是如何用指针访问其中的每个数组元素的呢? 例如,定义了一个二维数组a: int a[3][5]; a是以一个3*5的二维数组,它有三行,每一行都有其起始地址。 C++语言规定以a[0],a[1],a[2]分别表示第0行,第1行,第2行的起始地址,即该行第0列元素的地址。注意a[0],a[1],a[2]并不是一个元素,而是一行首地址,正如同一维数组名是数组起始地址一样,a[0]的值等于&a[0][0],a[1]的值等于&a[1][0],a[2]的值等于&a[2][0]。在同一行中是同样的类推方式,比如:a[0]+1的值等于&a[0][1],a[1]+2的值等于&a[1][2],……。
数组指针 数组指针与指针数组 在C++语言中,数组指针就是一个指向数组的指针;指针数组就是其元素为指针的数组。在学习C++语言时,要注意对它们进行区分,不能等同起来。 1. 数组指针 数组指针是一个指向一维数组的指针变量,定义数组指针的格式为: 数据类型 (*指针名)[常量表达式];
数组指针 例如: int (*p)[5]; 上面这个语句定义了一个数组指针p,它指向一个包含5个元素的一维数组,数组元素为整型。注意,*p两侧的圆括号不能省略,它表示p先与星号“*”结合,是指针变量。如果省略了圆括号,即写成*p[5]的形式,由于方括号的优先级比星号高,则p先与方括号[ ]结合,是数组类型,那么语句int *p[5];是定义了一个指针数组。
指向对象数组的指针 指向对象数组的指针定义格式如下: <类名>(*PL)[4]; 其中,PL是指向数组的指针名,该指针是一个指向一维对象数组的指针,该数组中有4个元素,每个元素是指定<类名>的对象
指针数组 2. 指针数组 指针数组就是其元素为指针的数组。它是指针的集合,它的每一个元素都是指针变量,并且它们具有相同的存储类型和指向相同的数据类型。 说明指针数组的语法格式为: 数据类型 *指针数组名[常量表达式]; 其中,数据类型式指数组中各元素指针所指向的类型,同一指针数组中各指针元素指向的类型相同;指针数组名也即数组的首地址,是一个标识符;常量表达式指出这个数组中的元素个数。 例如:下面定义了几个指针数组。 int *p1[6]; float *p2[3][4];
void指针 void指针 一般来说,只能用指向相同类型的指针给另一个指针赋值,而在不同类型的指针之间进行赋值是错误的。比如: int a,b; int *p1=&a,*p2=p1; //正确 而: int a; int *p1=&a; double *p2=p1; //错误 上述语句中的两个指针p1,p2指向的类型不同,因此,除非进行强制类型转换,否则它们之间不能相互赋值。但是void指针是一个特例。
void指针 C++语言允许使用空类型(void)指针,即不指定指针指向一个固定的类型,它的定义格式为: void *p; 表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址。 void指针它可以指向任何类型的C++数据。也就是说,可以用任何类型的指针直接给void指针赋值。不过,如果需要将void指针的值赋给其他类型的指针,则需要进行强制类型转换。比如: int a; int *p1=&a; void *p2=p1; int *p4=(int *)p2;
常类型 常引用 如果在说明引用时用const修饰,则被说明的引用为常引用。 常引用的说明形式如下: const 类型说明符& 引用名 例如: int a=5; const int& b=a; 其中,b是一个常引用,它所引用的对象不允许更改。
实例 #include<iostream.h> int add(const int& i,const int& j); void main() { int a=20; int b=30; cout<<a<<"+"<<b<<"="<<add(a,b)<<endl; } int add(const int& i,const& j) { return i+j; }
常对象 如果在说明对象时用const修饰,则被说明的对象为常对象。 常对象的说明形式如下: 类名 const 对象名[(参数表)]; 或者 const 类名 对象名[(参数表)]; 在定义对象时必须进行初始化,而且不能被更新。