C++提高之类和对象(封装,继承,多态)

发布于:2023-09-22 ⋅ 阅读:(73) ⋅ 点赞:(0)

 C++面向对象的三大特性:封装、继承、多态

在C++中万事万物都可以多为对象,小到蚂蚁,大到地球,宇宙等,对象上有其属性和行为。

举一个简单的例子:人

人可以作为对象,属性有姓名、年龄、身高等,行为有走、跳、跑等。

下面是关于C++对象的一些重要概念和特性:

  1. 类(Class):类是对象的模板或蓝图,描述了对象的属性和行为。它定义了对象的结构和行为的集合。

  2. 对象(Object):对象是类的一个实例,具体化了类的属性和行为。每个对象都有自己的状态和行为。

  3. 数据成员(Data Members):数据成员是类中用于存储对象状态的变量。它们可以是各种数据类型,如整数、浮点数、字符、指针等。

  4. 成员函数(Member Functions):成员函数是类中定义的函数,用于操作对象的数据成员和实现对象的行为。它们可以访问和修改对象的数据成员。

  5. 封装(Encapsulation):封装是面向对象编程的一个原则,它将数据和操作数据的函数封装在一个类中。通过封装,对象的内部细节对外部是隐藏的,只有类的成员函数可以访问和修改对象的数据。

  6. 继承(Inheritance):继承是一种机制,允许一个类派生出另一个类,从而共享基类的属性和行为。派生类可以扩展或修改基类的功能。

  7. 多态(Polymorphism):多态是指同一操作可以在不同的对象上产生不同的行为。它允许使用基类的指针或引用来调用派生类的成员函数,实现了代码的灵活性和可扩展性。

1、封装

1.1  封装的意义

封装的意义:

在C++中,封装是面向对象编程的核心概念之一,具有以下几个重要的意义:

  1. 数据隐藏:C++中的封装可以将数据成员隐藏在类的私有部分,只允许通过公有成员函数来访问和修改数据。这样可以防止外部代码直接访问和修改对象的内部数据,提高了数据的安全性和完整性。

  2. 实现细节隐藏:封装不仅隐藏了数据成员,还可以隐藏类的实现细节。外部代码只需要关注类的公有接口,而不需要了解类的内部实现细节。这样可以降低代码的耦合性,提高代码的可维护性和可扩展性。

  3. 接口定义:封装通过公有成员函数定义了类的接口,即类对外提供的操作和行为。外部代码只需要了解如何使用这些接口来与对象进行交互,而不需要了解对象的内部实现。这样可以简化外部代码的编写,并且在类的内部可以自由修改实现细节而不影响外部代码。

  4. 代码组织和模块化:封装将相关的数据和函数组织在一个类中,形成一个独立的模块。这样可以将复杂的程序分解为多个小模块,每个模块负责特定的功能。这种模块化的设计使得代码更易于理解、维护和扩展。

  5. 信息隐藏:封装可以隐藏对象的内部状态和实现细节,只暴露必要的信息给外部。这样可以降低外部代码对内部结构的依赖,减少代码之间的耦合性。当类的内部实现发生变化时,只需要调整类的接口而不影响外部代码。

简单的来讲就是:

将属性和行为作为一个整体,表现生活中的事物

将属性和行为加以权限控制

1.1.1  将属性和行为作为一个整体,表现生活中的事物

        在封装前,我们要先创建一个类。

类的定义通常包括以下几个部分:

(1)类声明:在类声明中,使用关键字 class 或 struct 定义类的名称,并指定类的访问权限(默认为私有)。类声明中可以包含数据成员和成员函数的声明,但不包含具体的实现。

(2)数据成员:类中的数据成员是类的属性,用于存储对象的状态信息。数据成员可以是各种类型的变量,包括基本数据类型(如整数、浮点数)和其他类的对象。

(3)成员函数:类中的成员函数是类的行为,用于操作和访问类的数据成员。成员函数可以在类内部定义,也可以在类外部定义。类的成员函数可以访问类的私有数据成员,并提供对外的接口来操作数据。

注意:

类中的属性和行为,我们统称为成员

属性又叫:成员属性,成员方法

行为又叫:成员函数,成员方法

(4)构造函数和析构函数:构造函数用于创建对象时初始化对象的数据成员,而析构函数用于在对象销毁时清理资源。构造函数和析构函数的名称与类的名称相同,没有返回类型。

