【类和对象的使用的知识点——案例1设计一个类的立方体,可以计算立方体的面积和体积和案例2创建点和圆的类,判断圆和点的关系】

本文介绍了C++中的面向对象特性,如封装、构造函数和析构函数的作用。封装通过权限控制实现数据隐藏,类和结构体(struct)的区别,以及如何设置成员属性为私有。还讨论了构造函数的分类(包括无参构造、有参构造和拷贝构造),以及拷贝构造函数的调用时机。文章通过实例展示了深拷贝和浅拷贝的区别,并强调了初始化列表在类对象初始化中的重要性。最后,提到了类中成员对象的概念,进一步强化了面向对象编程的理解。
摘要由CSDN通过智能技术生成

类和对象的使用的知识点

C++面向对象的三大特性为:封装,继承,多态
C++认为万事万物都皆为对象,对象上有其属性和行为
Eg:人可以作为一个对象,属性有姓名,年龄,身高,体重…行为有走,跑,跳…
车也可以作为对象,属性有轮胎,方向盘,车灯…行为有载人,放音乐,放空调
具有相同性质的对象,我们可以抽象为类,人属于人类,车属于车类

封装

封装的意义

封装是C++面向对象三大特性之一
封装的意义:1. 将属性和行为作为一个整体,表现生活中的事物

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

封装的意义一:在设计类的时候,属性和行为写在一起,表示事物,
语法:class 类名{ 访问权限 :属性 / 行为 };

#include <iostream>
using namespace std;
const double PI = 3.14;

class Circle {
	//访问权限,这里先暂时不解释
public:
	//属性
	int m_r;
	//行为
	double calculate2C() {
		return 2 * PI * m_r;
	}
};

int main()
{
	//通过一个圆类创建具体的圆(类)
	//实例化,通过一个类来创建一个对象的过程
	Circle c1;
	//给对象进行赋值
	c1.m_r = 10;
	//圆的周长公式 C = 2 * PI * r
	cout << "圆的周长为:" << c1.calculate2C() << endl;
	system("pause");
	return 0;
}

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

#include <iostream>
using namespace std;
class Student {
	//访问权限
public:
	//属性(成员属性或成员变量)
	string m_Name;
	int m_Id;
	//行为(成员函数或者成员方法)
	void setName(string name) {
		m_Name = name;
	}
	void setID(int id) {
		m_Id = id;
	}
	void showStudent() {
		cout << "学生姓名:" << m_Name << "\n学生学号: " << m_Id << endl;
	}

};
int main()
{
	Student s1;
	s1.setName("xiaochen");
	s1.setID(118);
	s1.showStudent();
}

封装意义二:类在进行设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:

1.public 公共权限:类内可以访问,类外可以访问

2.protected 保护权限:类内可以访问,类外不可以访问,儿子可以访问父亲中的保护内容

3.private 私有权限:类内可以访问,类外不可以访问,儿子不可以访问儿子的私有内容

#include <iostream>
using namespace std;
class Person {
public:
	string m_Name;
protected:
	string car;
private:
	string password;
public:
	void func()
	{
		m_Name = "陈赢";
		car = "拖拉机";
		password = "123456";
	}
};
int main()
{
	Person p1;
	p1.m_Name = "小陈";
	cout << p1.m_Name << endl;
	//p1.car="benchi";
	//p1.password="qwert";这两个属性都是访问不到的,因为这两个属性的访问权限收限制
}

struct和class区别

在C++中struct和class唯一的区别就是在于默认访问的权限不同
struct默认的访问权限为公共的,class默认访问的权限是私有的

成员属性设置为私有

优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
成员的访问有权限的控制,可以通过将属性进行私有化,在利用成员函数公共化,类内元素可以访问的条件,就可以达到这样的效果

#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
	//可读成员方法
	string getName() {
		return m_Name;
	}
	//可写成员方法
	void setName(string name) {
		m_Name = name;
	}
	//只读函数
	int getAge() {
		return m_Age;
	}
	void setAge(int age) {
		if (age < 0 || age > 150) {
			cout << "你输入的年龄有问题,请重新输入" << endl;
			m_Age = 0;
			return;
		}
		m_Age = age;
	}
	//只写函数
	void setLover(string lover) {
		m_Lover = lover;
	}

