(个人笔记)C++类与对象【封装】

本文详细介绍了C++中类与对象的基本概念、构造函数、析构函数、深拷贝与浅拷贝等内容,并深入探讨了类的权限、成员变量与函数的存储空间等核心知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


类与对象有三个重要的特性:封装,继承,多态

基本概念

  • 具有相同性质的对象,抽象为类
  • 类中的属性和行为,统一称为成员
  • 属性(成员属性)(成员变量)
  • 行为(成员函数)(成员方法)

封装中的权限:

作用域

  • public 公共权限 【类内可访问,类外可访问】
  • protected 保护权限 【类内可访问,类外不可访问】【子类可以访问父类中的保护内容】
  • private 私有权限 【类内可访问,类外不可访问】【子类不可以访问】

在类中,一般将成员属性设为私有,用函数提供对外的接口。

优点:

  • 可以自己控制读写的权限
  • 对于写权限,我们可以检测数据的有效性(比如年龄不可以输入-10,只能输入我们规定的范围)

在类中可以让另一个类作为本来类的成员

构造函数

用于对象的初始化
语法

  • 类名(){} 对成员进行初始化操作
  • 不用写void,没有返回值
  • 函数名称与类相同
  • 可以有参数,因此可以发生重载
  • 调用对象时,自动调用构造,无需手动调用,而且只会调用一次

构造函数的分类与调用

两种分类方式
按参数分:有参构造和无参构造(默认构造)

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

//创建一个person类
class person
{
public:
	//有参构造函数
	person(int age, string name)
	{
		m_age = age;
		m_name = name;
	}
	//无参构造
	//重载
	person()
	{
		m_age = 10;
		m_name = "小芳";
	}
	int m_age;
	string m_name;
};
int main()
{
	//创建一个对象
	//调用有参构造
	person p1(18, "小滨");
	//调用无参构造
	person p2;
	cout << "P1:" << endl;
	cout << "姓名:" << p1.m_name << " 年龄:" << p1.m_age << endl;
	cout << "P2:" << endl;
	cout << "姓名:" << p2.m_name << " 年龄:" << p2.m_age << endl;

	system("pause"); 
	return 0;
}

在这里插入图片描述

按类型分:拷贝构造函数(person(const person&p)),普通构造函数

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

//创建一个person类
class person
{
public:
	//拷贝构造函数
	person(const person&p)
	{
		m_age = p.m_age;
		m_name = p.m_name;
	}
	//普通构造函数
	//重载
	person()
	{
		m_age = 21;
		m_name = "小滨";
	}
	int m_age;
	string m_name;
};
int main()
{
	//创建一个对象
	//调用普通构造
	person p2;
	//调用拷贝构造
	person p1(p2);
	cout << "P1:" << endl;
	cout << "姓名:" << p1.m_name << " 年龄:" << p1.m_age << endl;
	cout << "P2:" << endl;
	cout << "姓名:" << p2.m_name << " 年龄:" << p2.m_age << endl;

	system("pause"); 
	return 0;
}

在这里插入图片描述
注意事项:

  • 匿名对象 person(10);当前行 执行完毕后,系统会立即回收掉匿名对象
  • 不要利用拷贝函数,初始化匿名对象

构造函数的调用规则

  • 只要创造一个类,C++编译器会给每个类添加至少三个函数
  • 无参构造
  • 有参构造
  • 拷贝构造(值拷贝)

如果用户提供有参,则编译器不提供无参,但是还是会有拷贝构造
如果用户写了一个拷贝构造,则编译器不提供有参和无参

类中构造函数初始化列表

直接看代码

#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:	
	//构造
	person(int age):m_age(age)
	{
		cout << "调用有参构造" << endl;
	}
	
	~person()
	{
		cout << "调用析构函数" << endl;
	}
	int m_age;
};

void test()
{
	person p(18);
	cout << "年龄:" << p.m_age << endl;
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

析构函数

用于对象的清理
语法

  • ~类名(){} 进行清理操作
  • 不用写void,没有返回值
  • 函数名称为在类名称前加~
  • 不可以有参数,无法重载
  • 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:	
	//构造
	person()
	{
		cout << "调用无参构造" << endl;
	}
	
	~person()
	{
		cout << "调用析构函数" << endl;
	}
	
};

