C++学习 --类和对象之封装

本文详细介绍了C++中的封装、类的创建、权限控制(public、private、protected)、构造函数(包括无参、有参和拷贝构造)、析构函数、深浅拷贝、静态成员、this指针的使用、const修饰成员属性以及结构体与类的区别。
摘要由CSDN通过智能技术生成

目录

1, 什么是封装

2, 创建类

3, 权限控制

3-1, public权限

3-2, private权限

3-3, protected权限

3-4, 类外访问私有属性

4, 构造函数

4-1, 创建构造函数

4-2, 调用构造函数

4-3, 初始化列表

5, 析构函数

6, 深拷贝与浅拷贝

6-1, 浅拷贝

6-2, 深拷贝

7, 静态成员

7-1, 静态成员变量

7-2, 静态成员变量访问

7-3, 静态成员变量修改

7-3-1, 类内修改

7-3-1, 类外修改

7-4, 静态成员函数

7-4-1, 静态成员函数访问

8, 成员函数和成员变量分开存储

9, this指针

9-1, this实现链式编程

10, const修饰成员属性

10-1, 常函数

10-2, 常对象

11, 类作为类的成员

12, 拆分类到不同文件中

12-1, 创建类的头文件

12-2, 创建类的源文件

12-3, 在主函数中调用

13, 构造函数调用规则

14, 结构体与类的区别


1, 什么是封装

将对象的数据属性和行为属性封装到一个中,并加以权限控制, 就叫封装

2, 创建类

通过class 类名{权限:属性}, 创建一个类

class Person
{
public:
	//定义数据属性
	int m_a;
	//定义行为属性
	void func(){}
};

3, 权限控制

权限分为public, private, private三种, 若没有指定权限关键字,则默认为private权限

3-1, public权限

public表示公共权限类内外均可访问子类可访问父类的public的属性

public:
	int m_age;

3-2, private权限

private表示私有权限, 仅类内可访问, 子类不可访问父类的private的属性

private:
	int m_age;

3-3, protected权限

protected表示保护权限, 类内可访问, 子类可访问父类的protected属性

protected:
	int m_age;

3-4, 类外访问私有属性

若要访问类的私有权限, 可通过在类中设置public的方法, 在类外调用public方法进行访问

4, 构造函数

通过构造函数给类的实例初始化, 构造函数与类同名无数据类型, 分为无参构造函数、有参构造函数、拷贝构造函数

4-1, 创建构造函数

class Person
{
public:
	//无参构造函数
	Person()
	{
		cout << "无参构造函数调用" << endl;
	}
	//有参构造函数
	Person(string name, string sex, int age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
		cout << "有参构造函数调用" << endl;
	}
	//拷贝构造函数, 注意参数为常量引用
    //若用户没有自定义拷贝构造函数, 编译器会自动创建一个拷贝构造函数
	Person(const Person& p)
	{
		cout << "拷贝构造函数调用" << endl;
	}

	string m_name;
	string m_sex;
	int m_age;
};

4-2, 调用构造函数

//无参构造函数调用
Person p1;
//括号法调用有参构造函数
Person p2("aaa", "m", 30);
//显示法调用有参构造函数
Person p3 = Person("bbb", "f", 31);
//括号法调用拷贝构造函数调用
Person p4(p3);
//显示法调用拷贝构造函数调用
Person p4 = Person(p3);

//编译器会当着Person p3进行处理, 重复定义p3
//Person(p3);

说明:类对象当着函数参数传递时,会调用拷贝构造函数

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	Person(int a)
	{
		cout << "调用Person类有参构造函数" << endl;
	}
	Person(const Person& p)
	{
		cout << "调用Person类拷贝构造函数" << endl;
	}
};

void func(Person p)
{
	cout << "func调用" << endl;
}

int main()
{
	Person p1(10);
	//函数参数为类对象, 调用下面的函数会自动调用拷贝构造函数
	func(p1);

	system("pause");

	return 0;
}
---------------------------------------------------------------
输出结果:
调用Person类有参构造函数
调用Person类拷贝构造函数
func调用

4-3, 初始化列表

通过构造函数(数据类型 变量1, ..., 数据类型 变量n):属性1(变量1)...属性n(变量n){}的方式, 可初始化对象的属性

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
    //初始化列表
	Person(string name, string sex, int age):m_name(name),m_sex(sex),m_age(age)
	{		
		cout << "m_name:" << m_name << endl;
		cout << "m_sex:"  << m_sex  << endl;
		cout << "m_age:"  << m_age  << endl;
	}

	string m_name;
	string m_sex;
	int m_age;
};

5, 析构函数

通过~类名 () {代码块}, 可创建一个析构函数其作用是清理对象的数据, 

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:

	//析构函数名与是~类名, 在对象释放时,由编译器调用
	//析构函数没有参数
	~Person()
	{
		cout << "Person类构造函数调用" << endl;
	}
};

6, 深拷贝与浅拷贝

6-1, 浅拷贝