(5)访问控制修饰符:C++中的类可以使用访问控制修饰符来控制成员的访问权限。常用的访问控制修饰符有 publicprivate 和 protectedpublic 成员可以被类的外部代码访问,private 成员只能在类的内部访问,protected 成员可以在派生类中访问。

语法:class  类名{  访问权限: 属性 / 行为 };

//类设计
class yuan
{
	//访问权限
	//公共权限
public:

	//属性
	//半径
	int m_r;

	//行为
	//周长
	double ZC()
	{
		return 2 * π * m_r;
	}
};

案例一:设计一个圆类,求圆的周长

代码示例:

#include<iostream>
using namespace std;

//圆周率
const double π = 3.14;

//设计一个圆类,求圆的周长
//圆求周长的公式
//2*π*半径

//类设计
class yuan
{
	//访问权限
	//公共权限
public:

	//属性
	//半径
	int m_r;

	//行为
	//周长
	double ZC()
	{
		return 2 * π * m_r;
	}
};

int main()
{
	//通过圆类,创建具体的圆(对象)
	yuan c1;
	//给圆的对象的属性进行赋值操作
	c1.m_r = 10;

	cout << "圆的周长:" << c1.ZC() << endl;

	system("pause");
	return 0;
}

运行结果:

案例二:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号。

以下是自己练习的,还有一个警告 (っ╥╯﹏╰╥c)             

#include<iostream>
using namespace std;
#include<string>

//类设计
class student
{
	//访问权限
	//公共权限
public:

	//属性
	string m_name;
	int m_number;

	//行为
	string showm()
	{
		return m_name;
	}

	int shown()
	{

		return  m_number;
	}

};

int main()
{
	student c1;

	cout << "请输入学生姓名:" << endl;
	cin >> c1.m_name;
	cout << "请输入学生学号:" << endl;
	cin >> c1.m_number;

	cout << "以下是你输入的学生信息:" << endl;
	cout << "您输入的学生姓名是:" << c1.showm() << endl;
	cout << "您输入的学生学号是:" << c1.shown() << endl;

	system("pause");
	return 0;
}

运行结果:

另一种解法:

#include<iostream>
using namespace std;
#include<string>

//设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号

//类设计
class student
{
	//访问权限
	//公共权限
public:

	//属性
	//姓名
	string m_name;
	//学号
	int m_number;

	//行为
	//显示姓名和学号
	void showstudent()
	{
	cout << "姓名:" << m_name << endl;
	cout << "学号:" << m_number << endl;
	}
	
};

int main()
{
	student c1;

	//给c1进行赋值操作
	c1.m_name = "张三";
	c1.m_number = 123456;


	
	//显示学生信息
	c1.showstudent();

	system("pause");
	return 0;
}

运行结果:

也可以通过行为给姓名等属性进行赋值:

#include<iostream>
using namespace std;
#include<string>

//设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号

//类设计
class student
{
	//访问权限
	//公共权限
public:

	//属性
	//姓名
	string m_name;
	//学号
	int m_number;

	//行为
	//显示姓名和学号
	void showstudent()
	{
	cout << "姓名:" << m_name << endl;
	cout << "学号:" << m_number << endl;
	}
	
	//可以通过行为给姓名等赋值
	void setstudent(string name)
	{
		m_name = name;
	}
	void setnumber(int number)
	{
		m_number = number;
	}
};

int main()
{
	student c1;

	//给c1进行赋值操作
	//c1.m_name = "张三";
	//c1.m_number = 123456;

	c1.setstudent("张三");
	c1.setnumber(123456);
	
	//显示学生信息
	c1.showstudent();

	system("pause");
	return 0;
}

运行结果:

1.1.2  将属性和行为加以权限控制

访问权限分为三种:

  1. 公有(public)访问权限:

    • 公有成员在类的内部和外部都可以被访问。
    • 公有成员可以被类的对象、类的成员函数以及类的外部代码访问。
    • 公有成员对外提供了类的接口,用于操作和访问类的数据成员和行为。
  2. 私有(private)访问权限:

    • 私有成员只能在类的内部被访问。
    • 私有成员对外部代码是隐藏的,外部代码无法直接访问私有成员。
    • 私有成员通常用于存储类的实现细节和内部状态。
  3. 保护(protected)访问权限:

    • 保护成员在类的内部和派生类中可以被访问。
    • 保护成员对外部代码是隐藏的,外部代码无法直接访问保护成员。
    • 保护成员通常用于在派生类中继承和访问基类的成员。