void test()
{
	person p;
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述

深拷贝与浅拷贝

背景:
在堆区开辟空间,析构函数进行释放时会重复释放堆区内存。

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

//创建一个person类
class person
{
public:
	
	//有参构造
	person(int age,int height)
	{
		m_age = age;
		//将数据创建在堆区
		m_height = new int(height);
		cout << "调用有参构造" << endl;
	}
	person(const person& p)
	{
		m_age = p.m_age;
		//编译器自己写的浅拷贝,导致队去内存重复释放
		//m_height = p.m_height;
		//自己写深拷贝
		m_height = new int(*p.m_height);
	}
	~person()//释放堆区内存
	{
		if (m_height != nullptr)
		{
			delete m_height;
			m_height = nullptr;
		}	
	}
	int m_age;
	int *m_height;
};

int main()
{
	person p1(10,170);
	cout << "年龄" << p1.m_age << " 身高:" << *p1.m_height << endl;
	//编译器浅拷贝
	person p2(p1);
	cout << "年龄" << p2.m_age << " 身高:" << *p2.m_height << endl;
	system("pause"); 
	return 0;
}

在这里插入图片描述

浅拷贝:简单的赋值拷贝操作 => 堆区的内存重复释放。

	person(const person& p)
	{
		m_age = p.m_age;
		//编译器自己写的浅拷贝,导致队去内存重复释放
		m_height = p.m_height;
	}

深拷贝:在堆区重新申请空间,进行拷贝构造 =>可解决堆区内存重复释放。

	person(const person& p)
	{
		m_age = p.m_age;
		//编译器自己写的浅拷贝,导致队去内存重复释放
		//m_height = p.m_height;
		//自己写深拷贝,在堆区开辟一块内存,进行拷贝构造
		m_height = new int(*p.m_height);
	}

静态成员变量

  • 所有对象共享一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化

`

静态成员变量不属于某一对象上,所有对象都共享这份数据
两种访问方式
按照类名访问

  • 如果该成员变量为公有的可以访问
  • 如果为保护或私有的不可以访问

按照对象访问

  • 如果该成员变量为公有的可以访问
  • 如果为保护或私有的不可以访问
#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:	
	person()
	{
		cout << "调用构造函数" << endl;
	}
	~person()
	{
		cout << "调用析构函数" << endl;
	}

	static int m_age;
};
//类内声明,内外初始化
int person::m_age = 18;
void test()
{
	//所有成员共用一份数据 m_age=18
	//两种方式
	//根据类名访问
	cout << "年龄:" << person::m_age << endl;
	//根据对象访问
	person p1;
	cout << "p1的年龄:" << p1.m_age << endl;
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量(在类内声明,在类内实现)

两种访问方式
按照类名访问

  • 如果该成员函数为公有的可以访问
  • 如果为保护或私有的不可以访问

按照对象访问

  • 如果该成员函数为公有的可以访问
  • 如果为保护或私有的不可以访问
#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:	
	person()
	{
		cout << "调用构造函数" << endl;
	}
	~person()
	{
		cout << "调用析构函数" << endl;
	}
	static void set_age(int age)
	{
		m_age = age;
	}
	static int m_age;
};
//类内声明,内外初始化
//必须要有初始化
int person::m_age = 18;

void test()
{
	//所有成员共享同一个函数
	//两种方式
	//根据类名访问
	person::set_age(20);
	cout << "p2的年龄:" <<person::m_age << endl;
	//根据对象访问
	person p1;
	p1.set_age(21);
	cout << "p1的年龄:" << p1.m_age << endl;	
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述

类和对象的存储空间

空对象占用空间为1个字节,因为每个空对象也该有一个独一无二的地址

#include <iostream>
using namespace std;

//创建一个person类
class person
{

};

void test()
{
	cout <<"空对象为:"<< sizeof(person)<<"个字节" << endl;
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述
成员变量和成员函数分开存储

只有一个成员变量时,占四个字节

//创建一个person类
class person
{
public:
	//void set(int A)
	//{
	//	m_A = A;
	//}
	int m_A;
};

void test()
{
	cout << "该类占" << sizeof(person) << "个字节" << endl;
}

在这里插入图片描述
加一个成员函数时,还是占四个字节
成员函数不属于对象上

//创建一个person类
class person
{
public:
	void set(int A)
	{
		m_A = A;
	}
	int m_A;
};

void test()
{
	cout << "该类占" << sizeof(person) << "个字节" << endl;
}

在这里插入图片描述

this指针

编译器内置指针,指向被调用的成员函数所属对象
this指针的本质是指针常量
常用场景(用于初始化)

#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:
	person(int a)
	{
		this->m_A = a;
	}
	int m_A;
};

void test()
{
	person p(10);
	cout << "年龄:" << p.m_A << endl;
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述
返回对象本身用 * this;

空指针调用成员函数

注意this指针的存在
会存在this指向空指针的操作,加上以下代码可解决

	void setAge(int age)
	{
		if (this == nullptr)
		{
			return;
		}
		m_age = age;//这里默认写的是this->m_age=age;
	}

全部测试代码如下:

#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:
	void showmess()
	{
		cout << "这是一个person类" << endl;
	}
	void setAge(int age)
	{
		if (this == nullptr)
		{
			return;
		}
		m_age = age;//这里默认写的是this->m_age=age;
	}
	int m_age;
};

void test()
{
	person *p=nullptr;
	p->showmess();
	p->setAge(18);//this 指向空指针

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

在这里插入图片描述

常函数

const修饰成员函数
在成员函数的后面加一个const关键字,修饰的是this指向让指针指向的值也不可以改变。

1.改变this指向,报错

	void setAge(int age)const
	{
		this->m_age = age;
	}
	int m_age;

在这里插入图片描述
2.在定义变量前加一个mutable关键字,即可改变该变量的值。

#include <iostream>
using namespace std;

//创建一个person类
class person
{
public:

	void setAge(int age)const
	{
		this->m_age = age;
	}
	//int m_age;
	mutable int m_age;
};

void test()
{
	person p;
	p.setAge(18);
	cout << p.m_age << endl;

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

在这里插入图片描述

常对象

常对象只可以调用常函数

	//关键代码
	void setAge(int age)const
	const person p1;
	p1.setAge(18);

友元函数

关键字 friend
作用:私有属性让某些外部人员访问

全局函数做友元

#include <iostream>
using namespace std;

//创建一个person类
class person
{
	//写在类里面的最上面,前面加一个关键字friend
	friend void viewFriend(person* p);
public:
	person()
	{
		this->m_height = 170;
		this->m_age = 18;
		
	}
	int m_age;
private :
	int m_height;
};
void viewFriend(person *p)
{
	cout <<"年龄:" << p->m_age << endl;
	//可以调用私有成员 m_height
	cout << "身高:" << p->m_height << endl;
}
void test()
{
	person p;
	viewFriend(&p);
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述

类做友元

#include <iostream>
using namespace std;


//创建一个person类
class person
{
	//写在类里面,前面加一个关键字friend
	friend class fri;
public:
	person()
	{
		this->m_height = 170;
		this->m_age = 18;	
	}
	int m_age;
private :
	int m_height;
};
class fri
{
public:
	fri()
	{
		//创建一个person类
		p = new person;
	}
	void visit()
	{
		cout << p->m_age << endl;
		cout << p->m_height << endl;
	}
	~fri()
	{
		delete p;//析构函数清除堆区内存
	}
	person* p;
};
void test()
{
	fri f;
	//访问person的私有成员
	f.visit();
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述

成员函数做友元

#include <iostream>
using namespace std;
class building;
class goodgay
{
public:
	goodgay();
	//释放内存
	~goodgay()
	{
		if (b != nullptr)
		{
			delete	b;
			b = nullptr;
		}
	}
	void vis();
	building* b;
};
class building
{
	//友元成员函数
	 friend void goodgay::vis();
public:
	building();
	
public:
	string m_sit;
private:
	string m_bed;
	int m_age;
};
/*
*类内函数的类外实现
*/
building::building()
{
	m_sit = "客厅";
	m_bed = "卧室";
	m_age = 18;
}
goodgay::goodgay()
{
	b = new building;
}
void goodgay::vis()
{
	cout << b->m_sit << endl;
	cout << b->m_bed << endl;
	cout << b->m_age << endl;
}
//测试函数
void test()
{
	goodgay g;
	g.vis();	
}
int main()
{
	test();
	system("pause"); 
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值