private:
	string m_Name;//可读可写
	int m_Age;//只读
	string m_Lover;//只写

};
int main()
{
	Person p1;
	p1.setName("小陈");
	p1.setLover("小刘");
	p1.setAge(200);
	cout << "年龄:" << p1.getAge() << endl;
	cout << "姓名:" << p1.getName() << endl;
	system("pause");
	return 0;
}

封装案例1:
问题:设计一个类的立方体,可以计算立方体的面积和体积,并且分别用全局函数和成员函数进行判断两个立方体是否相同

#include <iostream>
using namespace std;
class Cube {
	//成员的函数(方法)
public:
	//设置长
	void setL(int l) {
		m_L = l;
	}
	//获取长
	int getL() {
		return m_L;
	}
	//设置宽
	void setW(int w) {
		m_W = w;
	}
	//获取宽
	int getW() {
		return m_W;
	}
	//设置高
	void setH(int h) {
		m_H = h;
	}
	//获取高
	int getH() {
		return m_H;
	}
	//计算立方体的面积
	int calculcateS() {
		return m_L * m_W * 2 + m_L * m_H * 2 + m_W * m_H * 2;
	}
	//计算立方体的体积
	int calculcateV() {
		return m_L * m_W * m_H;
	}

	//成员函数判断
	bool isSameByClass(Cube& c) {
		if (m_L == c.getL() && m_W == c.getW() && m_H == c.getH()) {
			return true;
		}
		else
			return false;
	}
	//成员属性:各个立方体的属性
private:
	int m_L;
	int m_W;
	int m_H;
};

//利用全局函数来判断两个函数是否相等
bool isSame(Cube& c1, Cube& c2) {
	if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH()) {
		return true;
	}
	return false;
}
int main()
{
	//创建第一个立方体
	Cube c1;
	c1.setL(10);
	c1.setW(10);
	c1.setH(10);
	cout << "第一个立方体的面积为:" << c1.calculcateS() << endl;
	cout << "第一个立方体的体积为:" << c1.calculcateV() << endl;

	//创建第二个立方体
	Cube c2;
	c2.setL(10);
	c2.setW(10);
	c2.setH(10);
	cout << "第二个立方体的面积为:" << c2.calculcateS() << endl;
	cout << "第二个立方体的体积为:" << c2.calculcateV() << endl;

	//调用全局函数判断
	bool ret = isSame(c1, c2);
	if (ret) {
		cout << "全局函数:两个立方体相等" << endl;
	}
	else {
		cout << "全局函数:两个立方体的不相等" << endl;
	}

	//调用成员函数判断
	int ret_class = c1.isSameByClass(c2);

	if (ret_class) {
		cout << "成员函数调用:两个立方体相等" << endl;
	}
	else {
		cout << "成员函数调用:两个立方体不相等" << endl;
	}
	system("pause");
	return 0;

}



总结:在进行全局函数和成员函数判断的时候,成员函数可以只传入一个参数,本身又调用自己的成员函数
注意代码的规范,每一步细化

封装案例2:
总结:在类中可以让另一个类作为本来中的成员
在这个案例中,存在每个类进行分类并且用头文件和cpp 文件进行分开操作,在后续的修改更加方便

//创建点和圆的类,判断圆和点的关系

#include <iostream>
#include <math.h>
using namespace std;
class Point {
	//成员的函数
public:
	//设置x的坐标
	void setX(int x) {
		m_X = x;
	}
	//获取x的坐标
	int getX() {
		return m_X;
	}
	//设置y的坐标
	void setY(int y) {
		m_Y = y;
	}
	//获取y的坐标
	int getY() {
		return m_Y;
	}
	//成员的属性	
private:
	int m_X;
	int m_Y;
};

class Circle {
	//成员的函数,设置获取半径的大小,计算两点之间的距离,设置P点
public:
	//设置半径的大小
	void setR(int r) {
		m_R = r;
	}
	//获取半径的大小
	int getR() {
		return m_R;
	}
	//设置圆心的大小
	void setCenter(Point m) {
		center = m;
	}
	//获取圆心的大小
	Point getCenter() {
		return center;
	}

	//成员的属性:圆的半径和圆心,计算两者之间的距离
private:
	int m_R;
	Point center;
	int d;
};

