1. 类的定义
1.1 类定义格式
这是我们在C语言中定义的
在C++中就不一样了,在C++中struct就可以定义函数,但是我们遵循1.1第二条的原则,要加一个特殊标识符,就要设计成这个样子了。
• 定义在类里面的成员函数默认为inline。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
class Stack
{
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;
size_t capacity;
size_t top;
}; // 分号不能省略struct Person
{
public:
void Init(const char* name, int age, int tel)
{
//在这要初始化这个名字就不能直接给了,我们要借助C语言的库函数,strcpy
strcpy(_name, name);//加个特殊标识好区分
_age = age;
_tel = tel;
}
//我们也可以写一个打印函数
void Print()
{
cout << "姓名:" << _name << endl;
cout << "年龄:" << _age << endl;
cout << "电话号码:" << _tel << endl;
}
private:
char _name[10]; //在这为什么不用const char*,而要用一个数组呢?
int _age; //用一个数组的原因是:后面我们要修改一个信息,我们就可以修改,如果用一个const char*,
int _tel; //外面是一个常量字符串,想修改我们的名字,是修改不了的。其实用const char*也可以,不影响。
};
int main()
{
/*Stack st;
st.Init();
st.Push(1);
st.Push(2);
cout << st.Top() << endl;
st.Destroy();*/
Person p1;
p1.Init("张三", 18, 110);
p1.Print();
return 0;
}
//我们用class定义类的时候与struct定义类的时候,一模一样(在这个角度来说)
//C++对这块定义的时候进行了升级
反正不论struct还是class,一般来说,成员变量都是私有的,不期望在外面去访问,要访问都通过成员函数去访问。
C++还有一个特点是兼容C当中struct的所有用法,
1.2 访问限定符
1.3 类域
2. 实例化
2.1 实例化概念
class Data
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
//下面三个变量都是声明
int _year;
int _month;
int _day;
};
int year;//这里是定义
int main()
{
//我们用class定义出的是一个类型Data
//用类型定义出一个对象d1,这个对象包含_year,_month,_day
//是随着对象定义而定义的,这里的对象定义我们把它叫做类实例化出对象
//类和对象的关系是一对多的关系,也就是说,一个类可以实例化出多个对象
Data d1;
return 0;
}
2.2 对象大小
对象大小怎么算呢?
上面我们分析了对象中只存储成员变量,C++规定类实例化的对象也要符合内存对齐的规则。
首先也是要考虑内存对齐的问题,其实类的对象实例化里面只存了成员变量,没有存成员函数。成员函数可以认为存储在一个公共的区域。严格来说成员函数如果你调用,是在编译时就确定好地址了,只是函数呢编译好指令会存到一个公共的代码段。它不需要那个指针单独往对象里面存,因为都是一样的。编译的时候就已经确定好这个地址了。
内存对齐规则
首先类B和类C是一样的,都没有成员变量,那结果不应该是0吗?为什么会是1呢?
如果是0的话,说明B和C定义的时候不需要开空间,不需要开空间怎么表示B和C存在过呢?假设不开空间,那么类B和类C的地址是什么?0个字节不需要开辟空间,那它的地址是什么?
所以对于没有成员变量的类呢, 它的对象我们要给它开空间,开多大的空间呢?开一个字节,那开一个字节是干嘛的?是为了占位,不存储有效数据。只是为了标识对象存在。我们后面会讲到仿函数的类,这个类基本上就没有成员变量。没有成员变量的类还是很常用的。
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;
}
3. this指针
#include<iostream>
using namespace std;
class Date
{
public:
//这里的Init隐含一个形参 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;
}
//所有的成员函数都有一个隐含的参数,这个参数叫this
//void Print(Date* const this, int year, int month, int day)
void Print()
{
//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
Date d1;
Date d2;
//在这调用Init也不是这样调用的,是这样调用的
//d1.Init(&d1, 2024, 3, 31);
//d2.Init(&d2, 2024, 7, 5);
//所以Init和Print的时候就能够区分谁是谁了
//调用的确确实实是一个函数,但是用d1调用的时候,this是d1的地址,
//用d2调用的时候,this是d2的地址。
//d1.Print(&d1);
//d2.Print(&d2);
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
实参、形参、函数体类你不加,编译器都会显示的加,但是实参和形参的位置我们不能显示加,函数体类我们可以显示着加 。