【类与对象】封装对象的初始化及清理

news2024/9/26 5:20:20

C++面向对象的三大特性:封装、继承、多态。具有相同性质的对象,抽象为类。
在这里插入图片描述

文章目录

      • 1 封装
        • 1.1 封装的意义(一)
        • 1.2 封装的意义(二)
        • 1.3 struct 和 class区别
        • 1.4 成员属性设置为私有
        • 练习案例:
          • 1 设计立方体类
      • 2 对象的初始化和清理
        • 2.1 构造函数和析构函数
        • 2.2 构造函数的分类及调用
        • 2.3 拷贝构造函数调用时机
        • 2.4 构造函数调用规则
        • 2.5 深拷贝与浅拷贝(面试提及)
        • 2.6 初始化列表
        • 2.7 类对象作为类成员
        • 2.8 静态成员

1 封装

1.1 封装的意义(一)

  • 将属性和行为作为一个整体,表现为生活的事物
  • 将属性和行为加以权限控制

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

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

/*
	设计一个圆类,求圆的周长
		圆的周长公式: 2 * PI * 半径
*/
const double PI = 3.14;

class Circle
{
	//访问权限
public:

	//1、属性
	//半径
	int m_r;

	//2、行为
	//获取圆的周长
	double calculateZC()
	{
		return 2 * PI * m_r;
	}

};
int main()
{
	system("color 1E");
	//通过圆类,创建具体的圆c ==>>实例化(通过一个类实例化一个具体的对象)
	Circle c1;
	//给圆对象的属性赋值
	c1.m_r = 10;
	cout << "圆的周长:" << c1.calculateZC() << endl;
	system("pause");	
	return 0;
}

示例2:设计一个学生类,属性:姓名、学号。给学生姓名和学号赋值,然后显示出姓名和学号

class Student
{
public:	//	访问权限 ->公共权限

	//1、属性 :成员属性,成员变量
	string s_name;
	int stuId;

	//2、行为 :成员函数 成员方法
	void showStudent()
	{
		cout << "学生的姓名:" << s_name << " 学号是:" << stuId << endl;
	}
	//给姓名赋值
	void setName(string name)
	{
		s_name = name;
	}
	//给学号赋值
	void setId(int id)
	{
		stuId = id;
	}
};
int main()
{
	system("color 1E");
	//通过学生类,创建具体那个学生 ==>>实例化(通过一个类实例化一个具体的对象)
	Student stu1;
	Student stu2;
	//给学生对象的属性赋值
	stu1.setName ("张三");
	stu1.setId( 21202201);
	stu1.showStudent();
	stu1.s_name = "李四";
	stu1.stuId = 21202202;
	stu1.showStudent();
	system("pause");	
	return 0;
}

1.2 封装的意义(二)

说明:类在设计时,可以把属性和行为放在不同的权限下,加以控制

访问权限三种:

  • public:公共权限
    • 成员 :类内可以访问,类外也可以访问
  • protected:保护权限
    • 成员: 类内可以访问,类外不可以访问
    • 儿子可以访问父亲的保护内容
  • private:私有权限
    • 成员:类内可以访问,类外不可以访问
    • 儿子不可以访问父亲的私有内容
class Person
{

public:
	string m_name;

protected:
	string m_Car;

private: 
	int m_Password;

public:
	void func()
	{
		m_name = "张三";
		m_Car = "保时捷";
		m_Password = 12345;
	}
};

1.3 struct 和 class区别

说明:在C++中struct 和class唯一的区别在于默认的访问权限不同

区别:

  • struct:默认权限为公共
  • class:默认权限为私有
class C1
{
	int m_a; //默认权限 私有
};

struct C2
{
	int m_a; //默认权限 公共
};
C1 c1;
//c1.m_a = 100; //报错
C2 c2;
c2.m_a = 100; //公共权限,类内类外都可以访问

1.4 成员属性设置为私有