void calculateDistance(Circle& O, Point& p) {
	//计算两点之间的距离
	int distance =
		(O.getCenter().getX() - p.getX()) * (O.getCenter().getX() - p.getX()) +
		(O.getCenter().getY() - p.getY()) * (O.getCenter().getY() - p.getY());
	//计算半径的平方
	int rdistance = O.getR() * O.getR();
	if (distance == rdistance) {
		cout << "点在圆上" << endl;
	}
	else if (distance > rdistance)
	{
		cout << "点在圆外" << endl;
	}
	else {
		cout << "点在圆内" << endl;

	}
}

int main()
{
	//创建一个P点
	Point p1;
	p1.setX(10);
	p1.setY(10);
	cout << "P点的x坐标为:" << p1.getX() << endl;
	cout << "P点的y坐标为:" << p1.getY() << endl;

	//创建圆心
	Point m;
	m.setX(10);
	m.setY(0);
	cout << "圆心X坐标为:" << m.getX() << endl;
	cout << "圆心Y坐标为:" << m.getY() << endl;


	//创建一个圆
	Circle O1;
	O1.setR(10);
	O1.setCenter(m);
	cout << "圆的半径为:" << O1.getR() << endl;
	calculateDistance(O1, p1);


	system("pause");
	return 0;
}

对象的初始化和清理

构造函数和析构函数

(1)、构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用

(2)、析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

构造函数:类名(){}

1.构造函数,没有返回值也不写void

2.函数名称与类名相同

3.构造函数可以有参数,因此可以发生重载

4.程序在调用对象的时候会自动调用构造,无须手动调用,且只会调用一次

析构函数:~类名(){}

1.析构函数,没有返回值也不写void

2.函数名称与类名相同,在名称前加上~

3.构造函数不可以有参数,因此不可以发生重载

4.程序在对象的时候会自动调用构造,无须手动调用,且只会调用一次

构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构

#include <iostream>
using namespace std;
class Person {
public:
	Person()
	{
		cout << "Person的构造函数" << endl;
	}

	~Person()
	{
		cout << "Person的析构函数" << endl;
	}
};

//构造函数和析构函数都是自动会调用的,在进行销毁的时候,主要看数据存放的位置
//例如这个函数存放在栈区,在调用完这个函数的时候就会自动进行销毁,所以进能够看到调用析构函数

void test01()
{
	Person p;
}

int main()
{
	Person p1;
	//test01();
	system("pause");
	return 0;
}

构造函数的分类及调用

两种分类的方式:
按参数分:有参构造和无参构造
按类型分:普通构造和拷贝构造
三种调用的方式:括号法,显示法,隐式转换法

#include <iostream>
using namespace std;
class Person {
public:
	//构造函数
	Person() {
		cout << "Person的无参构造函数" << endl;
	}

	Person(int a)
	{
		age = a;
		cout << "Person的有参构造函数" << endl;
	}
	//拷贝构造函数
	Person(const Person& p) {
		age = p.age;
		cout << "Person的拷贝构造函数" << endl;
	}
	//析构函数
	~Person() {
		cout << "Person的析构函数调用" << endl;
	}

private:
	int age;
};

void test01() {
	//括号法
	Person p1;//构造函数无参调用
	Person p2(10);//构造函数有参调用
	Person p3(p2);//拷贝构造函数调用
	//注意事项1:调用默认构造函数的时候,不能加 (),因为加上编译器就会认为这是一个函数声明

	//显示法
	Person p4;
	Person p5 = Person(10);//有参构造
	Person p6 = Person(p5);//拷贝构造
	//隐式转换法
	//Person(10);这个是属于匿名对象,特点:当前执行结束后,系统就会立即回收掉匿名对象
	//注意事项2:不要利用拷贝构造函数来初始化匿名对象,编译器会认为这个是一个对象的声明

	//隐式转换法
	Person p7 = 10;//相当于写了Person p4=Person(10);有参构造
	Person p8 = p7;//拷贝构造

}

int main()
{
	test01();
	system("pause");
	return 0;
}


拷贝构造函数调用时机

C++中拷贝构造函数调用的时机通常由三种情况:

  1. 使用一个已经创建完毕的对象来初始化一个新对象

  2. 值传递的方式给函数参数传值

  3. 以值的方式返回局部对象

#include <iostream>
using namespace std;
class Person {
public:
	//构造函数
	Person() {
		cout << "Person的无参构造函数" << endl;
	}