没有堆区重新申请空间的赋值拷贝, 为浅拷贝

6-2, 深拷贝

在堆区重新申请空间的赋值拷贝, 为深拷贝

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	Person(int age, int height)
	{
		m_age = age;
		//在堆区创建一块内存
		m_height = new int(height);

		cout << "Person类有参构造调用" << endl;
	}

	//拷贝构造函数的作用是用户可自定义在堆区创建一块内存给p3使用
	//防止p1释放内存后,p2再次释放就放报错的问题
	//若使用编译器提供的拷贝构造函数, 就会出现上面说的问题
	Person(const Person& p)
	{
		m_age = p.m_age;
		m_height = new int(*p.m_height);
	}

	~Person()
	{
		cout << "析构函数调用" << endl;
        //在堆区释放内存时,要注意是否会存在多次释放的情况
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}
	}

	int m_age;
	int* m_height;
};

int main()
{
	Person p1(10, 160);

	Person p2(p1);
	cout << "p2的年龄:" << p2.m_age << endl;

	system("pause");

	return 0;
}
-----------------------------------------------------------
输出结果:
Person类有参构造调用
p2的年龄:10
请按任意键继续. . .
析构函数调用
析构函数调用

说明:在堆区释放内存时,要注意是否会存在多次释放的情况

7, 静态成员

静态成员分为静态成员变量静态成员函数

7-1, 静态成员变量

在类中, 通过static 数据类型 变量, 可定义一个静态成员变量, 所有的对象共享, 静态成员只能在类外定义

class Person
{
public:
	//类内声明
	static int m_a;
};

//类外定义, Person::m_a:表示m_a是属于Person中
int Person::m_a = 100;

7-2, 静态成员变量访问

分为类内访问和类外访问两种, 在类中,通过静态变量名进行访问, 在类外,通过实例.静态成员变量或者类名::静态成员变量 

7-3, 静态成员变量修改

7-3-1, 类内修改

通过类内的方法进行修改

class Person
{
public:
	static int m_a;
	void func()
	{
        //修改静态变量的值
		m_a = 1000;
		cout << "m_a:" << m_a << endl;
	}
};

7-3-1, 类外修改

通过实例.静态成员变量 = 值或者类名::静态成员变量 = 值进行修改

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	static int m_a;
};

//m_a是静态成员变量, 需要在类外定义
int Person::m_a = 100;

void test()
{
	Person p1;
	Person p2;

	cout << "p1.m_a:" << p1.m_a << endl;
	cout << "p2.m_a:" << p2.m_a << endl;
    //因为所有对象共享一份数据,任意对象将属性修改,都会映射到其他对象
	p1.m_a = 200;

	cout << "p1.m_a:" << p1.m_a << endl;
	cout << "p2.m_a:" << p2.m_a << endl;
}

int main()
{	
	test();

	system("pause");

	return 0;
}
-----------------------------------------------------
输出结果:
p1.m_a:100
p2.m_a:100
p1.m_a:200
p2.m_a:200

7-4, 静态成员函数

所有对象共享同一个函数, 静态成员函数只能访问静态成员变量, 不能在静态成员函数中访问非静态成员变量

7-4-1, 静态成员函数访问

通过实例.成员函数()或者类::成员函数(), 可访问静态成员函数

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	static void func()
	{
		//m_a是静态成员变量, 静态成员函数可以访问
		m_a = 100;
		//由于m_b不是静态成员变量, 所以m_b = 200是非法的
		//m_b = 200;
		cout << "static void func调用" << endl;
	}

	static int m_a;
	int m_b;
};

int Person::m_a = 100;

void test()
{
	Person p1;
	cout << "通过对象访问静态成员函数:" << endl;
	p1.func();
	cout << "通过类名访问静态成员函数:" << endl;
	Person::func();
}

int main()
{	
	test();

	system("pause");

	return 0;
}

8, 成员函数和成员变量分开存储

只有非静态成员变量存储在一起成员函数、静态成员函数、静态成员变量都不存储在类上, 所以类占用内存空间的大小非静态成员变量决定

#include <iostream>
#include <string>

using namespace std;

class Person
{
	//只有非静态成员变量与类在一块内存空间
	int m_a;  
	//下面的三种都不在类的内存空间
	void func() {};
	static int m_b;
	static void func1() {};
};

void test()
{
	Person p;
	cout << "sizeof of p:" << sizeof(p) << endl;
}

int main()
{
	test();

	system("pause");

	return 0;
}
------------------------------------------------------------
sizeof of p:4

说明:若类是一个空对象,则分配1个字节空间

9, this指针

this指针是类中所有成员属性自带的指针, 不用显示给出, 当形成与成员属性同名时, 可通过this进行区分

m_age = age;
等价于
this->m_age = age;

9-1, this实现链式编程

通过*this, 返回对象的本身, 若要实现链式编程, 则需要以引用的方式返回, 因为引用的修改会映射到原对象, this指向的是调用成员函数的对象

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	Person(int age)
	{
		m_age = age;
	};

	//注意返回的是引用
	Person& func(int age)
	{
		m_age += age;

		//通过*this返回对象本身
		return *this;
	}

	int m_age;
};