公共权限 public 类内可以访问 类外可以访问
保护权限 protected 类内可以访问 类外不可以访问
私有权限 private 类内可以访问 类外不可以访问

私有和保护的区别在于:

保护时,儿子可以访问父亲的保护内容

私有时,儿子不可以访问父亲的私有内容

代码示例:

#include<iostream>
using namespace std;
#include<string>


//类设计
class person
{
	//访问权限
	//公共权限
public:
	string m_name;//姓名

	//保护权限
protected:
	string m_car;//汽车

	//私有权限
private:
	int m_password;//银行卡密码

public:
	void func()
	{
		m_name = "张三";
		m_car = "自行车";
		m_password = 123456;
	}
};

int main()
{
	person p1;
	
	//类外
	p1.m_name = "Tom";
	p1.m_car = "要啥自行车";
	p1.m_password = 123098;


	system("pause");
	return 0;
}

保护权限类外不可以访问:

私有权限类外不可以访问:

1.2  struct和class的区别

        在实际使用中,struct 通常用于描述简单的数据结构,其中的成员变量通常是公有的,而 class 则更常用于封装复杂的数据和行为,其中的成员变量通常是私有的,并通过公有的成员函数提供对外的接口。

以下是 struct 和 class 的主要区别:

默认访问权限:

在 struct 中,默认的访问权限是公有的(public)。
在 class 中,默认的访问权限是私有的(private)。

成员变量和成员函数:

在 struct 中,成员变量和成员函数的默认访问权限是公有的(public)。
在 class 中,成员变量和成员函数的默认访问权限是私有的(private)。

继承方式:

在 struct 中,默认的继承方式是公有继承(public inheritance)。
在 class 中,默认的继承方式是私有继承(private inheritance)。

代码示例:

#include<iostream>
using namespace std;
#include<string>

//sruct和class的区别
//在 struct 中,默认的访问权限是公有的(public)。
//在 class 中,默认的访问权限是私有的(private)。

//类设计
class c1
{
	int m_a;//默认权限,私有

};

struct c2
{
	int m_a;//默认权限,共有
};
int main()
{
	c1 s1;
	//s1.m_a = 100;

	c2 s2;
	s2.m_a = 100;//公共可以访问

	system("pause");
	return 0;
}

运行结果:

在 class 中,默认的访问权限是私有的(private),类外不可以访问。

在 struct 中,默认的访问权限是公有的(public),类外可以访问。

1.3  成员属性设置为私有

       将成员属性设置为私有(private)是一种常见的做法,它有助于封装数据并提供更好的数据安全性和控制。通过将成员属性设置为私有,外部代码无法直接访问和修改这些属性。这样可以防止不受控制的修改和错误的使用。相反,可以通过公有的成员函数来提供对这些私有属性的访问和操作接口。这种方式称为封装,它隐藏了数据的实现细节,只暴露必要的接口给外部使用。

        通过封装,你可以控制属性的读写权限,可以在成员函数中添加验证逻辑,确保属性的有效性。你还可以在需要时修改属性的实现方式,而不会影响外部代码的使用。封装还提供了更好的代码组织和维护性。通过将相关的数据和操作封装在一起,可以更清晰地表达代码的意图,并减少代码之间的耦合。这使得代码更易于理解、调试和修改。

        总而言之,将成员属性设置为私有是一种良好的实践,它提供了更好的数据封装和控制,增加了代码的安全性和可维护性。

成员属性设置为私有的优点:

优点一:将所以成员属性设置为私有,可以自己控制读写权限。

优点二:对于写权限,我们可以检测数据的有效性。

1.3.1  将所以成员属性设置为私有,可以自己控制读写权限。

设置姓名,年龄,爱人,其中姓名能进行可读可写,年龄只能读,爱人只能写,代码如下:

#include<iostream>
using namespace std;
#include<string>

//成员属性设置为私有的优点:

//优点一:将所以成员属性设置为私有,可以自己控制读写权限。

//优点二:对于写权限,我们可以检测数据的有效性。

//类设计
class person
{
public:
	//设置姓名
	void setname(string name)
	{
		cout << "请输入本人姓名:" << endl;
		cin>>m_name;
	}
	//获取姓名
	string getname()
	{
		return m_name;
	}

	//获取年龄
	int getAge()
	{
		//年龄的只读状态
		m_age = 18;//年龄初始化操作

		return m_age;
	}

