C++:类与对象(一)封装、对象的初始化与清理

目录

一、概述

二、封装

1.封装的意义

2.访问权限

三、对象初始化与清理

1.概念认识

2.构造函数和析构函数

3.构造函数的分类与调用

4.拷贝构造函数使用时机

5.构造函数调用规则

6.深拷贝与浅拷贝

7.初始化列表

8.类对象作为类成员

9.静态成员

静态成员变量

静态成员函数


一、概述

在C++中的面向对象中有三大特性:

        封装、继承、多态

C++认为万物皆为对象,对象是系统中用来描述客观事物的一个实体,它是用来构成系统的一个基本单位。对象有自己的属性行为。具有相同性质的对象,我们可以抽象成

举例:

对象名称属性行为
人(类)姓名、年龄、身高...走、坐、躺、吃...
车(类)车牌、颜色...发动、载人...

二、封装

1.封装的意义

封装的意义:

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

实例化:通过一个类创建一个对象的过程

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

实例:写一个学生信息的类,并创建对象使用功能。访问权限为public公共。

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

class student {
public:
	string s_name;
	int s_id;

	//显示
	void show_information() {
		cout << "姓名:" << s_name << " 学号:" << s_id << endl;
	}
	//重置
	void set_information(string name, int id) {
		s_name = name;
		s_id = id;
	}
};

int main() {
	student s1;
	s1.s_name = "张三";
	s1.s_id = 1;
	s1.show_information();
	s1.set_information("李四", 2);
	s1.show_information();

	system("pause");
	return 0;
}

2.访问权限

public公共权限:

        类内可以访问,类外也可以访问;

protected保护权限:

        类内可以访问,类外不可以访问;

private私有权限:

        类内可以访问,类外不可以访问;

设计以下类:

class home {
public:
	string address;
protected:
	string car_name;
private:
	string password;

public:
	void set_information() {
		address = "王府井";
		car_name = "奔驰";
		password = "123123";
	}
};

调用各个数据,尝试是否会报错:

	home h1;
	h1.set_information();//正确
	h1.address = "昌平";//正确
	h1.car_name = "大众";//错误
	h1.password = "123456";//错误

因为类内的函数可以访问私有权限,所以可以控制类内数据的读与写的权限:

class person {
private:
	string p_name;
	int p_age;

public:
	//可写不可读
	void set_name(string name) {
		p_name = name;
	}
	//可读不可写
	int obtain_age() {
		return p_age;
	}
};

三、对象初始化与清理

1.概念认识

  • 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。

2.构造函数和析构函数

对象的初始化和清理也是两个非常重要的安全问题

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

c++利用了构造函数析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造函数和析构函数,编译器会提供,编译器提供的构造函数和析构函数是空实现

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

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

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

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

  1. 析构函数没有返回值也不写void
  2. 函数名称与类名相同,需要在前面加上~
  3. 析构函数不能有参数,因此不能发生重载
  4. 程序在对象销毁前会自动调用析构,无需手动调用且只会调用一次

因为对象存储的位置不同,所以销毁的时机也会不同:

如下代码,当我们调用的对象存储在栈区(局部),在函数调用后对象销毁。

#include<iostream>
using namespace std;

class test {
public:
	test() {
		cout << "test构造函数的调用" << endl;
	}
	~test() {
		cout << "test析构函数的调用" << endl;
	}
};

void test01() {
	test ts;
}

int main() {
	
	test01();

	system("pause");
	return 0;
}

当对象存储在堆区时(未使用delete),会在程序结束后销毁:

int main() {
	
	test test02;

	system("pause");
	return 0;
}

3.构造函数的分类与调用

按分类方式:

  • 按参数分类:有参构造、无参构造
  • 按类型分类:普通构造、拷贝构造

(不属于拷贝构造函数的皆为普通构造,无参构造函数同时也被称为默认构造函数)

按调用方法:

  • 括号法
  • 显示法
  • 隐式转换法

