C++面向对象程序设计----类(基础概念)

相关章节

这是下一章节的链接:类的继承与组合

书摘

夏日的暖阳把你我定格在了那个夏天,从此你不在参与我的春秋,时间啊,它只负责有规律的更替一年四季,哪里关心我们的惆怅呢·······

前言

早期软件开发程序的模式是:程序 = (算法)+ (数据结构),()代表一个整体,也就是说最早的程序是把数据结构和算法分开的。一般来说,算法只能适合于特定的数据结构,比如说算法是挤牛奶,这只对母牛起作用,如果你把这个算法应用于公牛,无异于让公牛本不富裕的生活雪上加霜,哈哈,开个玩笑 。后来,软件开发人员逐渐意识到了这个问题,调整了程序开发的模式,演化成:程序 = (数据结构 + 算法),而这个()的整体被称作对象。延续上面的例子,创建母牛这个对象的时候,便在这个对象中设计了算法为挤牛奶,在创建公牛对象的时候,没有挤牛奶这个算法。而后,程序的开发模式升级为:程序 = 对象 + ······ + 对象。在这样的背景下,类应运而生,C语言过渡出了C++语言。

定义一个类

铺垫

前言中说过 类 = 数据结构 + 针对该数据结构制作的算法,算法又是由函数体现的,所以想要定义一个完整的类,那么需要数据成员和函数成员(也叫做成员函数)。成员在类中可以定义为三种属性:private(私有)、public(公有)、protected(受保护的)。

@我们先区分一下private和public,而protected放在后面类的继承和派生模块说明其用处。
private:把类内成员的属性设置为私有,那么只有成员函数和友元函数才能访问和操作私有成员。数据成员一般设置为私有。
友元函数:不属于类但是可以操作类的数据成员,一般用于运算符的重载。
public:把类内成员的属性设置为公有,那么任何函数都能访问及操作这些公有成员。成员函数一般设置为公有。
protected:在讲解类的继承和派生之前,protected和private发挥的作用相同。
如果在类种不声明成员的属性,那么成员属性默认是private,以上也是类的封装性的体现。

声明一个类:

关键字: class

class name	//声明一个名为name的类
{
	public/*成员*/
	private:
	/*成员*/
	protected/*成员*/
};		//此处的分号要注意!

@成员函数的定义有两种方式,一种是在类的内部定义,默认为内联函数;另一种在类的内部声明,在类的外部定义,适用于逻辑复杂的函数。而且成员函数是可以重载的,重载的规则不变。
成员函数的作用就是为程序提供操作数据成员的方法。

@接下来我们举一个例子,这个例子将贯穿整篇文章并不断完善。如下包含了成员函数和数据成员的定义方式。

class Student
{
public:
	void average_score();	//计算平均分并更新number值函数的声明,定义在类外。
	void compare();
	void init()
	{
		name = "无数据!"
		telephone = "无数据!"
		score = 0;
		number = 0;
	}
	/* 此函数默认为内联函数 */
	void display()	
	{
		/* 成员函数可以访问类的私有成员 */
		cout<<name<<' '<<telephone<<' '<<score<<' '<<number<<'\n';
	}
private:
	string name;	//学生姓名
	string telephone;	//电话号码
	float score;	//平均分数
	int number;	//已修读完成的科目数量
};

/* 类外定义的格式,一定要有 Student:: */
void Student::average_score()
{
	int n;
	cin>>n;
	for(int i = 0; i < n; ++i)
	{
		float sc;
		cin>>sc;
		score = (score * number + sc) / (++number);
	}
}

成员函数中的成员变量与局部变量

以void Student::average_score()为例。
其实在这个函数中有些东西被省略了,完整的表达应该是:

void Student::average_score()
{
	int n;
	cin>>n;
	for(int i = 0; i < n; ++i)
	{
		float sc;
		cin>>sc;
		Student::score = (Student::score * Student::number + sc) / (++Student::number);
	}
}

那么问题来了,如果定义了一个局部变量和类的数据成员重名,编译器怎么判定呢?
编译器会判定为局部变量。举例:

void Student::compare()
{
	float score = 20.0;
	/*score*/
	score += 1;		//操作的是值为20.0的score,而不是Student::score! 
}

使用类的公有成员

一般类的公有成员都是成员函数,所以我们以成员函数为例。
直接调用成员函数要使用“ . ”操作符,通过指针调用要使用“ -> ”操作符。

