c++ 类与对象

c++中的类与对象(二)

this指针

对象的自身引用是面向对象程序设计语言中特有的、十分重要的一种机制。在c++语言中,为这种机制专门设立了一种表示方法叫做this指针。

当程序中调用类的成员函数时,this指针变量被自动初始化为发出函数调用的对象的地址。值得注意的是尽管在定义成员函数时没有看到this指针变量,也没有定义this指针变量,但是我们在函数内依然可以使用this指针变量,因为该指针是由系统内隐含给出的。
this指针的调用

class Sample{
	int x,y;
public:
	Sample(int a = 0,b = 0)
	{	this->x = a;						//this指针的调用
		this->y = b;						//this指针的调用
	}
	void Print()
	{
		cout<<this->x<<endl;
		cout<<this->y<<endl;
	}
};
int main()
{
	Sample sample(5,10);
	sample.Print();
	return 0;
}		

由于this指针是一个局部数据,它的作用域仅在一个函数的内部。因此,利用this机制可以实现成员函数在定义时与具体对象无关,在调用时与具体对象有关。
那么我们应该什么时候使用指针呢?
1.为了区分成员和非成员

void Sample::Fun(int x)
{
	this->x=x;
}

上述代码中出现成员与非成员,并且用是同一个名称,这样极容易混淆。但是我们加入了this指针后,便可以很好的进行区分。
2.当一个类的方法需要返回当前对象的引用

Sample &Sample::Set(int i,char *p)
{
	x = i;
	ptr = p;
	return *this;
}

方法Set返回当前对象的引用,*this就是当前对象

对象数组与对象指针

对象数组是指每一数组元素都是对象的数组,也就是说,若一个类中有若干个对象,我们把这一系列对象用一个数组来存放
例如 Data data[3]可表示为调用了三次Data的无参构造函数
Data data[3] = {Data(2019,10),Data(2018,10),Data(2017,10)};可表示为系统调用三次有参构造函数
另外在使用对象数组函数的时候也只能访问单个数组元素,也就是一个对象。
下面是一个对象数组的例子

class Sample{
Private:
	int x;
public:
	void Get_X(int x)
	{
		this->x=x;
	}
	int Set_X()
	{
		return x;
	}
};

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

输出结果为0 1 2 3
这个程序建立了类Sample的对象数组,并将0~3的值赋给各元素对象的x。

接下来我们将介绍堆对象,之前我们已经知道new和delete运算符动态地分配获释放堆内存。
使用new运算符动态分配的对象属于堆对象,其所占的存储空间被分配在堆区利用new建立对象会自动调用构造函数利用delete删除对象会自动调用析构函数
例如

class Sample {
private:
	int x;
	int m;
	int d;
public:
	Sample(int m, int d, int x)
	{
		this->m = m;
		this->d = d;
		this->x = x;
	}
	void Show();
};

void Sample::Show()
{
	cout << " m =" << m << " d = " << d << " x =" << x << endl;

}

int main()
{
	Sample* s;
	s = new Sample(1, 1, 1);
	s->Show();
	delete(s);
	return 0;
}

说明:
堆对象的生存期是整个程序的生命期,所以,只有当程序运行结束时,堆对象才被删除,它与一般运行的局部对象的生存期不同,局部对象的生存期开始于函数体的执行,而终止于函数体的执行结束。

对象指针
指向类对象的指针称为对象指针。每个对象在初始化后都会在内存占用一定的空间。因此,既可以通过对象名访问一个对象,也可以通过对象地址来访问一个对象。
声明指向类对象的指针变量的一般形式为Sample * point用指针引用单个对象成员方法与其他基本类型相同,point->a或者(*point).a
有关对象指针的使用方法

#include<iostream>
using namespace std;

class Date {
public:
	int month, day, year;
	Date(int m, int d, int y);
	void ShowDate();
};

Date::Date(int month, int day, int year)
{
	this->day = day;
	this->month = month;
	this->year = year;
}

void Date::ShowDate()
{
	cout << year << "." << month << "." << day << endl;
}

int main()
{
	Date obj(1, 1, 2008);
	int* pt1 = &obj.year;
	cout << *pt1 << endl;
	obj.ShowDate();
	Date* pt2 = &obj;
	pt2->ShowDate();
	return 0;
}

向函数传递对象

使用对象作为函数参数
对象可以作为函数的值形式参数,调用函数时用同类的实际参数对象与之对应。参数传递方法与传递其他数据类型相同,是单向值传递。故在函数中对形式参数对象的任何修改都不影响实际参数本身,这与一般类型变量作为形式参数的单向值传递原理完全一样。

