类和对象(二)

  • 对象的自身引用是面向对象程序设计语言中特有的、十分重要的一种机制。在C++中,为这种机制设立了专门的表示:this指针变量。
  • 在类的每一个成员函数的形参表中都有一个隐含的指针变量this,该指针变量的类型就是成员函数所属类的类型。
  • 当程序中调用类的成员函数时,this指针变量被自动初始化为发出函数调用的对象的地址。

例如:

123456789101112131415161718
#include <iostream>using namespace std;class Sample{	int x, y;public:	Sample(int a=0, int b=0)	{ x=a; y=b; }	void print()	{ cout<<x<<endl;	 cout<<y<<endl;	}};int main(){ Sample obj(5,10);  obj.print();  return 0;}

上述程序可用this指针显示改写为完全等价的形式:

12345678910111213141516171819
#include <iostream>using namespace std;class Sample{	int x, y;public:	Sample(int a=0, int b=0)	{ this->x=a;                //在此例中this=&obj	  this->y=b; }	void print()	{ cout<<this->x<<endl;     //在此例中this=&obj	 cout<<this->y<<endl;	}};int main(){ Sample obj(5,10);		//当程序中调用类的成员函数时,this指针变量被自动 obj.print();			//初始化为发出函数调用的对象的地址。 return 0;}

那么何时使用this指针呢?
编写代码时主要有两种场合要求尽可能使用this指针

  • 一种是为了区分成员和非成员

例如:

1234
void Sample::fun(int x){  this->x=x; }
  • 一种使用this指针的应用是一个类的方法需要返回当前对象的引用。

例如:

123456789101112131415
class Sample{ private: 	int x;	char* ptr; public:	Sample & Set(int i, char *p);    //..};Sample& Sample::Set(int i, char *p);		//方法Set返回当前对象的引用,*this就是当前对象。{ x=i; ptr=p; return *this;}

对象数组与对象指针

对象数组

所谓对象数组是指每一数组元素都是对象的数组,也就是说,若一个类有若干个对象,我们把这一系列的对象用一个数组来存放。
对象数组的元素是对象,不仅具有数据成员,而且还有函数成员。定义对象数组时,系统为每个数组元素对象调用一次构造函数以构造这些元素。
对象数组的定义的格式为:

1
类名 数组名 [数组大小];

例如:

123
Student stu[3];  定义了类Student的对象数组stu。  系统调用无参构造函数3次。

如果类Student有2个数据成员姓名(char name[10])、年龄(int age),那么在定义对象数组时也可以实现初始化。
例如:

1
Student stu[3]={ Student(“zhao”,22),  Student(“qian”,20),  Student(“sun”,8,90)  };

在建立对象数组时,分别调用构造函数,对每个元素初始化。
与基本数据类型的数组一样,在使用对象数组时也只能访问单个数组元素,也就是一个对象,通过这个对象,也可以访问到它的公有成员,一般形式是:

1
数组名[下标]. 成员名

对象数组的应用:

123456789101112131415161718192021222324
#include <iostream>using namespace std;class Sample{    private:        int x;	public:		void Set_x(int n)			{ x=n; }		int Get_x()			{  return x;  }};

int main(){	Sample obj[4];	int i;	for(i=0; i<4; i++)		obj[i].Set_x(i);	for(i=0; i<4; i++)		cout<<obj[i].Get_x()<<” ”;		cout<<endl;	return 0;}

运行结果为:0 1 2 3

堆对象

使用new运算符动态分配的对象属于堆对象,其所占存储空间被分配在堆区。

利用new建立对象会自动调用构造函数,利用delete删除对象会自动调用析构函数。
堆对象的建立与删除:

1234567891011121314151617181920212223
#include <iostream>using namespace std;class Date				//定义日期类Date{	private:		int month, day,  year;	public:		Date(int m, int d, int y);		//声明构造函数};Date::Date(int m, int d, int y) 	  //定义构造函数{	if (m>0 && m<13)		month=m;	if (d>0 && d<32)      	day=d;  	if (y>0 && y<3000)		year=y;  }int main()         {	Date *pd;		              //定义一个指向Date类的对象的指针变量pd    	pd=new Date(1,1,2008);	  //pd指向新建Date类的对象的起始地址    	//……    	delete(pd);			//释放pd所指向的内存空间		return 0;}

对象指针

指向类对象的指针称为对象指针。

声明指向类对象的指针变量的一般形式为
类名 *对象指针名;
用指针引用单个对象成员的方法与其他基本类型指针相同,可以有两种形式:
指针变量名->成员名

(*指针变量名).成员名
可以通过对象指针访问对象和对象的成员。

例如:

12
Date obj, *pt;	pt=&obj;

定义指向Date类对象obj的指针变量pt。

12345
*pt             //pt所指向的对象,即obj(*pt).year 	 //pt所指向的对象obj中的year成员,即obj.yearpt->year	//pt所指向的对象obj中的year成员,即obj.year(*pt).get_date()   //调用所指向的对象obj中的get_date函数,即obj.getdate()pt->get_date        //调用所指向的对象obj中的get_date函数,即obj.getdate()

有关对象指针的使用方法

12345678910111213141516171819202122232425262728293031323334
#include <iostream>using namespace std;class Date				//定义日期类Date{	private:		int month, day,  year;	public:		Date(int m, int d, int y);		//声明构造函数void showDate();     //声明显示数据成员值的成员函数};Date::Date(int m, int d, int y) 	  //定义构造函数{	if (m>0 && m<13)		month=m;	if (d>0 && d<32)      	day=d;  	if (y>0 && y<3000)		year=y;  }void Date::showDate(){cout<<year<<”.”<<month<<”.”<<day<<endl;}

int main(){Date obj(2008,1,1);  //定义Date类对象objint *pt1=&obj.year;          //定义指向整型数据的指针变量pt1,并使pt1  指向obj.year	cout<<*pt1<<endl;  //输出pt1所指向的数据成员obj.year	obj.showDate();    //调用对象obj的成员函数showDate()	Date *pt2=&obj;                 //定义指向Date类对象的指针变量pt2,并使pt2指向obj	pt2->showDate();  //调用pt2所指向对象obj的showDate()函数	return 0;}

运行结果为:
2008
2008.1.1
2008.1.1

静态成员

为了实现一个类的不同对象之间的数据和函数共享,C++提出了静态成员的概念。静态成员包括:

  • 静态数据成员
  • 静态函数成员

在类的定义中,可以用关键字static声明成员为静态成员,这些静态成员可以在同一个类的不同对象之间提供数据共享。
不管这个类创建了多少个对象,但静态成员只有一份拷贝(副本),为所有属于这个类的对象所共享。

静态数据成员

公有的静态数据成员可以在对象定义之前被访问,形式为:
类名::公有静态成员变量名;
在对象定义后还可以通过对象进行访问,形式为:
对象名.公有静态成员变量名;
私有的静态数据成员不能被类的外部函数访问,也不能用对象进行访问。
静态数据成员的初始化必须在类外进行,默认值为0,它是在编译时创建并初始化,所以它在该类的任何对象被创建前就存在。

静态数据成员的定义与使用示例:

12345678910111213141516171819202122232425
#include<iostream>using namespace std;class Ctype{private:	int a;           	static int s;                //定义私有的静态数据成员spublic:	void print();	Ctype(int x=0);};void Ctype::print(){ cout<<”a=”<<++a<<endl;  //输出普通数据成员 cout<<”s=”<<++s<<endl;  //输出静态数据成员,两者作比较 }Ctype::Ctype(int x){  a=x;}int Ctype::s=0;          //静态数据成员赋初值在类体外进行,前面不能再加staticint main(){  Ctype c1,c2,c3;     //定义3个对象,都使用默认的参数值	c1.print();         	c2.print();	c3.print();	return 0;}

运行结果为:
a=1
s=1
a=1
s=2
a=1
s=3

静态数据成员和一般数据成员的不同

123456789101112131415161718192021222324252627282930313233343536373839404142
#include<iostream>using namespace std;class Student{private:static int count;	//声明静态数据成员count,统计学生的总数int StudentNo;		//普通数据成员,用于表示每个学生的学号public:Student()		//构造函数{++count;		//每创建一个学生对象,学生数加1StudentNo=count;	//给当前学生的学号赋值}void Print()	//成员函数,显示学生的学号和当前学生数{cout<<”Student”<<StudentNo<<”  ”;cout<<”count=”<<count<<endl;}};

int Student::count=0;		//给静态数据成员couny赋初值int main(){Student Student1;		//创建第1个学生对象Student1.print();cout<<"---------"<<endl;Student Student2;	//创建第2个学生对象 Student2Student1.Print();Student2.Print();cout<<"----------"<<endl;Student Student3;		//创建第3个学生对象Student3Student1.Print();Student2.Print();Student3.Print();cout<<"----------"<<endl;;Student Student4;		//创建第4个学生对象Student4Student1.Print();Student2.Print();Student3.Print();Student4.Print();return 0;}

运行结果为:

12345678910111213
Studentl count=1----------Studentl count=2Student2 count=2----------Studentl count=3Student2 count=3Student3 count=3----------Studentl count=4Student2 count=4Student3 count=4Student4 count=4

静态成员函数

在类定义中,声明为static的成员函数能在类的范围内共享,把这样的成员函数称为静态成员函数。
静态成员函数属于整个类,是该类所有对象共享的成员函数,而不属于类中的某个对象。
静态成员函数只能访问静态数据成员,不能对类的其他类型的数据成员或成员函数进行访问,可以通过对象或类名进行调用。

定义静态成员函数的格式为
static返回类型 静态成员函数名(参数表);
与静态数据成员类似,调用公有静态成员函数的一般格式为
类名::静态成员函数名(实参表)
对象.静态成员函数名(实参表)

关于静态成员函数的使用有以下几点说明:

  1. 静态成员函数可以定义成内嵌的,也可以在类外定义,在类外定义时,不用static前缀。
  2. 一般情况下,静态函数成员主要用来访问全局变量或同一个类中的静态数据成员。特别是,当它与静态数据成员一起使用时,达到了对同一个类中对象之间共享数据进行维护的目的。
  3. 私有静态成员函数不能被类外部函数和对象访问。
  4. 使用静态成员函数的一个原因是,可以用它在建立任何对象之前处理静态数据成员,这是普通成员函数不能实现的功能。
  5. 编译系统将静态成员函数限定为内部连接,也就是说,与现行文件相连接的其他文件中的同名函数不会与该函数发生冲突,维护了该函数使用的安全性,这是使用静态成员函数的另一个原因。
  6. 在一般的成员函数中都隐含有一个this指针,用来指向对象自身,而在静态成员函数中是没有this指针的。
  7. 一般而言,静态成员函数不访问类中的非静态成员。若确实需要,静态成员函数只能通过对象名(或指向对象的指针)访问该对象的非静态成员。

友元

类的一个很重要的特点就是实现了数据隐藏和封装。在类定义的时候,一般都将数据成员声明为私有成员(或保护成员),只能在类定义的范围内使用,也就是说私有成员(或保护成员)只能通过它的成员函数来访问。

但是,调用公有函数接口是需要时间开销的,且有时候需要在类的外部访问类的私有成员(或保护成员)。
为此,就需要寻找一种途径,在不放弃私有成员(或保护成员)数据安全性的情况下,使得一个普通函数或者类的成员函数可以访问到封装于某一类中的信息(私有、保护成员) 。

在C++语言中,通过定义友元(friend member)来实现这一功能。类的友元包括友元函数友元类

友元函数

友元函数有2种形式:

  • 一个不属于任何类的普通函数声明为当前类的友元,称为当前类的友元函数。
  • 一个其他类的成员函数声明为当前类的友元,称为当前类的友元成员。

友元函数是在类中说明的由关键字friend修饰的非成员函数。
友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有成员、保护成员和公有成员。
在类中声明友元函数时,需在其函数名前加上关键字friend。此声明可以放在公有部分,也可以放在保护部分和私有部分。
友元函数可以定义在类内部,也可以定义在类的外部。

友元函数的使用:

123456789101112131415161718192021222324252627
#include<iostream.h>#include<cmath>	class Point	{	 int x, y;	 public:		Point(int a=0, int b=0)		{  x=a; y=b;  }		~Point()  {  }		void Show()		{ cout<<”x=”<<x<<” y=”<<y<<endl;   }		int Get_x()		{  return x;  }		int Get_y()		{  return y;  }	friend double Distance(Point &p1, Point &p2);  //友元函数声明	};

double Distance(Point &p1, Point &p2)   //友元函数定义	{	return  sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));	}	int main()	{ Point p1, p2(1,1);	  cout<<Distance(p1,p2)<<endl;	  return 0;	}

友元函数的说明:

  • 友元函数不是类的成员函数。因此,对友元函数指定访问权限无效,可以把友元函数的说明放在privatepublicprotected的任意段中。在类的外部定义友元函数时,不必像成员函数那样,在函数名前加上类名::
  • 使用友元函数的目的是提高程序的运行效率。
  • 慎用友元函数,因为它可在类外直接访问类的私有或保护成员,破坏了类的信息隐藏的特性。

友元成员

除了一般的函数可以作为某个类的友元外,一个类的成员函数也可以作为另一个类的友元。
这种成员函数不仅可以访问自己所在类对象中的所有成员,还可以访问friend声明语句所在类对象中的所有成员。
这样能使两个类相互合作、协调工作,完成某一任务。

将一个类的成员函数说明为另一个类的友元函数

1234567891011121314151617181920212223242526272829303132333435363738394041
#include<iostream>using namespace std;class N;       //N类的前向说明,因为N的定义在M之后,而M中使用了Nclass M	{	  int a, b;		public:		  M(int x, int y)		  { a=x; b=y; }		  void Print()		  { cout<<”a=”<<a<<” b=”<<b<<endl;  }		  void Setab(N &);		};		class N		{		  int c, d;		  public:			N(int a, int b)			{ c=a; d=b; }		  void Print()		  { cout<<”c=”<<c<<” d=”<<d<<endl;  }	      friend void M::Setab(N&);   //将M类的成员函数说明为本类的友元函数		};

void M::Setab(N &obj)    //M类的成员函数Setab()是类N的友元函数		{		  a=obj.c;            //在Setab()中可以直接访问类N的私有成员		  b=obj.d;		}		int main()		{ M m(25, 40);		  N n(55,66);		  cout<<”m: ”;		  m.Print();		  cout<<”n: ”;		  n.Print();		  m.Setab(n);		  cout<<”m: ”;		  m.Print();		  return 0;		}

运行结果为:
m: a=25 b=40
n: c=55 d=66
m: a=55 b=66

友元类

友元还可以是类,即一个类可以作为另一个类的友元。
当一个类作为另一个类的友元时,则该类中的所有成员函数都是另一个类的友元成员,都可以访问另一个类的所有成员。
友元类的声明格式为:
friend 类名;
此语句可以放在公有部分也可以放在私有部分或保护部分。

将一个类说明为另一个类的友元类:

12345678910111213141516171819202122232425
#include<iostream>using namespace std;class B;  // 前向说明,因为在后面定义的A类中用到B类class A{ int x; public: A(int a){  x=a;  }  friend class B;  //将类B声明为类A的友元类};

class B{public:void Show(A  a)  { cout<<”x=”<<a.x<<endl;  }};int main(){		A a(10);		B b;		b.Show(a);		return 0;}

运行结果为:
x=10

说明:

  • 本例中,类B是类A的友元,因此,在类B的成员函数中可以访问类A的任何成员。
  • 友元关系是单向的,不具有交换性,即类A中将类B声明为自己的友元类,但类B中没有将类A声明为友元类,所以类A的成员函数不可以访问类B的私有成员。
  • 当两个类都将对方声明为自己的友元时,才可以实现互访。
  • 友元关系也不具备传递性,即类A将类B声明为友元,类B将类C声明为友元,此时,类C不一定是类A的友元。

原文:大专栏  类和对象(二)

原文地址:https://www.cnblogs.com/petewell/p/11584743.html

时间: 2024-10-11 21:32:12

类和对象(二)的相关文章

C++中的类和对象(二)

一,对象的动态建立和释放 1.什么是对象的动态建立和释放 通常我们创建的对象都是由C++编译器为我们在栈内存中创建的,我们无法对其进行生命周期的管理.所以我们需要动态的去建立该对象,因此我们需要在堆内存中创建对象和释放对象.在C语言中为我们提供了malloc()函数和free()函数来为我们提供在堆内存中分配变量的方式,但是在C++中引入了new和delete关键字来让我们动态的创建和释放变量. 2.new和delete关键字 new关键字是用来在堆内存中创建变量的,格式为:Type * ptr

php类和对象(二)

面向对象第三大特性:多态 概念: 当父类引用指向子类实例的时候,由于子类对父类函数进行了重写,导致我们在使用该引用取调用相应方法时表现出的不同 条件: 1.必须有继承 2.子类必须对父类的方法进行重写 多态在弱类型语言中表现不明显,在强类型语言中表现比较明显,在php中不用考虑. class Ren { public $name; public $sex; function say() { echo "hello"; } } class China extends Ren { func

python cookbook第三版学习笔记十一:类和对象(二)调用父类的方法

在子类中调用父类的方法,可以下面的A.spam(self)的方法. class A(object):     def spam(self):         print 'A.spam' class B(A):     def spam(self):         print 'B.spam'         A.spam(self) if __name__=='__main__':     b=B()     b.spam() 但是上面的代码有一个问题,如果B的父类变更了,而且有很多子类的父

C++笔记二:类和对象

类和对象 1:面向对象基础 2: 定义类和对象 3: 类和对象的使用 4: 构造函数和析构函数 5: this指针和复制构造函数 6: static静态成员 7: const对象和const成员 8: friend友元 ---------------------------------------------------------------------------------------------------------------------- (1)面向对象基础 一.什么是面向对象程序

PHP 类与对象 全解析( 二)

目录 PHP 类与对象 全解析( 一) PHP 类与对象 全解析( 二) PHP 类与对象 全解析(三 ) 7.Static关键字 声明类成员或方法为static,就可以不实例化类而直接访问.不能通过一个对象来访问其中的静态成员(静态方法除外). 静态成员属于类,不属于任何对象实例,但类的对象实例都能共享. 小结: 在类内部访问静态成员属性或者方法,使用 self::(没有 $ 符号),如:  self:: $country  //类内部访问静态成员属性  self:: myCountry()

oc语言学习之基础知识点介绍(二):类和对象的进一步介绍

一.类.对象在内存中的存储 /* 内存分区: 栈:局部变量 堆:程序员自己写代码申请开辟的 程序员自己维护,编译器现在帮我们自动优化了,它在合适的给我们加上了释放空间的语句,所以我们现在写的对象不会造成内存泄露 全局区:所有的全局变量和静态变量 常量区:所有的常量 代码区:程序编译后的指令集 类是模板,肯定需要存在内存里面,因为实例化对象的时候需要根据这个模板来创建,那么存在内存里面,存在哪呢?? 类模板存在:全局区! 存的是:类的描述,还有所有的方法实现 每个对象都会有一个系统给我们的isa指

java基础(二)-- 类、对象

一.类:通过封装其他数据类型达到创建新的数据类型的目的.在类里面可以包含基本数据类型和非基本数据类型 1.java语言的源代码是以类为单位存放在文件当中的. 2.类的主体当中变量有两个正式的名字:属性和成员变量. 3.创建类的引用(声明一个类的变量):类名 + 空格 +类变量名 4.创建一个类的对象(创建一个类的实体): new + 空格 + 类名 + ().创建实体过程中,让3中声明的类的变量指向这个实体. 5.由于创建的引用指向类的实体,所以变量代表了类的对象.使用类中的属性时,可以直接使用

二维数组 面向对象编程的概念、 类、对象以及引用 和成员方法

1.二维数组(熟悉)1.1 基本概念 一维数组本质上就是一段连续的存储单元,用于存放多个类型相同的数据内容. 二维数组本质上就是由多个一维数组组成的数组,也就是说二维数组中的每个元素都是一维数组,而一维数组的每个元素才是具体数据内容. 1.2 二维数组的声明(1)语法格式 数据类型[][] 数组名称 = new 数据类型[行数][列数];如: int[][] arr = new int[2][5]; - 声明一个具有2行5列元素类型为int的二维数组 其中行下标的范围是:0 ~ 1: 其中列下标

二、类与对象(上)

从本节开始,介绍C#面向对象编程的基本内容. 与使用C语言等结构化编程语言不一样,使用C#编程,所有的程序代码几乎都放在类中,不存在独立于类之外的函数.因此,类是面向对象编程的基本单元. 在绝大多数面向对象语言中,一个类都可以包含两种成员:字段(Field)与方法(Method).字段与方法这两个概念是面向对象理论的术语,是通用于各种面向对象语言的.而在各种的具体面向对象语言(比如C#)中,可以简单的这样理解: 字段即变量,方法即函数. 类的字段一般代表类中被处理的数据,类的方法大多代表对这些数