	//设置爱人
	void setlover(string lover)
	{
		cout << "请输入爱人姓名:" << endl;
		cin >> m_lover;
	}

private:
	//姓名  可读可写权限
	string m_name;
	//年龄   可读权限
	int m_age;
	//爱人   可写权限
	string m_lover;

};

int main()
{
	person p;
	p.setname("");
	p.setlover("");

	cout << "本人姓名为:" << p.getname() << endl;

	cout << "本人年龄为:" << p.getAge() << endl;

	//只有内部可以获取到,内部可以进行写,但是无法读
	//cout << "爱人年龄为:" << p.getlover() << endl;

	system("pause");
	return 0;
}

运行结果:

1.3.2  对于写权限,我们可以检测数据的有效性。

对年龄进行读写操作,不过,年龄需要限制在0~150,若是超出返回,会在读年龄时显示-1。

#include<iostream>
using namespace std;
#include<string>

//成员属性设置为私有的优点:

//优点一:将所以成员属性设置为私有,可以自己控制读写权限。

//优点二:对于写权限,我们可以检测数据的有效性。

//类设计
class person
{
public:
	//设置姓名
	void setname(string name)
	{
		cout << "请输入本人姓名:" << endl;
		cin>>m_name;
	}
	//获取姓名
	string getname()
	{
		return m_name;
	}

	//获取年龄  可读可写,如果想修改,年龄范围必须是0~150之间
	int getAge()
	{
		return m_age;
	}
	//设置年龄
	void setage(int age)
	{
		cout << "请输入本人年龄:" << endl;
		cin >> m_age;
		if (m_age < 0 || m_age>150)
		{
			m_age = -1;
			cout << "您输入的年龄有误!!!" << endl;
			return;
		}

		
	}


	//设置爱人
	void setlover(string lover)
	{
		cout << "请输入爱人姓名:" << endl;
		cin >> m_lover;
	}

private:
	//姓名  可读可写权限
	string m_name;
	//年龄   可读权限
	int m_age;
	//爱人   可写权限
	string m_lover;

};

int main()
{
	person p;
	p.setname("");
	p.setage(1);
	p.setlover("");

	cout << "本人姓名为:" << p.getname() << endl;

	cout << "本人年龄为:" << p.getAge() << endl;

	//只有内部可以获取到,内部可以进行写,但是无法读
	//cout << "爱人年龄为:" << p.getlover() << endl;

	system("pause");
	return 0;
}

运行结果:

在0~150以内

在0~150以外

1.4  封装案例练习

1.4.1  设计立方体类

求立方体的面积和体积。

分别用全局函数和成员函数判断两个立方体是否相等。

利用全局函数判断:

#include<iostream>
using namespace std;
#include<string>


//类设计
class cube
{
public:
	//设置长方形的长
	void setlong()
	{
		cout << "请输入长方形的长:" << endl;
		cin >> m_long;

	}

	//获取长方形的长
	int getlong()
	{
		return m_long;
	}

	//设置长方形的宽
	void setwide()
	{
		cout << "请输入长方形的宽:" << endl;
		cin >> m_wide;
	}

	//获取长方形的宽
	int getwide()
	{
		return m_wide;
	}

	//设置长方形的高
	void sethigh()
	{
		cout << "请输入长方形的高:" << endl;
		cin >> m_high;
	}

	//获取长方形的高
	int gethigh()
	{
		return m_high;
	}

	//获取面积
	int getarea()
	{
		return 2 * (m_long * m_wide + m_long * m_high + m_high * m_wide);
	}

	//获取体积
	int getvolune()
	{
		return m_long * m_wide * m_high;
	}

private:
	//长
	int m_long;
	//宽
	int m_wide;
	//高
	int m_high;

};

//利用全局函数判断
bool issame(cube p1, cube p2)
{
	if (p1.getlong() == p2.getlong())
	{
		if (p1.getwide() == p2.getwide())
		{
			if (p1.gethigh() == p2.gethigh())
			{
				return true;
			}
			else
				return false;
		}
		else if (p1.getwide() == p2.gethigh())
		{
			if (p1.gethigh() == p2.getwide())
			{
				return true;
			}
			else
				return false;
		}
		else
			return false;
	}
	else if (p1.getlong() == p2.getwide())
	{
		if (p1.getwide() == p2.getlong())
		{
			if (p1.gethigh() == p2.gethigh())
			{
				return true;
			}
			else
				return false;
		}
		else if (p1.getwide() == p2.gethigh())
		{
			if (p1.gethigh() == p2.getlong())
			{
				return true;
			}
			else
				return false;
		}
		else
			return false;
	}
	else if (p1.getlong() == p2.gethigh())
	{
		if (p1.getwide() == p2.getwide())
		{
			if (p1.gethigh() == p2.getlong())
			{
				return true;
			}
			else
				return false;
		}
		else if (p1.getlong() == p2.gethigh())
		{
			if (p1.gethigh() == p2.getwide())
			{
				return true;
			}
			else
				return false;
		}
		else
			return false;
	}
	else
		return false;
}