优点:

  • 将所有成员属性设置为私有之后,自己可以控制读写权限
  • 对于写权限,检测数据的有效性
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

/*
	成员属性设置为私有
		1、可以自己控制读写权限 --某些属性的读写
		2、对于以检测数据的有效性  --对于年龄的特殊设置

*/
//设计人类
class Person 
{
public:
	//设置姓名
	void setName(string name)
	{
		m_Name = name;
	}
	//获取姓名
	string getName()
	{
		return m_Name;
	}
	int setAge(int age)
	{
		m_Age = 0;
		if (age < 0 || age>150)
		{
			cout << "你输入的年龄有误,默认设置为0." << endl;
			return m_Age;
		}
		m_Age = age;
	}
	int getAge()
	{
		return m_Age;
	}
	void setLower(string lover)
	{
		m_Lover = lover;
	}

private:
	//姓名   可读可写
	string m_Name;
	//年龄   只读
	int m_Age;
	//情人    只写
	string m_Lover;

};
int main()
{
	system("color 1E");
	Person p;
	p.setName("唐三");
	cout << "输入的姓名:" << p.getName()<<endl;
	//检测数据的有效性
	p.setAge(1000);
	cout << "输入的年龄:" << p.getAge()<<endl; //年龄只读
	p.setLower("小舞");  //情人只能可写
	return 0;
}

练习案例:

1 设计立方体类

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

在这里插入图片描述

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

/*
	立方体的类的设计
		1、创建立方体类
		2、设计属性
		3、设计行为 获取立方体面积和体积
		4、分别利用全局函数和成员函数  判断两个立方体是否相等

*/
class Cube
{

public:
	void setL(double l) {
		m_L = l;
	}
	void setW(double w) {
		m_W = w;
	}
	void setH(double h) {
		m_H = h;
	}
	double getL() {
		return m_L;
	}
	double getW() {
		return m_W;
	}
	double getH() {
		return m_H;
	}
	//获取立方体的面积
	double getArea()
	{
		double area;
		area = 2 * (m_H*m_L + m_H * m_W + m_L * m_W);
		return area;
	}
	//获取立方体的体积
	double getVolu()
	{
		double volume;
		volume = m_H * m_L  * m_W ;
		return volume;
	}
	//利用成员函数判断两个立方体是否相等
	bool isSameByClass(Cube &c)
	{
		if (m_L = c.m_L && m_H == c.m_H && m_W == c.m_W)
		{
			return true;
		}
		return false;
	}

private:
	double m_L, m_W, m_H;



};

//全局函数判断两个函数是否相等
bool isSameByClasss(Cube &c1, Cube &c2)
{
	if (c1.getL() == c2.getL() && c1.getH() == c2.getH() && c1.getW() == c2.getW())
	{
		return true;
	}
	return false;
}

int main()
{
	system("color 1E");
	Cube c,c1,c2;
	c.setL(1);
	c.setW(2);
	c.setH(3);
	c1.setL(2);
	c1.setW(2);
	c1.setH(2);
	c2.setL(2);
	c2.setW(2);
	c2.setH(3);
	cout << "立方体的面积:" << c.getArea() << endl;
	cout << "立方体的体积:" << c.getVolu() << endl;
	bool gouRet = isSameByClasss(c1, c2);
	if (gouRet == true)
	{
		cout << "全局函数的两个立方体是相等的" << endl;
	}
	else
		cout << "全局函数的两个立方体是不相等的" << endl;
	bool ret = c1.isSameByClass(c2);
	if (ret == true)
	{
		cout << "两个立方体是相等的" << endl;
	}
	else
		cout << "两个立方体是不相等的" << endl;
	return 0;
}

2 对象的初始化和清理

  • 生活中有些电子产品都有出厂设置
  • 每个对象初始设置以及对象销毁前的清理数据的设置

2.1 构造函数和析构函数

对象的初始化和清理是重要的安全问题

  • 一个对象或者变量没有初始状态,对其使用后果也是未知的
  • 同样,使用完一个对象或变量,没有及时清理也会造成 一定的安全问题

