一.类的定义
1.类定义的格式
• class为定义类的关键字,ST为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省 略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或 者成员函数。
• C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是 struct中可以定义函数,⼀般情况下我们还是推荐用class定义类。
• 定义在类面的成员函数默认为inline。
#include<iostream>
#include<assert.h>
using namespace std;
class ST
{
public:
// 成员函数
void Init(int n = 4)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
void Push(int x)
{
array[top++] = x;
}
int Top()
{
assert(top > 0);
return array[top - 1];
}
void Destroy()
{
free(array);
array = nullptr;
top = capacity = 0;
}
private:
// 成员变量
int* array;
int capacity;
int top;
};
int main()
{
ST a;
a.Init();
a.Push(1);
a.Push(2);
int c = a.Top();
cout << c << endl;
a.Push(1);
a.Push(2);
a.Destroy();
}
我在这个类里面可以定义函数来进行实现,也可以定义变量。
2.struct的升级
typedef struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
这是我们写结构体时常写的格式,但是在C++中我们不需要这么麻烦。
这里我们可以直接使用结构体的名字,不需要用typedef来进行重新的命名。并且用原来的方法也是不会错误的
在C++的程序中struct也是升级成了类,我们可以在里面写函数。
struct ListNodeC
{
void Init(int x)
{
next = nullptr;
val = x;
}
struct ListNodeC* next;
int val;
};
3.访问限定符
• C++⼀种实现封装的方式,用类将对象的属性与方法结合在⼀块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
• public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访 问,protected和private是⼀样的,以后在继承才能体现出他们的区别。
• 访问权限作用域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 }即类结束。
• class定义成员没有被访问限定符修饰时默认为private,struct默认为public。
• ⼀般成员变量都会被限制为private/protected,需要给别⼈使用的成员函数会放为public。
4.类域
• 类定义了⼀个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
• 类域影响的是编译的查找规则,下面程序中Init如果不指定类域ST,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪里,就会报错。指定类域ST,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。
#include<iostream>
using namespace std;
class ST
{
public:
// 成员函数
void Init(int n = 4);
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
void ST::Init(int n)//指定类域
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
int main()
{
ST a;
a.Init();
}
二.实例化
1.实例化的概念
• 用类类型在物理内存中创建对象的过程,称为类实例化出对象。
• 类是对象进行⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。
• ⼀个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。打个比方:类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,设计图规划了有多 少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房 子才能住人。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。
2.对象的大小
类的内存大小与结构体储存的规则一样,遵循内存对齐原则。
• 第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
• 注意:对齐数 = 编译器默认的⼀个对齐数与该成员大小的较小值。
• VS中默认的对齐数为8
• 结构体总大小为:最⼤对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
(如果以上看不懂可以看C++——详细讲解结构体_c++ 结构体套娃吗-CSDN博客,里面有图文并配的详细讲解)
注意:1.这里只计算成员变量,不计算成员函数,因为成员函数在结构体中不占空间。
2.当这个类中只有成员函数时,所占空间为1个字节。为什么是1呢?因为如果⼀个字节都不给,怎么表示对象存在过呢!所以这里给1字节,纯粹是为了占位标识对象存在。
三.this指针
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
• 这里我调用函数的时候,他们都是用的同一块图纸,那么他是怎么区分d1与d2的呢?并且打印的时候我也没有传任何东西。其实这是隐含的this指针来解决的这个问题。
• 编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this 指针。比如Print的真实样子是Print(Date* const this) 。
• 类的成员函数中访问成员变量,本质都是通过this指针访问的Print中真实的代码是
cout << this._year << "/" <<this. _month << "/" << this._day << endl;
四.C++与C语言实现ST的对比
• C++中数据和函数都放到了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++封装的⼀种体现,这个是最重要的变化。这里的封装的本质是⼀种更严格规范的管 理,避免出现乱访问修改的问题。当然封装不仅仅是这样的,我后面还会继续讲解。
• C++中有⼀些相对方便的语法,比如Init给的缺省参数会方便很多,成员函数每次不需要传对象地 址,因为this指针隐含的传递了,方便了很多,使用类型不再需要typedef用类名就很方便
• 在我们这个C++入门阶段实现的ST看起来变了很多,但是实质上变化不大。
//C++实现栈
#include<iostream>
#include<assert.h>
using namespace std;
typedef int STDataType;
class Stack
{
public:
// 成员函数
void Init(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}
_capacity = n;
_top = 0;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
--_top;
}
bool Empty()
{
return _top == 0;
}
int Top()
{
assert(_top > 0);
return _a[_top - 1];
}
void Destroy()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
// 成员变量
STDataType* _a;
size_t _capacity;
size_t _top;
};
(可以对比我的数据结构----栈和队列-CSDN博客)