struct Stack
{
int* a;
int top;
int capacity;
};
void StackInit(struct Stack* ps);
void StackPush(struct Stack* ps, int x);
void StackPop(struct Stack* ps);
在C++中是兼容C的,但是C++把struct升级成了类
类里面可以定义成员变量,成员函数
struct Stack
{
void Init()
{
a = nullptr;
top = capacity = 0;
}
void Push(int x)
{
// ...
}
void Pop()
{
// ...
}
int* a;
int top;
int capacity;
};
int main()
{
struct Stack st1;
Stack st2;
st1.Init();
st1.Push(1);
st1.Push(2);
st1.Push(3);
return 0;
}
C++可以这样写,因为ListNode既是类名也是类型
struct ListNode_CPP
{
ListNode_CPP* next;
int val;
};
类的定义
C++中使用calss进行类定义
访问限定符:共有,保护,私有
1. public 修饰的成员在类外可以直接被访问 2. protected 和 private 修饰的成员在类外不能直接被访问 ( 此处 protected 和 private 是类似的 ) 3. 访问权限 作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止 4. class 的默认访问权限为 private , struct 为 public( 因为 struct 要兼容 C)class Stack
{
public:
void Init()
{
a = 0;
top = capacity = 0;
}
void Push(int x)
{
}
void Pop()
{
}
int Top()
{
return a[top-1];
}
private:
int* a;
int top;
int capacity;
};
int main()
{
Stack st;
st.Init();
st.Push(1);
st.Push(2);
st.Push(3);
st.a = nullptr;//无法访问,因为是私有,类外无法访问,但是类里面的可以访问
return 0;
}
类的两种定义方式:
1. 声明和定义全部放在类体中,需要注意:成员函数如果
在类中定义
,编译器可能会将其当成
内联函数处理(函数里面有递归或者代码过长就不会按内联函数处理)。
2.
声明放在
.h
文件中,类的定义放在
.cpp
文件
//.h
struct QueueNode
{
QueueNode* next;
int val;
};
class Queue
{
public:
void Init();
void Push(int x);
void Pop();
private:
QueueNode* head;
QueueNode* tail;
};
//.cpp
//类是一个域影响访问,使用::
void Queue::Init()//要说清楚是哪的,不然链接不上,告诉是类里的,去类里面找
{
head = tail = nullptr;
}
void Queue::Push(int x)
{}
void Queue::Pop()
{}
小函数,想成为
inline
,直接在类里面定义,防止链接错误
大函数,声明和定义分离
C语言,没办法封装,规范使用函数访问数据,也可以直接访问数据 不规范
C++,经过封装,必须规范使用函数访问数据,不能直接访问数据
在上面之所以使用
::是因为
类定义了一个新的作用域
,类的所有成员都在类的作用域中
。
在类体外定义成员时,需要使用
::
作用域操作符指明成员属于哪个类域。
那么我们观察以下代码
int a;//定义
class Person
{
public:
void Print();//这里是声明
private:
char _arr[20];//这里是声明还是定义 ?
char _age[3];//这里是声明还是定义 ?
int _year;//这里是声明还是定义 ?
};
答案是下面的是声明,那么为什么不是定义
因为此时类并没有进行实例化,也就是说没有进行Person hh;没有进行开辟空间,开辟了空间才能称之为定义
此时我们使用sizeof(Person)验证一下,计算结果为28,既然我们没有实例化,没有实际开辟空间为什么会算出来28这个结果,这就好比我们买房子时,我们的房子并没有建好,但是我们可以看到房子的图纸,根据图纸我们就可以知道我们的房子面积有多大
这里计算类对象的大小也是遵循C中结构体对齐规则的
结构体内存对齐规则 1. 第一个成员在与结构体偏移量为 0 的地址处。 2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为 8 3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。 4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。没有成员变量的对象,给1byte,占位不存储实际数据,标识对象存在
#includeusing namespace std; //extern int a; static int a; class Person { }; int main() { cout << sizeof(Person) << endl; return 0; }
this指针
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <
我们运行上面的程序
Date
类中有
Init
与
Print
两个成员函数,函数体中没有关于不同对象的区分,我们创建了d1,d2两个对象,但是当
d1
调用
Init
函数时,该函数怎么知道调用的时d1
对象,而不是
d2
对象,不会发生冲突
这里就涉及到this指针的问题,下面我们把this指针展示出来给大家看一下
public:
void Init(Data*const this,int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
void Print(Data*const this)
{
cout <_year<< "-" <_month << "-"<< this->_day <<
endl;
}
这里的this指针编译过程不会显示出来,使用时只可以在函数内部加this- >,在其它地方加错误(实参和形参位置不能显示传递和接收this指针)(可以在成员函数内部使用this指针)
d1.Init(&d1,2022,1,11);//正常使用时不用&d1,下同
d2.Init(&d2,2022,1,12);
d1.Print(&d1);
d2.Print(&d2);
C++
编译器给每个
“
非静态的成员函数
“
增加了一个隐藏的指针参
数,让该指针指向当前对象
(
函数运行时调用该函数的对象
)
,在函数体中所有
“
成员变量
”
的操作,都是通过该
指针去访问。只不过所有的操作对用户是隐形的,即用户不需要来传递,编译器自动完成
。
this指针的性质总结
1. this
指针的类型:类类型
* const
,即成员函数中,不能给
this指针赋值。
2. 只能在
“
成员函数
”的内部使用。
3. this
指针本质上是
“
成员函数
”
的形参
,当对象调用成员函数时,将对象地址作为实参传递给
this
形参。 所以对象中不存储
this
指针。
4. this
指针是
“
成员函数
”
第一个隐含的指针形参,一般情况由编译器通过
ecx
寄存器自动传递,不需要用户传递
那么this指针是否可以为空?
class A {
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
运行上述代码是可以正常运行的,首先p虽然是空指针,但是我们并没有解引用,其次这里的this指针虽然也是空指针,但是我们并没有使用它进行任何操作,至于为什么怎么调用的Print函数,是通过符号表call函数地址来调用的,下面这种情况就不一样了
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void PrintA()
{
cout<<_a<PrintA();
return 0;
}
这里运行就会崩溃了,原因是cout<<_a<_a<
this指针一般存在栈,他是一个形参也可能在寄存器(VS通过ecx寄存器传递,this访问变量提高效率)