#include<iostream>
using namespace std;

class Myclass {
	int i;
public:
	Myclass(int n)
	{
		i = n;
	}
	void set(int n)
	{
		i = n;
	}
	int Get()
	{
		return i;
	}
};

void Sqr(Myclass obj)
{
	obj.set(obj.Get() * obj.Get());
	cout << obj.Get() << endl;
}


int main()
{
	Myclass obj(10);
	Sqr(obj);
	cout << obj.Get() << endl;
	return 0;
}

从结果运行可以看出,函数中对形式参数对象的任何修改都不影响实际参数本身,但是如同其他类型的变量一样,可以通过传地址或引用的方法进行修改,由于语法比指针方法简洁,所以我们将完成引用方法的代码,传地址的方法请读者自行练习。

void Sqr(Myclass &obj)
{
	obj.set(obj.Get() * obj.Get());
	cout << obj.Get() << endl;
}

静态成员

在类的定义中,除了前面的所描述的常规成员外,还有一种方面程序设计的特殊成员,即静态成员。静态成员包括静态函数成员和静态数据成员。
在类的定义中,可以用关键字static声明成员为静态成员,这些成员可以在同一个类的不同对象之间提供数据共享。也就是说,不管这个类创建了多少对象,但静态成员只有一份拷贝,为所有属于这个类的对象共享,并且可以是public、private、protected
首先我们来讲静态数据成员
静态数据成员的初始化必须在类外进行,默认值是0,它是在编译时创建并初始化的,所以它在该类的任何对象被创建前就存在。
下面,我们给出代码

#include<iostream>
using namespace std;

class Myclass {
	int i;
	static int s;						//定义私有静态数据成员s
public:
	void Print();
	Myclass(int x=0);
};

void Myclass::Print()
{
	cout <<"i:"<< ++i << endl;				//输出普通数据成员
	cout <<"s:"<< ++s << endl;				//输出静态数据成员,两者比较
}

Myclass::Myclass(int x)
{
	i = x;
}
int Myclass::s=0;					//静态数据成员赋初值在类体外进行,前面不能再加static
int main()
{
	Myclass obj1,obj2,obj3;					//定义三个对象,都是用默认的参数值
	obj1.Print();
	obj2.Print();
	obj3.Print();
	return 0;
}

分析说明:
该类中定义了普通数据成员a和静态数据成员s,a变量对每个对象各自拥有,而s为c1、c2、c3这三个对象共有,所以每次调用print()都会对s加1
由于静态数据成员只有一个值,所以不论用哪个对象访问,所得的结果都是一样的。
接下来我们讲静态成员函数
在类定义中,声明为static的成员函数能在类的范围内共享,把这样的成员函数称为静态成员函数。静态成员函数属于整个类,是该类所有对象共享的成员函数,而不属于类中的某个对象。静态成员函数只能使用静态数据成员,不能对类的数据成员或成员函数进行访问,可以通过对象或类名进行调用。

#include<iostream>
using namespace std;

class Ctype {
	int a;
	static int s;
public:
	static void Print();
	Ctype();
};

void Ctype::Print()
{
	cout <<"s:"<< s << endl;
}

Ctype::Ctype()
{
	a = 0;
	s++;
	cout << "a=" << ++a << endl;
}
int Ctype::s=0;
int main()
{
	Ctype::Print();								//在未定义对象时,可以用类名来调用静态函数
	Ctype c1, c2;
	c1.Print();								//也可以用对象来调用静态函数
	c2.Print();
	Ctype c3;
	c3.Print();
	return 0;
}

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

友元

类的一个很重要的特点就是实现了数据隐藏和封装。在类定义的时候,一般都将数据成员声明为私有成员,只能在类定义的范围内使用,也就是说私有成员只能通过它的成员函数来访问。但是,调用共有函数接口是需要时间和开销的,且有的时候需要在类的外部访问类的私有成员。为此,就需要寻找一种途径,在不放弃私有成员安全性的情况下,使得一个普通函数或者类的成员函数可以访问到封装与某一类中的信息。在c++语言中,通过定义友元来实现这一功能。类的友元包括友元类和友元函数
友元函数

#include<iostream>
using namespace std;

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;
}

友元类

class A
{
	//...
};
class B
{
	//...
	friend A;										//声明类A是类B的友元类
	//...
};

对象成员