以下是创造一个人的对象时,各个类型的构造函数的创建:

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;
	}

	int age;
};

其中拷贝构造函数是传入一个对象,将其属性等复制到另外一个创建的对象中。

在创建的函数中写出构造函数的测试案例,写出三种调用方式以及各方式的注意事项:

void test01() {
	//1.括号法
	person p;//默认构造函数调用
	person p1(1);//有参构造函数调用
	person p2(p1);//拷贝构造函数
}

注意:调用默认构造函数时,不要加(),例如person p();编译器会默认这个指令是一个函数的声明

void test01(){
	//2.显示法
	person p;//默认构造
	person p1 = person(10);//有参构造
	person p2 = person(p1);//拷贝函数
	person(10);//被称为匿名对象(特点:当前行执行结束后系统自动回收匿名对象)
}

注意:不要用一个拷贝函数初始化匿名对象,例如person(p2),其等价于person p2,编译器会默认该指令为一个对象的声明

void test01(){
	//3.隐式转换法
	person p4 = 10;//有参构造 相当于写了person p4=person(10);
	person p5 = p4;//拷贝构造 
}

4.拷贝构造函数使用时机

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值的方式返回局部对象

如标题3创造一个person的人“类”,属性创建为age,并标明各个类型构造函数:

class person {
public:
	//默认构造
	person() {
		cout << "person默认构造函数调用" << endl;
	}
	//有参构造
	person(int p_age) {
		age = p_age;
		cout << "person有参构造函数调用" << endl;
	}
	//拷贝构造
	person(const person& p) {
		age = p.age;
		cout << "person拷贝构造函数调用" << endl;
	}
	//析构函数
	~person() {
		cout << "person析构函数调用" << endl;
	}
	//属性
	int age;
};

1.创建一个p2对象,并将p1的属性复制给新对象p2

//1.使用一个已经创建完毕的对象来初始化一个新的对象
void test01() {
	person p1(20);
	person p2(p1);
}

2.实参通过拷贝构造函数的形式创造了一个复制体传递给形参

//2.值传递的方式给函数参数传值
void doWork02(person p) {

}

void test02() {
	person p;
	doWork02(p);
}

3.doWork03函数中创建的person的p3为局部对象,不会直接返回p3,而是拷贝一个新的复制对象以此返回

person doWork03() {
	person p3;
	return p3;
}
void test03() {
	person p = doWork03();
}

5.构造函数调用规则

默认情况下,当我们没有在某个类写任何构造函数时,编译器会自动给这个类默认添加3个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认构造函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝

值拷贝相当于为新建对象复制传递的对象的所有属性。

构造函数的调用规则:

  • 当用户自行定义有参构造函数,c++不再提供默认构造函数,但会提供默认拷贝构造
  • 当用户自行定义拷贝构造函数,c++不再提供其他的构造函数

6.深拷贝与浅拷贝

  • 浅拷贝:简单的赋值拷贝操作
  • 深拷贝:在堆区重新申请空间进行拷贝操作

示例:

创建一个person类,相较于上面的person类加上了一个int指针指向“身高”属性

class person {
public:
	person() {
		cout << "person默认构造函数调用" << endl;
	}
	person(int age,int height) {
		p_age = age;
		p_height = new int(height);
		cout << "person有参构造函数调用" << endl;
	}
	~person() {
		if (p_height != NULL) {
			delete p_height;
			p_height = NULL;
			//防止野指针的出现使其指向为空
		}
		cout << "析构函数调用" << endl;
	}
	//属性
	int p_age;//年龄
	int* p_height;//身高
};

测试案例:

void test01(){
	person p1(18, 160);
	cout << "p1的年龄为:" << p1.p_age << "身高:" << *p1.p_height << endl;
	person p2(p1);
	cout << "p2的年龄为:" << p2.p_age << "身高:" << *p2.p_height << endl;
}

但运行测试案例后程序进行报错。