C++利用构造函数和析构函数解决上述问题,这两个函数会被编译器自动调用 ,完成对象初始化和清理工作。若是不提供构造和析构,编译器会提供构造函数和析构函数是空实现的(自己不写,有人写)

  • 构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动
  • 析构函数:作用于对象销毁前自动调用,执行一些清理 工作。

构造函数语法: 类名(){ }

  • 1 构造函数,没有返回值也不写void
  • 2 函数名称与类明相同
  • 3 构造函数可以有参数,因此可以重载
  • 4 程序在调用对象的时候会自动调用构造,无需 手动调用,且只调用一次

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

  • 1 析构函数,没有返回值不写void
  • 2 函数名称与类名相同,在名称前面加上符合 ~
  • 3 析构函数不可以有参数,因此不能发送重载
  • 4 程序在对象销毁前会自动调用析构,无需手动调用,只调用一次
class Person
{
public:
	Person()
	{
		cout << "正在构造函数!! " << endl;
	}

	~Person()
	{
		cout << "正在析构函数!! " << endl;
	}	
};
void test01() 
{
	Person p;
}
int main()
{
	test01();

	return 0;
}

在这里插入图片描述

2.2 构造函数的分类及调用

两种分类方式:

  • 按照参数分:有参构造和无参构造

    Person()
    	{
    		cout << "无参的构造函数调用!! " << endl;
    	}
    	Person(int a)
    	{
    		age = a;
    		cout << "有参的构造函数调用!! " << endl;
    	}
    
  • 按照类型分:普通构造和拷贝构造

    //拷贝构造函数
    	/*
    		定义:若是有张三这个人,然后用李四去克隆张三这个人的属性
    		不过张三本身属性不能改变,加上 const只读
    	
    	*/
    	Person(const Person &p)
    	{
    		age = p.age;
    	}
    

三种调用方式:

  • 括号法(一般这个比较好 )

    注意:若是无参的函数构造不要写"p1()";这样会被认为是函数的声明

    //1、括号法
    	Person p1;
    	//有参构造函数
    	Person p2(10);
    	cout << "p2的年龄为:" << p2.age << endl;
    	//拷贝构造函数
    	Person p3(p2);
    	cout << "p3的年龄为:" << p3.age << endl;
    

在这里插入图片描述

  • 显示法

    注意:Person(10)若是在左侧,则是匿名对象;执行结束后,系统立马回收匿名对象函数

    Person p1;
    //有参构造函数
    Person p2 = Person(10);
    //拷贝构造函数
    Person  p3 = Person(p2);
    
  • 隐式转换法

    Pensor p4=10; //相当于写了Person p4=Person(10) 有参构造
    Pensor p5=p3; //拷贝构造
    

2.3 拷贝构造函数调用时机

C++拷贝构造函数调用时机通常有三种情况

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

  • 值传递的方式给函数参数传值

    void doWork(Person p)
    {
    }
    void test()
    {
    	Person p; //默认构造函数
        doWork(p);//拷贝构造函数,给函数的p,
    }
    
  • 以值的方式返回局部对象

    Person doWork()
    {
        Person p1;
        return p1; //返回p1的时候,也会产生一个拷贝构造函数。返回会重新生成一个跟p1一样的数据
    }
    void test()
    {
        Person p = doWork();
    }
    

2.4 构造函数调用规则

默认情况下,只要创建类,C++编译器至少给应该类添加3个函数

  • 默认构造函数(无参,为空)
  • 默认析构函数(无参,为空)
  • 默认拷贝析构函数,对属性的值进行拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,C++不提供默认无参构造,但会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,C++不再提供其他构造函数(有参、无参)

2.5 深拷贝与浅拷贝(面试提及)

  • 浅拷贝:简单的赋值拷贝(编译器做)-》会导致堆区的内存重复释放

