一、类的定义
1、定义格式
class data
{
//成员函数
void init()
{
_year=1;
_month=1;
_day=1;
}
//成员变量
//为了区分成员变量,一般都会在成员变量前面加上"_"
int _year;
int _month;
int _day;
};
(代码1)
C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是struct中可以定义函数,⼀般情况下我们还是推荐⽤class定义类。
2、访问限定符
public、private和protected
访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有 访问限定符,作⽤域就到 }即类结束。class定义成员没有被访问限定符修饰时默认为private,struct默认为public。⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。
3、类域
#include<iostream>
using namespace std;
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;
}
capacity = n;
top = 0;
}
int main()
{
Stack st;
st.Init();
return 0;
}
(代码2)
二、实例化
#include<iostream>
using namespace std;
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;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
(代码3)
对象大小
类实例化的每个对象都有独立的空间,所以对象中肯定包含成员变量。对于成员函数,函数被编译后一串指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),如果对象中非要存储的话,只能存成员函数的指针,但是存放指针没有必要,比如Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量_year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了,其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指令[call 地址],其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找,只有动态多态是在运⾏时找,就需要存储函数地址。
注:c++规定实例化的对象也要符合内存对齐的规则
#include<iostream> using namespace std; class A { public: void Print() { cout << _ch << endl; } private: char _ch; int _i; }; class B { public: void Print() { //... } }; class C {}; int main() { A a; B b; C c; cout << sizeof(a) << endl; cout << sizeof(b) << endl; cout << sizeof(c) << endl; return 0; }
(代码4)上⾯的程序运⾏后,我们看到没有成员变量的B和C类对象的⼤⼩是1,为什么没有成员变量还要给1个字节呢?因为如果⼀个字节都不给,怎么表⽰对象存在过呢!所以这⾥给1字节,纯粹是为了占位标识对象存在。
三、this指针
思考一下,代码3中Data类中有Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到C++给了⼀个隐含的this指针解决这⾥的问题。
- 类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。⽐如Date类的Init的真实原型为,void Init(Date* const this, int year, int month, int day)
- 类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值, this- >_year = year;
- C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。
#include<iostream>
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
// 编译报错:error C2106: “=”: 左操作数必须为左值
// this = nullptr;
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
// d1.Init(&d1, 2024, 3, 31);
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
(代码5)
两个题目
对第一题: 成员函数的指针是在编译时确定的,没有存在对象中,这一虽然写了p->,但是没有解引用,A*const this指针接收了一个空指针,并不会影响程序的正常运行。
对第二题:当空指针传给this指针后,this空指针,空指针解引用肯定会报错。