/*代码承接上面例子*/
Student stu;	//用类 Student 创建对象 stu 。注意区分类和对象!

/*调用函数举例 */
/*先指明对象是stu,再指明成员函数是init*/
stu.init();		//直接调用

/*间接调用,先指明指针,再指明调用的函数*/

Student* pStu = stu;
pStu->display();	//指针间接调用

公有数据成员的使用和成员函数相同,私有成员不支持调用操作。所以我们在使用类的时候只需要知道它的公有成员及其作用就可以了。

this指针

在调用类的成员函数stu.init()时,传入函数中的不只是参数(虽然这个没有参数),还有一个this指针,这个指针指向stu。可以想象为:Student* this = &stu;

stu.init();	//调用	

/* 调用后函数相当于下面的样子 */
//这里只是表述this指针是如何起作用的,不要当成真正的代码啊!!!
Student* this = &stu;
	void init()
	{
		this->name = "无数据!"
		this->telephone = "无数据!"
		this->float = 0;
		this->number = 0;
	}

看到这有没有和 “成员函数中的成员变量与局部变量”产生什么关联呢?有!如果把函数中的 this-> 换成Student:: 是不是init函数就是相同的了呢。

类的公有静态成员

静态成员变量和普通的静态变量一样,只能被定义一次,所以静态成员是所有对象共同拥有的,静态成员函数只能调用静态成员变量,用图来解释较好:
在这里插入图片描述

如图所示,所有的对象共用相同的静态成员,当你调用静态成员函数(假设是add()函数)时,如果add()中用到了非静态数据成员m,那么add()应该调用哪个m呢?是1还是2还是3还是4呢?显然这样会带来很多不必要的麻烦,因此C++中规定,静态成员函数只能调用静态数据成员,因为静态数据成员只存在一个,不会产生刚才说的问题。

构造函数

作用

构造函数在创建对象的时候就会自动执行。
构造函数的作用是初始化数据成员,如同上面的init函数,接下来我们将用构造函数取代init函数并实现其功能。
构造函数无返回值,函数名和类名相同。

默认构造函数

在类的声明中,虽然我们没有声明构造函数,但是编译器会自动为我们生成一个构造函数。
举例:

/*此处省略了一些没有用到的成员*/
class Student
{
public:
	//默认构造函数,没错就是空的。
	Student()
	{
	
	}	
};

重载构造函数

可以看到上面的默认构造函数什么都没做,无法完成我们需要的初始化,接下来我们将它重载来实现init的功能。
重载的方式分为三种:

	/* 重载构造函数,第一种方法 */
	Student()
	{
		name = "无数据!"
		telephone = "无数据!"
		score = 0.0;
		number = 0;
	}	//可以看到和init没什么区别,唯一区别就是它自动执行,init需要调用才执行。

	/* 重载构造函数,第二种*/
	Studnet(string na = "无数据!", string te = "无数据", float sc = 0, int nu = 0)
	{
		name = na;
		telephone = te;
		score = sc;
		number = nu;
	}//这种重载方式可以在创建对象的时候指定初值,具体用法下面说
	
	/* 重载构造函数,第三种*/
Student(string na = "1", string te = "2", float sc = 0.0, int nu = 0)
:name(na),telephone(te),score(sc),number(nu){}
//这个其实就是换了种写法,把大括号中的赋值换成了“ :”和{}中间的表达式,注意那个冒号啊!不能没有!

通过构造函数创建对象

class Student
{
public:
	void average_score(int n);	//计算平均分并更新number值函数的声明,定义在类外。
	void compare();
	
	//采用第三种重载
	Student(string na = "1", string te = "2", float sc = 0.0, int nu = 0) 
	 : name(na),telephone(te),score(sc),number(nu){}
	/* 此函数默认为内联函数 */
	void display()	
	{
		/* 成员函数可以访问类的私有成员 */
		cout<<name<<' '<<telephone<<' '<<score<<' '<<number<<'\n';
	}
private:
	string name;	//学生姓名
	string telephone;	//电话号码
	float score;	//平均分数
	int number;	//已修读完成的科目数量
};

//创建对象
Studnet stu("露忆丶十二""18545669661", 100.0, 5);
/* 此时对象stu的数据成员的值
 *	name : 露忆丶十二
 * telephone :18545669661
 * score : 100.0
 * number : 5
*/

//对比
Student stu1;
/* 此时stu1 对象中的数据成员的值
 * name : 1
 * telephone : 2
 * score : 0.0 
 * number : 0
*/