一个对象成员除了可以是int、char、float等这些基本数据类型外,还可以是一个类的一个对象,也就是把已有类的对象作为新类的数据成员,这时可称这种成员对象是新建子对象或成员对象。
如果一个类的对象是另一个对象的数据成员,则称这样的数据成员为对象成员,例如:

class A
{
	//...
};
class B
{
	A a;'
public:
	//...
};

当创建对象的时候,如果这个类具有内嵌对象成员也将被自动创建。因此,在创建对象时既要对本类的基本数据成员初始化,又要对内嵌的对象成员进行初始化
一般来说,类X的构造函数的定义形式为:

X::X(形参表 0):对象成员1(参数表 1),对象成员2(参数表 2),...,对象成员n(参数表 n)
{
	...//构造函数
}

当调用构造函数X::X()时,首先按各对象成员在类声明中的顺序依次调用它们的构造函数,对这些对象初始化,而不是按照初始化列表的顺序进行初始化。最后在执行X::X()的函数体。析构函数的调用顺序与此相反。

#include<iostream>
using namespace std;

class Date {
private:
	int year;
	int month;
	int day;
public:
	Date(int y, int m, int d)
	{
		cout << "Constructing Date" << endl;
		year = y;
		month = m;
		day = d;
	}
	void Show()
	{
		cout << year << "." << month << "." << day << endl;
	}
};
class Time {
private:
	int hour;
	int minute;
	int second;
public:
	Time(int h, int m, int s)
	{
		cout << "Constructing Time..." << endl;
		hour = h;
		minute = m;
		second = s;
	}
	void Show()
	{
		cout << hour << ":" << minute << ":" << second << endl;
	}
};

class Schedule {
private:
	int number;
	Date date;
	Time time;
public:
	Schedule(int num, int a, int b, int c, int d, int e, int f) :time(d, e, f),date(a, b, c)
	{
		cout << "Constructing Schedule..." << endl;
		number = num;
	}
	void Show()
	{
		cout << "number" << number << ":";
		date.Show();
		time.Show();
	}
};

int main()
{
	Schedule obj1(1, 2008, 3, 12, 12, 10, 0);
	obj1.Show();
	Schedule obj2(2, 2009, 2, 8, 18, 20, 0);
	obj2.Show();
	return 0;
}

常对象

既然各种形式的数据共享如友元类、友元函数等不同程度的破坏了数据的安全性。因此,对于既需要共享又需要防止改变的数据,应该声明为常量进行保护,这些常量需要用关键字const修饰
常成员函数

类型 函数名(参数表)const

注意:常成员函数不能修改本类的数据成员,也不能调用该类中没有由关键字const修饰的成员函数,从而保证了在常成员函数不会修改数据成员的值。
常对象

const 类名 对象名

类名 const 对象名

常对象在定义时必须初始化,而且其数据成员的值在对象的整个生存期间内不能被改变。也就是说,常对象必须进行初始化,而且不能被更新。


#include<iostream>
using namespace std;

class Person {
private:
	int age;
	char* name;	
public:
	Person(int n, char*);
	~Person();
	void Print();
	void Print()const;
	void ModifyAge();
};
Person::Person(int n, char* na)
{
	age = n;
	name = new char[strlen(na) + 1];
	strcpy(name, na);
}
Person::~Person()
{
	delete[]name;
}
void Person::Print()
{
	cout << "age:" << age << "name:" << name << endl;
	cout << "This is general Print()." << endl;
}
void Person::Print()const
{
	cout << "age:" << age << "name:" << name << endl;
	cout << "This is const Print()." << endl;
}
void Person::ModifyAge()
{
	age++;
}
int main()
{
	const Person p1(17, "wu");
	cout << "output const object p1" << endl;
	p1.Print();
	Person p2(18, "zhang");
	cout << "output general object p2" << endl;
	p2.ModifyAge();
	p2.Print();
	return 0;
}

结果为:
output const object p1
age:17 name:wu
This is const Print().
output general object p2
age:19 name:zhang
This is general Print().

说明:
(1)如果将本例中的void Print();函数的原型声明及定义删除,运行结果的最后一行变为:This is const Print().这说明普通的对象可以调用常成员函数;但是,如果有普通成员函数的重载函数,则首先会调用普通成员函数,否则,自动调用常成员函数,也就是说公有的常成员函数可以被该类的所有对象调用
(2)如果将本例中void Print() const;函数的原型声明及定义删除,其余代码不变,则程序编译的时候会报错,原因是常对象只能调用常成员函数,而不能自动调用普通的成员函数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值