【C++】类和对象 (第一弹)

类具有三大特性:封装、继承、多态

c++中万物皆可为对象,对象有属性和行为

例如:汽车可以看作对象,属性有:发动机、轮胎、方向盘等; 行为有:放歌、载人等

具有相同属性的也可以抽象为类

一、封装

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

行为是以函数形式出现

例如:设计一个学生类,包括姓名学号,可以赋值,也可以打印

cpp 复制代码
#include<string>
using namespace std;

class Student
{
	//设置访问权限
    // public 是公共权限
public:

	// 属性
	string name;
	long S_ID;

	// 行为
    // 打印姓名和学号
	void Print()
	{
		cout << "姓名:" << name << " 学号:" << S_ID << endl;
	}

	//也可通过行为对属性赋值
	void set_name(string N)
	{
		name = N;
	}

	void set_ID(long ID)
	{
		S_ID = ID;
	}

};


int main()
{
	// 创建具体的学生 (对象)也叫实例化对象
	Student S1;
	// 对实例化对象进行赋值
	S1.name = "李四";
	S1.S_ID = 2025001;

	S1.Print();
	// 创建第二个对象并通过行为赋值
	Student S2;
	S2.set_name("康康");
	S2.set_ID(2001007);

	S2.Print();

	return 0;
}

注意:

1、类中的属性和行为,统一称为 成员

2、属性又叫 : 成员属性、成员变量

3、行为又名: 成员函数、成员方法

2、访问权限:

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

2、protected: 保护权限 类内可以访问,类外不可访问 (子可以访问父的保护内容)

3、private: 私有权限 类内可以访问,类外不可访问 (only myself)

cpp 复制代码
class Peo
{
	//公共权限
	// 人的名字
public:
	string name;

	// 保护权限,类内可访问
	// 大门密码
protected:
	long password;

	//私有权限,类内可访问
	// 资产
private:
	double money;

	//类内  均可访问
public:
	void fun()
	{
		name = "六六";
		password = 321123;
		money = 555555.55;
	}

};

int main()
{
	Peo P2;
	// 在类外 只能访问公共部分,保护和私有不能访问修改
	P2.name = "往往";

	return 0;
	
}

3、struct 与 class 的不同

在c++中唯一区别:默认访问权限不同

struct 默认 公共权限

class 默认 私有权限

4、成员属性设置为私有 (private)

优点:

1、可以自己控制读写权限

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

cpp 复制代码
class Peo
{
public:

	// 写名字
	void set_name(string N)
	{
		name = N;
	}
	//读名字
	string prit_name()
	{
		return name;
	}
	//读年纪
	int prit_age()
	{
		return age;
	}
	// 写最爱
	void set_love(string L)
	{
		love = L;
	}

	// 写年龄,并且判断是否符合(0~150)
	void set_age(int A)
	{
		// 判断是否符合
		if (A > 0 && A < 150)
			age = A;
		else
			cout << "设置失败,年龄不合实际";

	}

	// 私有权限,类内可访问,类外不行
private:
	string name;  //可读可写
	int age=20; // 只读不写
	// ②、可写年龄,但是有范围
	string love; // 只写不读

};

int main()
{
	Peo p;
	// 直接访问 p1中的成员不行,private

	//读写姓名
	p.set_name("张三");
	cout<<p.prit_name()<<endl;

	// 设置年龄
	int A = 0;
	cin >> A;
	p.set_age(A);

	//读年龄
	cout<<p.prit_age()<<endl;

	//写最爱
	p.set_love("火锅");


	return 0;
}

利用,类内可以访问私有和保护的功能,可以完成,读写功能。

补充: 在类外设置类内私有成员,然后设置呢?

例题:

利用 ::符号就可以达到这种效果

cpp 复制代码
#include<iostream>
using namespace std;
// 定义长方形类
class oblong
{
private:
	int width;
	int length;

	//权限要是公共的
public:
	//函数声明
	void set(int w, int l);
	int alcircle();
	int calarea();

};

//函数定义
// 赋值函数
void oblong::set(int w, int l)
{
	width = w;
	length = l;
}

// 计算周长
int oblong::alcircle()
{
	return 2 * (width + length);
}

//计算面积
int oblong::calarea()
{
	return width * length;
}

int main()
{
	int w, l;
	cin >> w >> l;
	oblong ob;
	ob.set(w, l);
	cout << ob.alcircle() << endl
		<< ob.calarea() << endl;

	return 0;
}

补充:

::是C++里的作用域分解运算符,"比如声明了一个类A,类A里声明了一个成员函数voidf(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成voidA::f(),表示这个f()函数是类A的成员函数,用这个函数时,通过这个类的变量去找到它。

【 :: 】的用法请转这位博主



二、对象的初始化和清理

1、构造函数和析构函数

这两个函数会被编译器自动调用,完成对象初始化和清理工作,如果我们不提供,编译器会自己提供,但是是空实现,内容为空。

构造函数:主要作用在于创建对象时为对象的成员赋值,有编译器自动调用;

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

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

(1)没有返回值也不用写void