友元函数

在前面已经提到过友元函数了,它不属于类的成员函数并且可以访问类的数据成员,往往用于双目运算符的重载。单目运算符重载为类的成员函数比较好哈!
@友元的声明:关键字 friend

//省略不必要的成员
Student stu
{
public: 
	//声明add函数为Student的友元函数
	friend float add(Student& stu1, Student& stu2);
};

//函数定义,就是一个普通的函数定义
//函数的参数选择引用参数,会节约大量的空间!! 
float add(Student& stu1, Student& stu2)
{
	float sum = stu1.score + stu2.score;
	return sum;
}

@一个小小的操作符重载,就重载 ”+“和 “=” 吧,刚好一个是双目一个是单目而且能结合起来用。
单目运算符 = 重载为成员函数;
双目运算符 + 重载为友元函数;

//省略不必要的成员
Student stu
{
public: 
	//声明add函数为Student的友元函数
	friend Student operator +(Student& stu1, Student& stu2);

	//赋值运算符重载
	Student& operator =(Student& stu1)
	{
		//眼熟吗这个this ,用处来了
		this->name = stu1.name;
		this->telephone = stu1.telephone;
		this->score = stu1.score;
		this->number = stu1.number;
	}
};

//这个加法没什么意义呀,就当是把同一个学生不同学期的分数加一起把。
Student operator +(Student& stu1, Student& stu2)
{
	Student stu3;
	stu3.name = stu1.name;
	stu3.telephone = stu1.telephone;
	stu3.number = stu1.number + stu2.number;
	stu3.score = (stu1.score + stu2.score)/ 2;
	return stu3;
}

其实在类中赋值运算符是编译器已经重载过的了,这里我们只是拿来说明一下,演示一下用法。

析构函数

析构函数在对象被程序释放时自动执行,不需要什么多余的操作。
析构函数的原型:
~Student();

完整代码段

由于上面的代码太多而且纯手打的,有可能有错误,所以我又用编译器来了一遍,顺带整理了一下。

// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <string>
class Student
{
public:
	void average_score();	//计算平均分并更新number值函数的声明,定义在类外。
	
	void compare();
	
	Student(std::string na = "1", std::string te = "2", float sc = 0.0, int nu = 0) 
	: name(na), telephone(te), score(sc), number(nu) {}
	
	friend Student operator + (Student & stu1, Student & stu2);
	
	friend float add(Student& stu1, Student& stu2);
	
	/* 此函数默认为内联函数 */
	void display()
	{
		/* 成员函数可以访问类的私有成员 */
		std::cout << name << ' ' << telephone << ' ' << score << ' ' << number << '\n';
	}

	//赋值运算符重载
	Student& operator = (Student& stu1)
	{
		//眼熟吗这个this ,用处来了
		this->name = stu1.name;
		this->telephone = stu1.telephone;
		this->score = stu1.score;
		this->number = stu1.number;
	}
	
private:
	std::string name;	//学生姓名
	std::string telephone;	//电话号码
	float score;	//平均分数
	int number;	//已修读完成的科目数量
};


int main()
{
    std::cout << "Hello World!\n";
}


/* 类外定义的格式,一定要有 Student:: */
void Student::average_score()
{
	int n;
	std::cin >> n;
	for (int i = 0; i < n; ++i)
	{
		float sc;
		std::cin >> sc;
		score = (score * number + sc) / (++number);
	}
}


void Student::compare()
{
	float score = 20.0;
	/*score*/
	score += 1;		//操作的是值为20.0的score,而不是Student::score! 
}

//这个加法没什么意义呀,就当是把同一个学生不同学期的分数加一起把。
Student operator + (Student& stu1, Student& stu2)
{
	Student stu3;
	stu3.name = stu1.name;
	stu3.telephone = stu1.telephone;
	stu3.number = stu1.number + stu2.number;
	stu3.score = (stu1.score + stu2.score) / 2;
	return stu3;
}

//函数定义,就是一个普通的函数定义
//函数的参数选择引用参数,会节约大量的空间!! 
float add(Student& stu1, Student& stu2)
{
	float sum = stu1.score + stu2.score;
	return sum;
}

总结

虽然这一章节的知识并不难,但是很多很杂,需要用心体会,
感谢看到这里的诸位,如果觉得写的还不错的话,点个小小的赞吧,谢谢啦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

露忆丶十二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值