类具有三大特性:封装、继承、多态
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;
第一弹 就此先结束啦!!!!