在这里插入图片描述

  • 深拷贝:在堆区重新申请空间,进行拷贝操作

class Person
{
public:
	int age;
	int * Height;
	//构造函数
	Person()
	{
		cout << "无参的构造函数调用!! " << endl;
	}
	Person(int a,int height)
	{
		age = a;
		//用new在堆区创建一个对象
		Height = new int(height);
		cout << "有参的构造函数调用!! " << endl;
	}
	//自己实现拷贝构造函数 解决浅拷贝带来的问题
	Person(const Person &p)
	{
		cout << "Person 拷贝构造函数调用" << endl;
		age = p.age;
		//Height = p.Height; 编译器默认实现这行代码

		//在堆区重新申请空间,进行深拷贝
		Height = new int(*p.Height);
	}
	//析构函数
	~Person()
	{
		//释放堆区创建的内存
		if (Height != NULL)
		{
			delete Height;
			Height = NULL;
		}
		//作用:析构代码,将堆区开辟数据做释放
		cout << "正在析构函数!! " << endl;
	}
};
//调用
void test01()
{
	//p1走自己的析构,p2也是自己的析构
	Person p1(18, 180);
	cout << "p1的年龄为:" << p1.age << " 身高为:" << *p1.Height << endl; //*表示指针的实际化,输出的是一个值,不是地址
	Person p2(p1);
	cout << "p2的年龄为:" << p2.age << " 身高为:" << *p2.Height << endl;
}

说明:若是使用指针就会在堆区使用内存空间,p1和p2两个对象,然后两个对象指向同一块内存;delete是释放对象指向的内存,这里用浅拷贝会被释放两次。

在这里插入图片描述

2.6 初始化列表

说明:C++提供初始化列表语法,用来初始化属性

语法:构造类名():属性1(值1),属性2(值2) . . . { }

1)传统初始化列表:

class Person
{
public:
	int m_A;
	int m_B;
	int m_C;
	//传统列表赋初值
	Person(int a, int b, int c)
	{
		m_A = a;
		m_B = b;
		m_C = c;
	}
};
void test()
{
	Person p(10, 20, 30);
	cout << "m_A = " << p.m_A << endl;
	cout << "m_A = " << p.m_B << endl;
	cout << "m_A = " << p.m_C << endl;
}

改进之后的:

Person(int a,int b,int c):m_A(a),m_B(b),m_C(c)
{
}

在这里插入图片描述

2.7 类对象作为类成员

说明:C++类中的成员可以是另一个类的对象,称为该成员为对象成员。当其他类对象作为本类对象,构造的时候先构造其他类对象,再构造自身对象。析构的时候先释放自身内存 ,再释放其他对象内存。

//创建手机的类
class Phone
{
public:
	string m_Pname;
	Phone(string name)
	{
		m_Pname = name;
		cout << "正在调用手机类的构造函数" << endl;
	}

	~Phone()
	{
		cout << "正在调用手机类的析构函数" << endl;
	}
};
class Person
{
public:

	string m_Name;
	Phone m_Phone;

	Person(string name, string phone):m_Name(name),m_Phone(phone)
	{
		cout << "正在调用Person类的构造函数" << endl;
	}

	~Person()
	{
		cout << "正在调用Person类的析构函数" << endl;
	}
};
//调用
void test01()
{

	Person p("张三", "苹果14pro");
	cout << p.m_Name << "拿着:" << p.m_Phone.m_Pname << "手机" << endl;
	 
}

在这里插入图片描述

2.8 静态成员

静态成员就是想在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

1)静态成员变量

  • 所有对象共享一份数据(若是p1.a=100,若是p2也调用,p2.a=200,则p1.a=200;a是共享的)
  • 在编译阶段分配内存
  • 类内声明,类外初始化(在类外声明一个类内定义的属性,初始化:int Person::a=100),类外访问不到私有静态成员变量。
  • 私有成员变量访问不到
class Person
{
public:
	static int m_Age;
};
int Person::m_Age = 100;