int main()
{
	cube p1;
	p1.setlong();
	p1.setwide();
	p1.sethigh();

	cout << "长方形1面积为:" << p1.getarea() << endl;

	cout << "长方形1体积为:" << p1.getvolune() << endl;

	cube p2;
	p2.setlong();
	p2.setwide();
	p2.sethigh();

	cout << "长方形2面积为:" << p2.getarea() << endl;

	cout << "长方形2体积为:" << p2.getvolune() << endl;

	//利用全局函数判断
	bool ret = issame(p1, p2);
	if (ret)
	{
		cout << "p1和p2是相等的" << endl;
	}
	else
	{
		cout << "p1和p2是不相等的" << endl;
	}

	system("pause");
	return 0;
}

运行结果:

        考虑到,当第一个长方形的长等于另一个长方形的宽或者高,而第一个长方形的宽或者高等于另一个长方形的长,这样长方形还是相等的,所以运用if语句进行判断。

若是有一个数据不相等,则:

利用成员函数判断:

#include<iostream>
using namespace std;
#include<string>


//类设计
class cube
{
public:
	//设置长方形的长
	void setlong()
	{
		cout << "请输入长方形的长:" << endl;
		cin >> m_long;

	}

	//获取长方形的长
	int getlong()
	{
		return m_long;
	}

	//设置长方形的宽
	void setwide()
	{
		cout << "请输入长方形的宽:" << endl;
		cin >> m_wide;
	}

	//获取长方形的宽
	int getwide()
	{
		return m_wide;
	}

	//设置长方形的高
	void sethigh()
	{
		cout << "请输入长方形的高:" << endl;
		cin >> m_high;
	}

	//获取长方形的高
	int gethigh()
	{
		return m_high;
	}

	//获取面积
	int getarea()
	{
		return 2 * (m_long * m_wide + m_long * m_high + m_high * m_wide);
	}

	//获取体积
	int getvolune()
	{
		return m_long * m_wide * m_high;
	}

	//利用成员函数判断
	bool issamebyclass(cube& p2)
	{
		if (m_long == p2.getlong())
		{
			if (m_wide == p2.getwide())
			{
				if (m_high == p2.gethigh())
				{
					return true;
				}
				else
					return false;
			}
			else if (m_wide == p2.gethigh())
			{
				if (m_high == p2.getwide())
				{
					return true;
				}
				else
					return false;
			}
			else
				return false;
		}
		else if (m_long == p2.getwide())
		{
			if (m_wide == p2.getlong())
			{
				if (m_high == p2.gethigh())
				{
					return true;
				}
				else
					return false;
			}
			else if (m_wide == p2.gethigh())
			{
				if (m_high == p2.getlong())
				{
					return true;
				}
				else
					return false;
			}
			else
				return false;
		}
		else if (m_long == p2.gethigh())
		{
			if (m_wide == p2.getwide())
			{
				if (m_high == p2.getlong())
				{
					return true;
				}
				else
					return false;
			}
			else if (m_long == p2.gethigh())
			{
				if (m_high == p2.getwide())
				{
					return true;
				}
				else
					return false;
			}
			else
				return false;
		}
		else
			return false;
	}

private:
	//长
	int m_long;
	//宽
	int m_wide;
	//高
	int m_high;

};


int main()
{
	cube p1;
	p1.setlong();
	p1.setwide();
	p1.sethigh();

	cout << "长方形1面积为:" << p1.getarea() << endl;

	cout << "长方形1体积为:" << p1.getvolune() << endl;

	cube p2;
	p2.setlong();
	p2.setwide();
	p2.sethigh();

	cout << "长方形2面积为:" << p2.getarea() << endl;

	cout << "长方形2体积为:" << p2.getvolune() << endl;

	//利用成员函数判断
	bool ret = p1.issamebyclass(p2);
	if (ret)
	{
		cout << "p1和p2是相等的" << endl;
	}
	else
	{
		cout << "p1和p2是不相等的" << endl;
	}

	system("pause");
	return 0;
}

运行结果:

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到