(2)函数名与类名相同,可以有参数,能发生重载

(3)程序调用对象时,自己调,无需手动,只会调用一次

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

(1)没有返回值,不写void ,加~,与类名相同

(2)不可以有参数,不能发生重载

(3)程序销毁前,自己调用,无需手动,只调一次

cpp 复制代码
#include<iostream>
using namespace std;

class stu
{

public:
	stu()
	{
		cout << "stu 的构造函数" << endl;
	}

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

void test_1()
{
	stu p;
}

int main()
{
	// 函数调用,用完释放
	test_1();
	// 主函数调用结束释放
	stu p;

	system("pause");

	return 0;
}

可以观察到,第二次的调用,结束后释放,才调用了析构函数。

2、构造函数的分类和调用

2种分类方式:

按参数分为:有参构造和无参构造

按类型分为:普通构造和拷贝构造

【拷贝构造语法: 类名(const 类名 & 变量)】

3种调用方式:

括号法

显示法

隐式转换法

cpp 复制代码
#include<iostream>
using namespace std;


class Person
{
public:
	// 普通构造函数 :有参和无参
	Person()
	{
		cout << "Person的无参构造函数" << endl;
	}

	// 初始化列表  语法: 构造函数():属性1(值1),属性2(值2)...
	Person(int i):age(i)
	{
		// 相当于 age=i;
		cout << "Person的有参构造函数" << endl;
	}
	// 拷贝构造函数
	Person(const Person& p)
	{
		age = p.age;

		cout << "Person的拷贝构造函数" << endl;
	}


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

	int age;

};

int main()
{
	// 1、括号法
	Person p1;
	Person p2(100);
	Person p3(p2);
	cout << "------------------" << endl;
	// 注意1:调用无参构造的时候,不要加() ,
	//编译器会认为是函数声明  Person p1()  ❌

	// 2、显示法
	Person pp = Person(100);
	Person pp1 = Person(pp);

	Person(10); // 匿名对象,执行这行后,立即释放
	cout << "---------------------" << endl;
	// 注意2: 不要用拷贝构造函数,初始化匿名对象
	// 编译器会认为: Person(pp1)==Person pp1  对象声明

	// 3、隐式转换法
	Person ppp = 10;  //相当于 Person ppp=Person(10)
	Person ppp1 = ppp;

	cout << "---------------------" << endl;

	return 0;
}

①、构造函数调用原则

1、创建一个 类 ,编译器就会给它分配添加至少3个函数:

默认构造(空实现)、析构函数(空实现)、拷贝构造(值拷贝)

2、若 我们写了 有参构造函数,编译器就不会给类提供默认构造函数啦,依然提供拷贝构造函数

3、若 我们写了 拷贝构造函数,编译器就不提供其 他普通构造函数

总结:(等级划分)拷贝>有参>默认

对于我们写的构造函数,编译器就不会提供更低级的,会提供更高级的


②、拷贝构造函数的调用时机

1、用已有对象初始化一个新对象

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

3、值的方式返回

值传递的 实质是 拷贝临时副本

cpp 复制代码
#include<iostream>
using namespace std;

class Stu
{
public:
	Stu()
	{
		cout << "Stu 默认构造函数" << endl;
	}
	Stu(const Stu& p)
	{
		sore = p.sore;
		cout << "Stu 拷贝构造函数" << endl;
	}

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

	int sore;
};
// NO.2 值传递的方式给函数参数传值
void operate(Stu pp)
{
	cout << "NO.2 值传递-拷贝临时数据" << endl;
}

void test_1()
{
	Stu p;
	operate(p);
}
// 3、值方式返回
Stu retu()
{
	Stu p1;
	return p1;
	// 返回时,按照p1 拷贝一个新的对象,返回给函数外
}

void test_2()
{
	Stu p= retu();
}

int main()
{
	test_1();
	cout << "------------" << endl;
	test_2();

	return 0;
}

③、深拷贝与浅拷贝

|-----|----------------|-----------|
| 浅拷贝 | 简单赋值拷贝 | 编译器默认提供的 |
| 深拷贝 | 在堆区重新申请空间,进行拷贝 | 自己写的深拷贝操作 |

析构函数:可以 释放在堆区开辟的空间(delete)

cpp 复制代码
#include<iostream>
using namespace std;

class peo
{
public:
	peo()
	{
		cout << "peo 的默认构造函数" << endl;
	}
	peo(int a, int h)
	{
		age = a;
		hight = new int(h);
		cout << "peo 的有参构造函数" << endl;

	}

	~peo()
	{
		// 释放身高指针指向的堆区空间
		if (hight != NULL)
		{
			delete hight;
			hight = NULL;
		}

		cout << "peo 的析构函数" << endl;
	}

	// 如果我们没有这一步操作,编译器会提供一个拷贝构造函数
	// 实现 hight=p.hight 进行的是地址传递,运行时会发生错误
	peo(const peo& p)
	{
		// 深拷贝,在堆区开辟空间,进行拷贝
		age = p.age;
		hight = new int(*p.hight);
		cout << "peo 的深拷贝" << endl;
	}

