类的引入
类和对象关系
【一个类 -> 实例化->N个对象】
【C++兼容C语言中struct的用法】
【C++升级struct升级成了类】
1.类里面可以定义函数
2.struct名称就可以代表类型 ,例如下面的stack
struct stack
{
//......函数
//......成员
}
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:
用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数。
#include<iostream>
using namespace std;
typedef int DataType;
struct Stack
{
void Init(size_t capacity)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(const DataType& data)
{
// 扩容
_array[_size] = data;
++_size;
}
DataType Top()
{
return _array[_size - 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
//类的成员
DataType* _array;
size_t _capacity;
size_t _size;
};
int main()
{
Stack s;
s.Init(10);
s.Push(1);
s.Push(2);
s.Push(3);
cout << s.Top() << endl;
s.Destroy();
return 0;
}
事实上,C++更喜欢下面的写法
class stack
{
void Init(size_t capacity)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(const DataType& data)
{
// 扩容
_array[_size] = data;
++_size;
}
DataType Top()
{
return _array[_size - 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
//类的成员
DataType* _array;
size_t _capacity;
size_t _size;
};
int main()
{
Stack s;
// s.Init(10);
// s.Push(1);
// s.Push(2);
// s.Push(3);
//cout << s.Top() << endl;
//s.Destroy();
return 0;
}
为什么这里屏蔽了类里面的函数呢?这里会报错
这里不得不用谈到C++里面struct和class的区别了
而这里又需要引入访问限定符了
访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选
择性的将其接口提供给外部的用户使用
【访问限定符说明】
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到} 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
为什么下面的类成员变量名前面加了一个'_'?
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
d.Init(2024, 6, 27);
return 0;
}
实际上这是为了突出成员变量,更易区分外面的参数,写法可以多样
类的声明和定义分离
类本身也形成了一个类域
对类里面的函数和成员进行保护
如果有多个类都含有初始化函数,都放在.cpp文件里面,势必会造成命名冲突
因此我们用类名加上“::"放在函数名前限定
stack.cpp文件 放定义
void stack::Init()
{
//.....
}
void stack::Destroy()
{
//.....
}
stack.h文件 放声明
class stack
{
public:
void Init()
{
//.....
}
void Destroy()
{
//.....
}
//这里成员变量是声明没有给空间,在实例化(stack st1;)的时候才叫定义
private:
int*_a;
int _capacity;
int _top;
};
如何计算类对象的大小
#include<iostream>
using namespace std;
struct Stack
{
public:
void Init();
private:
int* _a;
int _top;
int _capacity;
};
int main()
{
stack st1;
cout<<sizeof(st1)<<endl;
cout<<sizeof(stack)<<endl;
return 0;
}
这里结果都是12/12,为什么呢,因为没有计算成员函数的大小
为什么不计算成员函数呢,因为一个类可以实例化多个成员,如果每个成员里面都放入一模一样的成员函数,便会极大浪费空间,于是便将成员函数放入到了一个公共空间供同一类的对象共同使用(类似于一个小区里面有多个房屋,而只有一个大的游泳池作为公用,如果每个房屋里面造一个巨大的游泳池,空间不是浪费了吗)
因此计算对象的大小时,只考虑成员变量(注意内存对齐)
下面就是内存对齐的示例(x64)
class A
{
char _a;//一个字节
int _b;//四个字节
int* _c;//四个字节
//A类大小是12
}
为什么要内存对齐?由于硬件设计,cpu从缓存中读取数据为整数倍字节,不同类型的数据不进行内存对齐,会造成一个数据需要二次读取,降低了运行效率
用空间换时间,提高缓存命中率
我们可以修改默认对齐数为:1
#pragma pack(1)
结果计算类A大小结果变成了:9
空类的大小是多少?
#include<iostream>
using namespace std;
class A{};
int main()
{
cout<<sizeof(A)<<endl;
return 0;
}
答案是:1个字节
为什么呢?
#include<iostream>
using namespace std;
class A{};
int main()
{
//这里a1开了多少大小?是0吗?我们给a1开了个空气?a1地址是啥?
A a1;
A a2;
return 0;
}
这1个字节,不存储有效数据,表示对象被定义出来
#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;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
为什么上述d1.Print()和d2.Print()打印的结果不一样呢?明明调用的同一个函数
这里得引入this指针
隐含的this指针
1.实参和形参的位置不能显示写,编译器自己加,
2.但是可以在类里面显示地用
下面注释部分为编译器的处理
#include<iostream>
using namespace std;
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//this本身的内容不可以修改,但是指向的对象可以修改,这里是有const的原因
//void Print(Date* const this)
void Print()
{
//通过this指针访问成员里面的成员变量,这儿可以显示的用
//cout <<this->_year<< "-" <<this->_month << "-"<< this->_day <<endl;
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
//d1.Print(&d1);取出对象d1的地址,传给this指针
d1.Print();
//d2.Print(&d2);取出对象d1的地址,传给this指针
d2.Print();
return 0;
}
答案是:C正常运行
A:空指针的访问会报编译错误吗 这里传空指针并没有对空指针进行访问
类的函数不在类里面,而在公共区域里面,这里调用类的成员函数并不会报错
答案是:B 运行崩溃 这儿对A类型的空指针进行了成员变量访问(空指针传给了this指针),即空指针访问
3.this指针的存储位置
(栈 (形参,局部变量)?堆(malloc)? 静态区(静态数据,全局数据) ?常量区(常量)? 对象里面?)
答案是:栈 (作为形参放在栈里面)(有的是ecx寄存器)(根据编译器有不同)
为什么不是对象里面?sizeof(对象) 将this指针计算了吗?