Person p;
//访问方式一:通过对象
cout << p.m_Age << endl; ->100
//访问方式二:通过类名
cout<<Person::m_Age<<endl; ->100
Person p1;
p1.m_Age = 200;
cout << p.m_Age << endl; ->200 //修改了

2)静态成员函数

  • 所有对象共享一个函数
  • 静态成员函数只能访问静态成员变量,非静态成员变量函数,无法区分到底是那个对象的属性;而静态成员变量所有人共享一份,不属于任何人。
  • 私有成员函数访问不到

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/367121.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go gin学习记录5

有了前面几节的学习&#xff0c;如果做个简单的web服务端已经可以完成了。 这节来做一下优化。 我们实验了3种SQL写入的方法&#xff0c;但是发现每一种都需要在方法中去做数据库链接的操作&#xff0c;有些重复了。 所以&#xff0c;我们把这部分提取出来&#xff0c;数据库链…

【C/C++】中【typedef】用法大全

总结一下typedef用法&#xff0c;一共七种&#xff0c;分别是&#xff1a;为基本数据类型起别名、为结构体起别名、为指针类型起别名、为数组类型起别名、为枚举类型起别名、为模版函数起别名。 目录 一、为基本数据类型起别名 二、为结构体起别名 三、为指针类型起别名 四…

Windows应用之——设置定时关机

一 概述 本文介绍window设置自动关机的两种方式&#xff1a; cmd指令设置自动关机任务计划程序设置自动关机第三方定时关机软件 二 cmd指令设置自动关机—不推荐 2.1 自动关机-开启(管理员模式下) 依次点击‘“开始”&#xff0c;在“搜索程序和文件”中输入cmd&#xff0c…

Python GDAL读取栅格数据并基于质量评估波段QA对指定数据加以筛选掩膜

本文介绍基于Python语言中gdal模块&#xff0c;对遥感影像数据进行栅格读取与计算&#xff0c;同时基于QA波段对像元加以筛选、掩膜的操作。本文所要实现的需求具体为&#xff1a;现有自行计算的全球叶面积指数&#xff08;LAI&#xff09;.tif格式栅格产品&#xff08;下称“自…

关于Transformer的一些问题总结

一些与Transformer模型相关的问题总结&#xff0c;有不对的欢迎指出。 &#x1f4a1; 残差网络为何可以解决梯度消失 对比1和2可以发现&#xff0c;对于普通网络&#xff0c;当有几个偏导很小的时候&#xff0c;梯度会迅速趋近于0&#xff1b;而对于残差网络&#xff0c;要趋近…

基于appium的app自动化测试框架

App自动化测试主要难点在于环境的搭建&#xff0c;appium完全是基于selenium进行的扩展&#xff0c;所以app测试框架也是基于web测试框架开发的 一、设备连接 &#xff08;即构建基础的测试环境&#xff0c;保证可以驱动设备进行操作&#xff09; 0.准备测试环境 1&#xff0…

第09章_MySQL子查询

第09章_子查询 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从MySQL 4.1开始引入。 SQL 中子查询的使用大大增强了…

Spring MVC 源码 - HandlerAdapter 组件(一)之 HandlerAdapter

HandlerAdapter 组件HandlerAdapter 组件&#xff0c;处理器的适配器。因为处理器 handler 的类型是 Object 类型&#xff0c;需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变&#xff0c;比如用户的处理器可以实现 Controller 接口或者 HttpReques…

从零开始学typescript

https://coding.imooc.com/learn/list/412.html 公司花钱买的&#xff0c;我边学边做笔记 设置 vscode设置 然后下个Prettier - Code formatter 以后保存就能格式化了 下载ts npm install typescript3.6.4 -g ts版本 npm install -g ts-node8.4.1 node执行ts文件 这样&a…

_linux (TCP协议通讯流程)

