📌 博主名称:池茗

欢迎来到池茗的博客☆*: .. o(≧∇≦)o..:*☆
⭐ 数据结构系列个人专栏:
⭐ C++基础个人专栏:
⭐ 不会因为虚度年华而悔恨,也不会因为碌碌无为而羞愧
📖 点击展开/收起 文章目录
文章目录
- [📌 博主名称:池茗](#📌 博主名称:池茗)
下面是C++文档地址,方便大家查阅C++有关使用及语法知识,里面也有论坛,大家自行取用
欢迎各位来到池茗的创意工坊:
今天我们要讲的是类与对象
今天内容对于初学C++的小伙伴较为抽象,新概念比较多,不过我会尽我所能讲清楚,这部分类容对于C++学习比较重哟,希望大家能够从中能够有所收获哟, 喵~~
本节知识较为简单,如有知识讲解不对的地方,敬请指正哟,喵~~
类的定义
首先定义一个类有两种方式可以用两个关键字,class和struct,这里面struct大家很熟习,在学习C语言时他作为结构体,被大家使用在这里又要变成类,作为他的第二种使用方法
C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
cpp
struct Date
{
public:
void DatePrint(int _year, int _month, int _day)
{
cout << _year <<"/" << _month <<"/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
cpp
class Date
{
public:
void DatePrint(int _year, int _month, int _day)
{
cout << _year <<"/" << _month <<"/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
看上面代码,这是量的简单的类,Date就是类的名称,private下面叫做成员,public下面函数叫做成员函数,这样就简单定义出了一个类
public是什么意思? private又是什么意思?
public,private还有后面要学的protected叫做访问限定符
在回答这两个问题之前,先讲讲class和struct的区别>:class默认是private属性,struct默认是public属性
在C++中我们有个很重要的思想叫做封装,这里的public和private就是为了这种封装做准备,在一个类中public,private是可以定义多
个的public到第一个遇到的private之间都是可以被外界访问的 不管是成员还是成员函数,我们把它叫做公开成员,或者叫做公开成员
函数,private到第一个遇到的public之间时私有的,也就是不可被外界访问
为什么要这样做?
因为我们定义出来一个类,里面实现类某些功能,我们只要别人调用这些功能,不要他们碰我们的私有成员,与外界耦合度不高,外界调
用不会很大影响我定义的这个模块,他只有功能的使用权没有功能的修改权 ,你想在一个大工程里面,你要是外界可以随便调用
我内部私有成员,随便修改我的功能,我代码不就可能会崩吗?
类域
cpp
class Stack
{
public:
// 成员函数
void Init(int n = 4);
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
} c
apacity = n;
top = 0;
}
在这里类似于上一期讲解的命名域,你可以在这里用域作用访问符来访问类内部声明的函数void Stack::Init(int n)
什么时候在类外面定义成员函数,什么时候在类里面定义成员函数?
这就不得不提及上期提到的inline (内联函数),在类里面,小而短的成员函数会默认变成内联函数以此来减少调用函数栈帧,减少消耗 ,
inline函数就算你加上了,编译器他也会有自己的判断,如果你函数过于大,一就不会在main函数中直接展开
inline 关键字只是对编译器的一个"建议",编译器有权忽略它
在类里面也是一致的规则,所以总结一下,调用频次高小的函数放类里面,大而调用频次低的函数声明与函数定义分离
实例化
类似于结构体,定义一个对象,访问方式也与结构体相似
下面展示实例化一个对象,以及调用成员函数
Date2 d1;//定义一个对象
d1.DatePrint();
对象大小计算
那对象大小怎么算?要不要加成员函数
不加成员函数,成员函数默认是inline是直接展开的
"大小"这个属性是针对于变量(对象)的,函数不是变量,所以函数本身没有"大小"。
那怎么算一个对象大小;和算结构体大小差不多,要用到内存对齐
对齐规则:
- 第一个成员从0开始对齐,
- 每个成员起始位置要是自身对齐数的整数倍
- VS默认对齐数为8
- 成员的对齐数=min(默认对齐数,自己类型大小)
- 对象的大小是最大对齐数的整数倍,最大对齐数=min(最大成员,默认对对齐数)


this指针
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,当你多个对象调用这个成员函数,编译器咋知道你是那个在调用
编译器会在第一个形参位置偷偷放一个指针,这个指针就是this
void initdate(int year, int month, int day)我们看到的
void initdate(const date2*this ,int year, int month, int day)实际上的
该形参位置无法更改,有了这个指针,每次对象调用成员函数,编译器都会偷偷传入指向该对象的指针,这样就能做到区分是谁在调用此函数
date2 d2;
d2.dateprint();//等价于d2.dateprint(&d2);
cpp
class Date
{
public:
//void initdate(const date2*this ,int year, int month, int day)
void initdate(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
//void dateprint(const date2*this)
void dateprint()
{
//cout << _year << "/" << _month << "/" << _day << endl;
cout << this->_year << "/" <<this-> _month << "/" <<this-> _day << endl;
}
private:
//注意这里并不是真正的赋值而是告诉电脑,只要我定义一个对象,
//它里面的这些私有成员默认给我给的值,更像是一种声明叫做缺省值
//为了区分成员与函数的形参,一般在前面加个"_"
int _year=0;
int _month=0;
int _day=0;
};
int main()
{
Date d1;
Date d2;
d1.initdate(0,0,0);
d2.initdate(0,0,0);
}
c++规定不能在形参和实参位置显式写this指针但可以在函数体内部调用
cpp
class Date {
private:
int _year;
public:
// 错误写法(编译器报错):
// void setYear(Date* this, int year) { ... }
// 正确写法:
void setYear(int year) {
// 在函数体内部,可以显式使用 this 来访问成员
this->_year = year;
// 也可以不写 this,编译器默认帮你加了
// _year = year;
}
};
int main() {
Date d;
// 错误写法(编译器报错):
// d.setYear(&d, 2026);
// 正确调用(编译器会自动帮你传地址):
d.setYear(2026);
}
this在类里面是可以显式调用的
调用成员时编译器本身会自动调用this,你可以直接写_year,你也可以显示写出来 this->_year ,
cout << _year << "/" << _month << "/" << _day << endl;
cout << this->_year << "/" << this-> _month << "/" << this-> _day << endl;
假如你型返回this指针也可以直接
return this;
this指针的存储位置
this存储在栈区少数在寄存器中间
经典问题(关于this指针空引用问题)
cpp
class A1
{
public:
void Print()
{
cout<<"A::Pirint"<< endl;
}
private:
int _a;
};
下面不会运行崩溃 原因:虽然这里p为空指针,没有发生解引用
A1* p;
p=nullptr;
p->Print();//虽然有->符号但是在汇编上他只是传给了成员函数相当于this
cpp
class A2
{
public:
void Print()
{
cout<<"A::Pirint"<< endl;
cout<<_a<< endl;
}
private:
int _a;
};
会运行崩溃
A2* p2;
p2=nullptr;
p2->Print();//在成员函数中间有_a,相当于this->_a解引用了
总结:
成员函数调用类似于全局函数调用,函数地址在编译时即确定,与对象指针是否为空无关。
A1没有访问成员变量 A2访问了成员变量
- 不崩溃的原因:若函数体未涉及对成员变量的访问,就不会发生 this 指针的解引用。
- 崩溃的原因:一旦函数体内部访问了成员变量,编译器会自动通过 this 指针进行寻址。
如果 this 为 nullptr,便会触发经典的"空指针解引用"错误。
C语言版本实现栈
c
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);
// 满了, 扩容
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
} p
s->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
C++版本实现栈
cpp
#include<iostream>
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;
};

下期内容预告:我会讲解构造函数,析构函数,拷贝构造