	int age;
	int* hight; // 身高指针,接收在堆区开辟的地址

};

int main()
{
	peo p1(10, 175);
	peo p2(p1);

	cout << "p1的年龄和身高:" << p1.age << " " << *p1.hight << endl;
	cout << "p2的年龄和身高:" << p2.age << " " << *p2.hight << endl;

	return 0;
}

如若我们没有自己定义拷贝函数进行深拷贝,编译器会给我们提供一个拷贝函数,进行浅拷贝

(hight=p.hight),会发生如下错误 👇:

如果我们写了可以进行深拷贝的拷贝构造函数,就可以避免↑上面情况的发生



④、类对象作为类成员

class A{};

Class B

{

A member;

};

B类中有A来作为成员,它俩创建的顺序是怎么样的呢?

先有A,再有B;释放的时候 先释放 B,后释放A

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

class cloth
{
public:
	cloth(string p)
	{
		c_name = p;
		cout << "cloth 的构造函数" << endl;
	}
	~cloth()
	{
		cout << "cloth 的析构函数" << endl;

	}

	string c_name;
};

class Person
{
public:
	// cl(b) 相当于 cloth cl=b;隐式转换法
	Person(int a, string b) :age(a), cl(b)
	{
		cout << "Person 的构造函数......" << endl;
	}
	~Person()
	{
		cout << "Person 的析构函数......" << endl;

	}

	int age;
	cloth cl;
};

int main()
{
	Person p1(24, "亮晶晶");
	cout << "年龄:" << p1.age << " 衣服品牌:" << p1.cl.c_name << endl;

}

结果如下:

【当其他类对象作为本类成员,构造时先构造类对象,再构造本类】

析构时 与之相反 👇👇

⑤、静态成员

静态成员 就是 在成员前面+static

静态成员也都有访问权限,在public 的属性下,才能随意的调用,private下面不行了。

一、静态成员变量:

1、所有对象都共享同一份数据

2、编译阶段就分配内存

3、类内声明,类外初始化

注意: 静态成员变量 不属于某个对象上,所有对象都共享同一份数据

静态成员变量有2种访问方式:

1、通过对象

2、通过类名

cpp 复制代码
#include<iostream>
using namespace std;

class home
{
public:
	// 类内声明,类外初始化
	static int people;
};
// 类外 初始化
int home::people = 4;

// 静态成员变量的两种访问方法
void test_001()
{
	// 一、通过对象
	home t1;
	t1.people = 2;
	cout << "通过对象:" << t1.people << endl;
	
	// 二、通过类名
	cout << "通过类名 :" << home::people << endl;

}

int main()
{
	home h1;
	cout << "h1 家庭成员个数为:" << h1.people << endl;

	home h2;
	h2.people = 6;
	cout << "h2 家庭成员个数为:" << h1.people << endl;

	home h3;
	cout << "h3 家庭成员个数为:" << h1.people << endl;

	test_001();

	return 0;
}

结果: 👇 👇

二、静态成员函数

1、所有对象共享一个函数

2、静态成员函数只能访问静态成员变量

因为无法区分是哪个对象的非静态成员变量,静态成员变量是大家共有的,所以都可以访问

静态成员函数调用方法与👆一样

cpp 复制代码
class AAA
{
public:

	//静态成员函数
	static void fun()
	{
		// 可以调用 静态成员变量
		A = 100;

		// 不可以调用 非静态成员变量
		// 因为不知道,这个B是对象p1的B呀,还是对象p2的B呀
		// 人家A 是大家共有的,都可以使用,你B可是一人一个,个人财产
		//B = 100; 错误

	}

	// 静态成员变量
	static int A;
	// 非静态成员变量
	int B;

};

// 静态成员变量,类外声明
int AAA::A = 10;

第一弹 就此先结束啦!!!!

相关推荐
PHASELESS4111 分钟前
Java堆结构深度解析:原理、实现与应用全指南
java·开发语言·数据结构
DXM05219 分钟前
牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记2
开发语言·javascript·笔记·学习·arcgis·ae
啥都鼓捣的小yao20 分钟前
实战5:Python使用循环神经网络生成诗歌
开发语言·人工智能·python·rnn·深度学习
Monly2122 分钟前
Uniapp:列表选择提示框
开发语言·javascript·uni-app
HelloDam34 分钟前
912. 排序数组 超级通俗易懂、全面的快速排序教程(优化重复元素、实例有序问题)
后端·算法·排序算法
梦の38 分钟前
C++Cherno 学习笔记day21 [86]-[90] 持续集成、静态分析、参数计算顺序、移动语义、stdmove与移动赋值操作符
c++·笔记·学习
HelloDam41 分钟前
leetcode51.N 皇后 回溯算法求解 + 效率优化
后端·算法
DataFunTalk1 小时前
30位数据科学家集结完毕,揭晓大模型时代数据科学的“晋级之路”
前端·后端·算法
夜月yeyue1 小时前
STM32启动流程详解
linux·c++·stm32·单片机·嵌入式硬件·c#
ん贤1 小时前
图论基础理论
c语言·数据结构·c++·算法·图论