文章目录TCP协议通讯流程TCP 和 UDP 对比TCP协议通讯流程 下图是基于TCP协议的客户端/服务器程序的一般流程: 服务器初始化: 调用socket, 创建文件描述符;调用bind, 将当前的文件描述符和ip/port绑定在一起;如果这个端口已经被其他进程占用了, 就会bind失 败;调用listen, 声…

FPGA入门系列15--SPI(文末有易灵思核心板及配套下载线)

文章简介 本系列文章主要针对FPGA初学者编写&#xff0c;包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解&#xff0c;旨在更快速的提升初学者在FPGA开发方面的能力&#xff0c;每一个章节中都有针对性的代码…

国家推进招投标全过程电子签,契约锁帮助组织减负91%

根据某工程建设集团反馈&#xff0c;电子签章的应用帮助招投标工作实现&#xff1a;“参与方5分钟内线上实名认证&#xff1b;招标、中标通知等格式文件最快2分钟完成盖章&#xff1b;标书等大体量文件20分钟内盖章生成&#xff1b;专家实名认证远程评标、10分钟完成线上开标&a…

leetcode 502. IPO(上市,3种方法)

假设leetcode 即将上市&#xff0c;如何筹集资金的问题。 有两个数组profits和capital, 分别代表第 i 个项目有多少净利润 和 需要多少启动资金。 手上的原始资金是w, 用这个w的资金去启动项目&#xff0c;完成项目之后净利润会加到w上&#xff0c;再做下一个项目&#xff0c; …

硬件原理图中的“英文缩写”大全

设计原理图时&#xff0c;网络标号要尽量简洁眀了。本文总结了一下基本的表示方法&#xff0c;供大家参考。常用控制接口 EN&#xff1a;Enable&#xff0c;使能。使芯片能够工作。要用的时候&#xff0c;就打开EN脚&#xff0c;不用的时候就关闭。有些芯片是高使能&#xff0c…

SPI机制源码:JDK Dubbo Spring

JDK 17 Dubbo 3.1.6 JDK SPI JDK SPI在sql驱动类加载、以及slf4j日志实现加载方面有具体实现。 示例 public class Test {private static final Logger logger LoggerFactory.getLogger(Test.class);public static void main(String[] args) {ServiceLoader<JdkSpiServi…

软件测试计划怎么写?模板在这呢

目录 第1章 引言 第2章 项目背景 第3章质量目标 第4章 资源需求 第5章 测试策略 第6章 测试计划 总结感谢每一个认真阅读我文章的人&#xff01;&#xff01;&#xff01; 重点&#xff1a;配套学习资料和视频教学 第1章 引言 1.1目的 简述本计划的目的&#xff0c;旨…

【THREE.JS学习(3)】使用THREEJS加载GeoJSON地图数据

本文接着系列文章&#xff08;2&#xff09;进行介绍&#xff0c;以VUE2为开发框架&#xff0c;该文涉及代码存放在HelloWorld.vue中。相较于上一篇文章对div命名class等&#xff0c;该文简洁许多。<template> <div></div> </template>接着引入核心库i…

RPC(3)--基于 Nacos 的服务发现与负载均衡版

nacos:提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。 nacos架构如下(图片来源) 依赖包&#xff1a; <dependency>…

国内领先的十大API接口排行

应用程序编程接口API即&#xff08;Application Programming Interface&#xff09;&#xff0c;现在众多企业的应用系统中常用的开放接口&#xff0c;对接相应的系统、软件功能&#xff0c;简化专业化的程序开发。 一、百度API 百度API超市开通1136个数据服务接口。 网址&a…

git 的使用方法 (下 - 远程仓库和图形化)

目录前言&#xff1a;一、什么是协同开发二、Gitee 使用协同开发1. 首先注册一个码云账号2. 新建一个仓库3. 根据下图把新建仓库设置为开源4. 在远端合并分支的方法5. 链接 git 远程6. 提交&#xff08;同步&#xff09;远程7. 远程拉取至本地8. 远程分支三、git 图形化的使用1…