1. 类的定义
1.1 格式
- class为定义类的关键字,Stack为类的名字,{ }中为类的主体,类定义结束时后分号不能省略。
- 类体中内容称为类的成员:类中的变量称为类的成员变量,类中的函数称为类的成员函数
- 为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_ 或者m开头(并不是强制的)
- 定义在类⾯的成员函数默认为inline。
1.2 访问限定符
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问
- 访问权限作用域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 } 即类结束
- class定义成员没有被访问限定符修饰时默认private,struct默认为public
- ⼀般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public。
class stack
{
//成员函数
public:
void Init(int n=4)//专属于stack的函数,不需要加抬头
{
_arr = (int*)malloc(sizeof(int) * n);
if (_arr == nullptr)
{
perror("malloc");
exit(1);
}
_size = 0;
_capacity = n;
}
void Push(int x)
{
//扩容
_arr[_size++] = x;
}
//成员变量
private:
int* _arr;
size_t _size;
size_t _capacity;
};
int main()
{
stack st1;//stack是类名,类名就是类型,st1是对象
st1.Init();
st1.Push(1);
st1.Push(2);
st1.Push(3);
st1.Push(4);
return 0;
}
回顾:结构体访问
1)通过结构体变量直接访问成员
点操作符(.)
struct stu
{
char name[10];
};
struct stu a = { "zhangsan" };
printf("%s", a.name);
2)通过结构体指针访问成员
操作符:-> 和 *
struct stu
{
int id;
char name[50];
} ;
struct stu a;
struct stu* ptr = &a;
//访问
ptr->id = 1;
strcpy(ptr->name, "zhangsan");
//或者这样访问
(*ptr).id = 1;
strcpy((*ptr).name, "zhangsan");
1.3 C++中的struct
- C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是 struct中可以定义函数,一般情况下还用class定义类。
- 通过类名==类型这个特性,可以区分struct是C还是C++
//struct升级成了类
struct ListNode
{
void init()//可以定义函数
{
}
//不需要typedef,struct名称就可以代表类型
ListNode* next;
int data;
};
1.4 类域
类定义了⼀个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域
class stack
{
public:
///缺省参数要在声明中给
void Init(int n = 4);
private:
int* _arr;
int _size;
int _capacity;
};
//声明和定义分离,需要指定类域
void stack::Init(int n)
{
_arr = (int*)malloc(sizeof(int) * n);
if (_arr == nullptr)
{
perror("malloc");
exit(1);
}
_capacity = n;
_size = 0;
}
2. 实例化
2.1 实例化概念
- 类是对象进行⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间
- ⼀个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
举例:类就像是设计图,但是并没有实体的建筑存在,类实例化出对象就像现实中使用建筑设计图建造出房子才能住人。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << ' ';
cout << _month << ' ';
cout << _day << ' ';
}
private:
//这里只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
//Date类实例化出对象today
Date today;
today.Init(2024, 9, 20);
today.print();
return 0;
}
2.2 对象大小
- 类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量,
- 成员函数是否包含呢?函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段)
- 其实函数指针是不需要存储的,函数指针是⼀个地址,调用函数被编译成汇编指令[ call 地址],其实编译器在编译链接时,就要找到函数的地址
- C++规定类实例化的对象也要符合内存对齐的规则
回顾:内存对齐
•‘ 第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
• 注意:对齐数=编译器默认的⼀个对齐数与该成员大小的较小值。
• VS中默认的对齐数为8
• 结构体总大小为:最大对齐数的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
拓展:为什么要内存对齐?
实例计算
class A
{
public:
void print()
{
cout << _m << endl;
}
private:
char _m;
int _n;
};
class B
{
void print()
{
//...
}
};
class C
{
};
int main()
{
A a;
B b;
C c;
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
cout << sizeof(b) << endl;
return 0;
}
成员变量A内存对齐后是8,而没有成员变量的B和C类对象的大小是1
所以,没有成员变量的类对象,开1byte占位,不存储有效数据
3. this指针
概念
class Date
{
public:
//void Init(Date*const this,int year, int month, int day)
void Init(int year, int month, int day)
{
//this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void print()
{ }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
return 0;
}
• Date类中有 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指针
练习
1) 以下两个程序是否运行成功?
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
2) this指针存在内存哪个区域的
A. 栈 B.堆 C.静态区 D.常量区 E.对象里面
this指针是一个隐藏的形参,所以选A
回顾:c语言内存分配
4. C++和C语言实现Stack对比
• C++中数据和函数都放到了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++封装的⼀种体现,避免出现乱访问修改的问题。
• C++中有⼀些相对方便的语法:Init给的缺省参数会方便很多
成员函数每次不需要传对象地址,因为this指针隐含的传递了
使用类型不再需要typedef修改类名