原因是:在测试案例中创造的新的对象p2时,因为本身没有创造拷贝函数,那就会调用编译器提供的拷贝构造函数,而编译器自带的拷贝构造函数属于浅拷贝,只进行简单的赋值拷贝操作,因此对象p2的height指针指向与p1一致指向同一块内存空间,析构函数在释放空间时对该内存空间进行了两次的释放,因而造成程序的报错。因此正确的做法是写出深拷贝,在堆区为新对象申请新的内存空间储存对应的数据。

	person(const person& p) {
		cout << "拷贝构造函数的调用" << endl;
		p_height = new int(*p.p_height);
	}

7.初始化列表

C++提供了初始化列表的方法来初始化属性

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

如下使用该语法可以在创建对象为属性a,b,c赋值10,20,30

class person {
public:
	person():a(10),b(20),c(30){}
	int a;
	int b;
	int c;
};

创建多个对象为了方便修改可以按以下方式初始化列表,后续使用含参构造的方式创建对象即可

class person {
public:
	person(int A, int B, int C) :a(A), b(B), c(C) {}
	int a;
	int b;
	int c;
};

8.类对象作为类成员

C++中类中的成员可以是另外一个类中的对象,该对象被称为对象成员

class A {};
class B {
	A a;
};

创建B对象时,A与B的构造与析构顺序是谁先谁后?

新建一个person类,内涵phone对象成员

//手机类
class phone {
public:
	phone(string name) {
		phoneName = name;
		cout << "phone构造" << endl;
	}
	~phone() {
		cout << "phone析构" << endl;
	}
	//属性
	string phoneName;
};
//人类
class person {
public:
	person(string person_name, string phone_name) :p_name(person_name), p_phone(phone_name) {
		cout << "person构造函数" << endl;
	}
	~person() {
		cout << "person析构函数" << endl;
	}

	void showing() {
		cout << p_name << ":" << p_phone.phoneName << endl;
	}
	//属性
	string p_name;
	phone p_phone;
};

调用测试案例:

void test() {
	person p("小明", "OPPO");
	p.showing();
}

构造与析构顺序如下:

9.静态成员

静态成员即在成员变量和成员函数前加上关键字static,称为静态成员。静态成员有两种分类,一类是静态成员变量,一类为静态成员函数

静态成员变量

  • 所有对象共享一份数据
  • 在编译阶段分配内存(全局区)
  • 类内声明,类外初始化,二者缺一不可
class person {
public:
	static int A;
};
int person::A = 100;

类内声明,类外初始化。person::的作用是表明A是处于person类作用域下,A和其他类的属性一样,也是有访问权限的,私有权限下不可访问。

void test01() {
	person p;
	cout << p.A << endl;
	person p1;
	p1.A = 200;
	cout << p1.A << endl;

	cout << p.A << endl;

}

该测试案例可以探明“所有对象共享一份数据”。

静态成员变量不属于某个对象上,所有的对象共享一份数据,因此访问静态成员变量有两种访问方式:一种是通过任一对象进行访问,另一种为通过类名进行访问。

void test02() {
	//通过对象进行访问
	person p;
	cout << p.A << endl;
	//通过类名进行访问
	cout << person::A << endl;
}

静态成员函数

  • 所有的对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

因为所有对象共享同一个函数,因此和静态成员变量一样,静态成员函数也有相同的两种访问方式

class person {
public:
	static void fun() {
		cout << "静态成员函数fun()调用" << endl;
	}
};
void test01() {
	//通过对象访问
	person p;
	p.fun();
	//通过类名访问
	person::fun();
}

 不能访问修改类内变量B的原因是B的调用是需要对象进行访问的,此时在静态成员函数中其无法区分是哪一个对象下的变量。

class person {
public:
	static void fun() {
		A = 0;
		//B = 0;报错
		cout << "静态成员函数fun()调用" << endl;
	}

	int B = 100;
	static int A;
};
int person::A = 100;

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值