	Person(int a)
	{
		age = a;
		cout << "Person的有参构造函数" << endl;
	}
	//拷贝构造函数
	Person(const Person& p) {
		age = p.age;
		cout << "Person的拷贝构造函数" << endl;
	}
	//析构函数
	~Person() {
		cout << "Person的析构函数调用" << endl;
	}

public:
	int age;
};

//使用一个已经创建完毕的对象来初始化一个新对象

void test01() {
	Person p1(20);
	Person p2(p1);
	cout << "p2的年龄: " << p2.age << endl;
}

// 值传递的方式给函数参数传值
//值传递就相当于在栈区开辟一块新的空间,拷贝过去所有的值,但是本身的值是不会改变的,他们所指的不是同一个地址
void doWork(Person p) {

}

void test02() {
	Person p;
	doWork(p);
}

//以值的方式返回局部对象

Person doWork2() {
	Person p1;
	return p1;
}

void test03() {
	Person p = doWork2();
}

int main()
{
	//test01();
	//test02();
	test03();
	system("pause");
	return 0;
}


构造函数调用规则

默认情况下:C++的编译器至少给一个类添加3个函数

  1. 默认构造函数(无参,函数体为空)

  2. 默认析构函数(无参,函数体为空)

  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数的调用规则如下:

  1. 如果用户定义有参构造函数,C++不在提供默认无参构造函数,但是会提供默认拷贝函数

  2. 如果用户定义拷贝构造函数,C++不会提供默认其他构造函数

深拷贝和浅拷贝

深浅拷贝是一个常见的坑
浅拷贝:简单赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作

#include <iostream>
using namespace std;
class Person {
public:
	Person() {
		cout << "Person的无参构造函数" << endl;
	}

	Person(int age, int height) {
		m_Age = age;
		m_Height = new int(height);
		cout << "Person的有参构造函数" << endl;
	}

	Person(const Person& p) {
		cout << "Person的拷贝构造函数" << endl;
		m_Age = p.m_Age;
		m_Height = new int(*p.m_Height);

	}
	~Person() {
		if (m_Height != NULL)
		{
			delete m_Height;
			m_Height = NULL;
		}
		cout << "Person的析构函数" << endl;
	}
	int m_Age;
	int* m_Height;
};

void test01()
{
	Person p1(18, 160);
	cout << "p1的年龄:" << p1.m_Age << "p1的身高:" << endl;

	Person p2(p1);
	cout << "p2的年龄:" << p2.m_Age << "p2的身高:" << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}


总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
因为浅拷贝仅仅只是简单的赋值,但是当数据是一个地址,并且拷贝的时候肯定是指向同一个地址,但是在进行删除操作的时候,就有一个找不到,因为已经被删除了,他们指的是同一片空间,但是在自己写的拷贝函数就是两个相同的存放地址的元素,虽然值相等,但是他们所指的空间是不同的

初始化列表

作用:C++提供初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)…{}

#include <iostream>
using namespace std;
class Person {
public:
	Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {

	}
	void printPerson() {
		cout << "mA=" << m_A << endl;
		cout << "mB=" << m_B << endl;
		cout << "mC=" << m_C << endl;
	}

private:
	int m_A;
	int m_B;
	int m_C;

};
int main()
{
	Person p(30, 20, 10);
	p.printPerson();
	system("pause");
	return 0;
}

7.类对象作为类成员
C++类中的成员可以是另一个类的对象,我们就称该对象为成员对象


总结:通过实验,我深刻领会类与对象的区别,类实现数据隐藏与封装的原理,并对C++编程有了更进一步的理解。掌握了声明类的方法,类和类的成员概念以及定义对象的方法,也掌握了成员函数的实现与调用方法,掌握引检查和调试基于对象的程序的方法。在学习类与对象的时候,了解写程序又两种方法,一个是面向过程,一个是面向对象,其中面向过程就是直接通过主函数或者借用其他函数,在主函数中直接写算法程序,但是面向对象的程序,就是要先创建对象,例如老师让我们定义一个学生,在处理学生的信息程序上面,就需要考虑学生的信息,学号,班级,成绩等等,然后在定义出学生这一类后,在进行对类操作的函数,面向对象的方法程序,只需要在主函数定义一个变量,直接调用这个变量项目下的程序即可进行对其他数据的操作。面向对象还有三个特性,封装,继承,多态。其中学习的封装,就是封闭的程序,也就是把客观的事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的则进行信息隐蔽。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值