中国大学MOOC 程序设计与算法(三)笔记:类和对象

this指针

this指针作用

1、其作用就是指向成员函数所作用的对象。
2、非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针。

class Complex {
	public:
		double real, imag;
		void Print() 
		{ 
			cout << real << "," << imag ; 
		}
		Complex(double r,double i):real(r),imag(i){ }
		Complex AddOne() 
		{
			this->real ++; //等价于 real ++;
			this->Print(); //等价于 Print
			return * this;
		}
};
int main() 
{
	Complex c1(1,1),c2(0,0);
	c2 = c1.AddOne();
	return 0;
} //输出 2,1
  • 静态成员函数中不能使用 this 指针,因为静态成员函数并不具体作用与某个对象,因此,静态成员函数的真实的参数的个数,就是程序中写出的参数个数!

静态成员

基本概念

静态成员:在定义前面加了static关键字的成员。

  • 普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。
    (sizeof 运算符不会计算静态成员变量)
  • 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。
  • 因此静态成员不需要通过对象就能访问。

如何访问静态成员

1、类名::成员名

CRectangle::PrintTotal();

2、对象名.成员名

CRectangle r; 
r.PrintTotal();

3、指针->成员名

CRectangle * p = &r; 
p->PrintTotal();

4、引用.成员名

CRectangle & ref = r; 
int n = ref.nTotalNumber;

性质

1、静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
2、静态成员函数本质上是全局函数。
3、设置静态成员这种机制的目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解。

实例

考虑一个需要随时知道矩形总数和总面积的图形处理程序

class CRectangle
{
	private:
		int w, h;
		static int nTotalArea;			//计算总面积
		static int nTotalNumber;		//计算矩形总数
	public:
		CRectangle(int w_,int h_);
		~CRectangle();
		static void PrintTotal();
};
CRectangle::CRectangle(int w_,int h_)
{
	w = w_;
	h = h_;
	nTotalNumber ++;
	nTotalArea += w * h;
}
CRectangle::~CRectangle()
{
	nTotalNumber --;
	nTotalArea -= w * h;
}
void CRectangle::PrintTotal()
{
	cout << nTotalNumber << "," << nTotalArea << endl;
}

1、必须在定义类的文件中对静态成员变量进行一次说明或初始化。否则编译能通过,链接不能通过。

int CRectangle::nTotalNumber = 0;
int CRectangle::nTotalArea = 0;
/*
必须在定义类的文件中对静态成员变量进行一次说明或初始化。
否则编译能通过,链接不能通过。
*/
int main()
{
	CRectangle r1(3,3), r2(2,2);
	//cout << CRectangle::nTotalNumber; // Wrong ,  nTotalNumber私有
	CRectangle::PrintTotal();
	r1.PrintTotal();
	return 0;
}

2、在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数

void CRectangle::PrintTotal()
{
	cout << w << "," << nTotalNumber << "," <<nTotalArea << endl;//wrong
}
CRetangle::PrintTotal();  //解释不通,w 到底是属于那个对象的

成员对象和封闭类

基本概念

成员对象:有一个类的成员是其它类的对象,就把这样的成员成为成员对象。
封闭类:有成员对象的类叫 封闭类。

class CTyre // 轮胎类
{
	private:
		int radius; // 半径
		int width; // 宽度
	public:
		CTyre(int r,int w):radius(r),width(w) { }
};
class CEngine // 引擎类
{
};
class CCar // 汽车类
{ 
	private:
		int price; // 价格
		CTyre tyre;  
		CEngine engine; 
	public:
		CCar(int p,int tr,int tw );
};

tyre、engine这两个成员变量是其它类的对象,因此是成员对象。
CCar这个类包含其它类,因此称为封闭类。

上例中,如果 CCar类不定义构造函数, 则下面的语句会编译出错:
CCar car;
因为编译器不明白 car.tyre该如何初始化。car.engine 的初始化没问题,用默认构造函数即可。

初始化列表

  • 任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象,是如何初始化的。
  • 具体的做法就是:通过封闭类的构造函数的初始化列表。
  • 成员对象初始化列表中的参数可以是任意复杂的表达式,可以包括函数,变量,只要表达式中的函数或变量有定义就行。
CPoint::CPoint(double tx, double ty) : x(tx), y(ty) {} 
CPoint::CPoint(CPoint & p) : x(p.x), y(p.y) {} 
CCircle::CCircle(double tx, double ty, double r) :CPoint(tx, ty), radius(r) {}
CCircle::CCircle(CCircle & c) : CPoint :: CPoint(c), radius (c.radius){}

封闭类构造函数和析构函数的执行顺序

1、封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数。

  • 构造时:先对象成员,后封闭类。

2、对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与它们在成员初始化列表中出现的次序无关。

  • 对象成员构造顺序与其在类中说明次序一致。

3、当封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。次序和构造函数的调用次序相反。

  • 消亡时:先封闭类,再成员对象。(先构造的后消亡)

友元

友元分为友元函数和友元类两种
1、友元函数 : 一个类的友元函数可以访问该类的私有成员

  • 可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元。
class CCar ; //明 提前声明 CCar 类,以便后面的CDriver 类使用
class CDriver
{
	public:
	void ModifyCar( CCar * pCar) ; // 改装汽车
};
class CCar
{
	private:
		int price;
		friend int MostExpensiveCar( CCar cars[], int total); // 声明友元
		friend void CDriver::ModifyCar(CCar * pCar); // 声明友元
};
void CDriver::ModifyCar( CCar * pCar)
{
	pCar->price += 1000; // 汽车改装后价值增加   //可以访问CCar的私有成员
}

2、友元类 : 如果A 是 B 的友元类,那么A 的成员函数可以访问 B 的私有成员。

class CCar
{
	private:
		int price;
		friend class CDriver; // 声明CDriver 为友元类
};
class CDriver
{
	public:
		CCar myCar;
		void ModifyCar()  // 改装汽车
		{
			myCar.price += 1000;// 因CDriver 是CCar 的友元类,故此处可以访问其私有成员
		}
};
int main(){ return 0; }

友元类之间的关系不能传递,不能继承

常量成员函数

常量对象

如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加 const关键字

  • 常量对象只能使用构造函数、析构函数和有const说明的函数( 常量方法)
class Sample 
{
	private :
		int value;
	public:
		Sample() { }
		void SetValue() {  }
};
const Sample Obj; //  常量对象
Obj.SetValue (); //错误。常量对象只能使用构造函数、析构函数和有const说明的函数( 常量方法)

常量成员函数

在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数。

  • 常量成员函数内部不能改变属性的值,也不能调用非常量成员函数。
  • 在定义常量成员函数和声明常量成员函数时都应该使用const 关键字。
class Sample 
{
	private :
		int value;
	public:
		void func() { };
		Sample() { }
		void SetValue() const 
		{
			value = 0; // wrong  改变了属性的值
			func(); //wrong  调用了非常量成员函数
		}
};
const Sample Obj;
Obj.SetValue (); //  常量对象上可以使用常量成员函数

常量成员函数的重载

两个函数,名字和参数表都一样,但是一个是const,一个不是,算重载。

class CTest 
{
	private :
		int n;
	public:
		CTest() { n = 1 ; }
		int GetValue() const 
		{ 
			return n ;
		}
		int GetValue() 
		{
			return 2 * n ;
		}
};

mutable成员变量

可以在const成员函数中修改mutable成员变量

class CTest
{
	public:
		bool GetData() const
		{
			m_n1++;    	//mutable成员变量,const成员函数中可以被修改
			return m_b2;
		}
	private:
		mutable int m_n1;
		bool m_b2;
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值