1,结构体到类过渡
c++兼容c语言,结构用法可以继续使用,同时struct也升级成了类。
struct Stack {
//成员变量
int* a;
int top;
int capacity;
//成员函数
void Init() {
a = nullptr;
top = capacity = 0;
}
void push(int x) {
}
};
既可以用结构体的方式去访问成员变量,也可以用类的的方式去访问成员变量。
2,类的定义
(1)class 类名{主体},主体包含成员变量,和成员函数
(2)访问限定符(三种):public(共有),protected(保护),private(私有)
访问限定符一般作用到下一个访问限定符结束或者遇到“}”类结束
在类中,成员的默认访问限定符是私有的,所以如果不写访问限定符,类中的成员函数是无法使用的。而在结构体中,成员的默认访问限定符是共有的。
(3)类的声明和定义可以分离:
例如:
stack.h//声明部分
class Stack {
private:
int* a;//数组的地址
int top;//下一个元素的下标
int capacity;//容量
public:
void Init();
void push(int x);
int Top();
};
//定义部分
#include"stack.h"
#include <exception>
需要在类域中寻找变量,所以需要加 Stack::
void Stack::Init() {
a = nullptr;
top = capacity = 0;
}
void Stack::push(int x) {
if (top == capacity) {
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
a = (int*)realloc(a, sizeof(int) * newcapacity);//realloc申请空间
capacity = newcapacity;
}
a[top++] = x;
}
int Stack::Top() {
return a[top - 1];
}
注意:直接在类里面定义,会默认为内联函数,不会生成符号表
(4)类的实例化
类中的成员变量相当于声明部分,并没有分配空间,这时就需要定义开空间,也称为对象的实例化。成员变量会在对象中存储一份,但成员方法在公共代码区中。
(5)类的大小
class Date1{//类不占内存的空间
private:
int _year;//声明
int _month;
int _day;
public:
void Inti(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
};
Date d;
cout << sizeof(Date) << endl;//12
cout << sizeof(d) << endl;//12
类的大小或者说对象的大小看的是成员变量的类型和个数,且遵循内存对齐规则,所以上述对象的大小为12(3*4)
例1:
class A {//4byte
int a;
void add() {
}
};
例2:
class A1 {//内存对齐规则、8byte
int a;
char b;
void add() {
}
};
例3:class B {//1byte表示占位表示对象存在过
void add() {
}
};
class C {//1byte
};
(6)this指针:
编译器在调用成员函数是,会有一个隐藏的this指针
例如:
void Inti(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
Date d;
d.Inti(2024, 9, 9);
而编译器处理后为:
void Inti(Date* this,int year, int month, int day) {
this->_year = year;
this->_month = month;
this->_day = day;
}
Date d;
d.Inti(&d,2024, 9, 9);
但是自己写代码时,不能写成把this作为参数传递,但在类里面可以使用,比如: this->_year = year;这样的写法是可以的
总结:this在实参和形参位置不可以显示的写,但是在类里面可以显示的用,this作为形参存在栈里面,vs里面存在寄存器里面
3,构造函数
构造函数:特殊的成员函数
1,函数名与类名相同
2,无返回值
3,对象实例化时,自动调用
4,构造函数可以重载
例1:class Date {//类不占内存的空间
private:
int _year;//声明
int _month;
int _day;
public:
//无参数的构造函数
Date() {
_year = 1;
_month = 1;
_day =1;
}
//有参数的构造函数
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
*/
void Print() {
cout << _year << " " << _month << " " << _day << endl;
}
};
类中的两个构造函数两者结合一下(缺省参数);
Date(int year=1, int month=1, int day=1) {
_year = year;
_month = month;
_day = day;
}
例2:class Stack {
private:
int* a;
int top;
int capacity;
public:
Stack() {
a = nullptr;
top = capacity = 0;
}
void push(int x) {
if (top == capacity) {
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);
if (tmp == nullptr) {
perror("realloc fail");
exit(-1);
}
if (tmp == a) {
cout << capacity <<"本地扩容"<< endl;
}
else {
cout << capacity << "异地扩容" << endl;
}
a = tmp;
capacity = newcapacity;
}
a[top++] = x;
}
int Top() {
return a[top - 1];
}
void Pop() {
assert(top>0);
top--;
}
void Destroy() {
free(a);
a = nullptr;
top = capacity = 0;
}
Stack s1(1000);
for (int n = 0;n < 1000;n++) {
s1.push(n);
}在这种明确知道自己想要的空间的情况下,会不断扩容,本地扩容还好,异地扩容会非常的复杂,未了避免这种情况,我们可以写成以下这种代码:
Stack(size_t n=4) {
if (n == 0) {
a = nullptr;
top = capacity = 0;
}
else {
a = (int *)malloc(sizeof(int) * n);
if (a == nullptr) {
perror("realloc fail");
exit(-1);
}
top = 0;
capacity = n;
}
}//当你知道自己要的空间大小,这个构造函数可以减少扩容
如果你不传入参数,他会自动调用,并且空间大小初始化为4
1,不写构造函数时,编译器会自动生成,如果写了,编译器就不会提供了
2,内置类型成员不会处理(有些编译器会处理为0)(声明部分可以给缺省值)
3,对于自定义类型的成员才会处理,会去调用这个成员的构造函数,一般情况都需要我们写构造函数,决定初始化数值
4,默认构造函数,只有一个。注意:无参构造函数,全缺省构造函数,自动生成的构造函数,都默认为构造函数
总结:但是如果成员变量都是自定义类型,可以不进行初始化
4,析构函数
析构函数:
1,在类名前加 ~
2,没有参数没有返回值
3,只有一个析构函数,若没有显示定义,会自动生成一个默认的析构函数,析构函数不能重载
,内置类型成员不会处理,对于自定义类型的成员才会处理
4,在对象销毁时,自动调用析构函数
void Destroy() {
free(a);
a = nullptr;
top = capacity = 0;
}可以写成以下代码:
~Stack() {
free(a);
a = nullptr;
top = capacity = 0;
}