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