void test()
{
	Person p(10);
	//链式编程
	p.func(10).func(10).func(10);
	cout << "age:" << p.m_age << endl;
}
int main()
{
	test();

	system("pause");

	return 0;
}
------------------------------------------------------------
输出结果:
age:40

10, const修饰成员属性

10-1, 常函数

在类中, 通过const修饰的成员函数, 叫着常函数, 常函数中不能修改成员属性, 但是修改mutable修饰的成员变量

class Person
{
public:
	//通过const修饰的函数为常函数
	void func_1() const
	{
		//因为func_1是常函数,不能修改成员属性,所以下面的操作非法
		//m_age = 100;
		//因为m_name使用了关键字mutable, 所以能够修改
		m_name = "张三";
	};

	int m_age;
	//mutable的作用,在常函数中能够修改的变量 
	mutable string m_name;
};

10-2, 常对象

通过在对象前加const, 则该对象就是常对象, 常对象只能调用常函数

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	//通过const修饰的函数为常函数
	void func_1() const
	{
		cout << "func_1调用" << endl;
	};

	void func_2()
	{};
};

void test()
{
	//通过const修饰的对象为常对象, 常对象只能调用常函数
	const Person p;
	//p是常对象, func_2不是常函数,所以下面的操作非法
	//p.func_2()
	//func_1是常函数,可以调用
	p.func_1();
}

int main()
{
	test();

	system("pause");

	return 0;
}
-----------------------------------------------------------------
输出结果:
func_1调用

11, 类作为类的成员

#include <iostream>
#include <string>

using namespace std;

class Phone
{
public:
	Phone(string pname, int tel) :m_pname(pname), m_ptel(tel) {};

	string m_pname;
	int m_ptel;
};

class Person
{
public:
	Person(string name, Phone phone):m_name(name), m_phone(phone.m_pname, phone.m_ptel){}
	
	string m_name;
	//类成员作为成员属性
	Phone m_phone;
};

int main()
{	
	Phone phone("苹果", 123456789);
	Person p1("张三", phone);

	cout << "姓名:" << p1.m_name << endl;
	cout << "机型:" << p1.m_phone.m_pname << endl;
	cout << "号码:" << p1.m_phone.m_ptel << endl;

	system("pause");

	return 0;
}

12, 拆分类到不同文件中

类的声明和定义可拆分到不同的文件中, 声明放在头文件中, 类定义放在源文件

12-1, 创建类的头文件

头文件中,实现类以及其属性声明

在项目的头文件夹中, 创建person.h

#pragma once
#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	void func1();
	void func2();
	string func3();
	string func4();

protected:
	void func5();
	
private:
	string m_name = "张三";
	string m_sex = "男";

	void func6();
};

12-2, 创建类的源文件

源文件中,实现方法的定义

项目的源文件夹中, 创建person.cpp源文件

#include <iostream>
#include <string>
#include "person.h"

using namespace std;

//Person::func1(), 表示申明func1在Person类中
void Person::func1()
{
	return func5();
}

void Person::func2()
{
	return func6();
}

string Person::func3()
{
	return m_name;
}

string Person::func4()
{
	return m_sex;
}

void Person::func5()
{
	cout << "类保护属性访问" << endl;
}

void Person::func6()
{
	cout << "类私有属性访问" << endl;
}

12-3, 在主函数中调用

#include <iostream>
#include <string>
#include "person.h"

using namespace std;

void test()
{
	Person p;
	p.func1();
	p.func2();
	cout << "获取类的私有属性:" << p.func3() << endl;
	cout << "获取类的保护属性:" << p.func4() << endl;
}

int main()
{	
	test();

	system("pause");

	return 0;
}

13, 构造函数调用规则

默认情况下, 创建一个类, 编译器会自动创建默认构造函数(空实现) 默认析构函数(空实现), 拷贝构造函数(具有属性拷贝功能)

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	Person(int age)
	{
		m_age = age;
		cout << "Person类有参构造调用" << endl;
	}
	int m_age;
};

int main()
{
	Person p1(10);

	//虽然Person类没有定义拷贝构造函数, 但是会默认提供一个
	//所以下面能正常打印
	Person p2(p1);
	cout << "p2的年龄:" << p2.m_age << endl;

	system("pause");

	return 0;
}
----------------------------------------------------------------
输出结果:
Person类有参构造调用
p2的年龄:10

说明:

1, 若在类中定义有参构造函数, 则编译器不会自动提供默认构造函数

2, 若在类中定义拷贝构造函数, 则编译器不会自动提供默认和有参构造函数

14, 结构体与类的区别

结构体属性权限默认为公共权限类的属性权限默认为私有权限

struct Student
{
	//默认为公共权限
	string m_name;
};
class Student
{
	//默认为私有权限private
	string m_name;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值