目录
- 1.结构体与类
- 1.1.C++struct和class定义的区别
- 2.类的定义
- 3.类的访问限定符
- 3.1.访问限定符
- 3.2.封装:
- 4.类的作用域
- 5.类的实例化
- 6.类象的存储方式
- 6.1.类的大小
- 7.this指针
- 7.1.this指针的特性
- 7.1.1.this指针是否可以为空指针nullptr
- 8.C语言结构体与C++类的对比
1.结构体与类
C++兼容了C语言中结构体原本的用法,同时将结构体升级成为新的类。
在C语言结构体中,只可以定义变量。
在C++类中,不仅可以定义变量,还可以定义函数。
同时用法也有区别:
//结构体:
typedef struct ListNode
{
int val;
struxt ListNode* next;//typedef此处还未生效,用不了
}LtN;
----------------------------
//类:
struct ListNode
{
int _val;
ListNode* _next;
};
//C语言结构体当中struct ListNode为名字
//C++类中ListNode为名字
//C++中也支持C语言的结构体写法
1.1.C++struct和class定义的区别
那么在C++中struct和class有什么区别呢?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。
C++中struct也可以用来定义类,和class定义类是一样的。
区别:
struct定义的类默认访问权限是public;
class定义的类默认访问权限是private;
注意:在继承和模板参数列表位置,struct和class也有区别。
2.类的定义
1.类的定义方法:struct 名称{} 或者 class 名称{}
C++中更常用class来定义类。
类中的变量:称为成员变量/类的属性
类中的函数:称为成员函数/类的方法
class _className
{
//成员函数
//成员变量
}; //分号不要忘记
2.类有两种定义方式:
- 声明和定义都放在类体中:
注:成员函数在类体中定义时,编译器可能会将其看成内联函数inline处理。
class Stack
{
void push(int x)
{
//....
}
int* _a;
int _size;
int _capacity;
};
- 成员函数的声明放在头文件(类体)中,定义放在.cpp中:
//Stack.h
class Stack
{
void push(int x);
int* _a;
int _size;
int _capacity;
};
----------------------
//Stack.cpp
#include"Stack.h"
void Stack::push(int x)
{
//....
}
推荐定义和声明分开来写。
3.类的访问限定符
3.1.访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
1.访问限定符:public、protected、private
说明:
- public公有修饰的成员在类外可以直接被使用。
- protected、private修饰的成员在类外不可以直接被使用,在类内可以直接使用。(暂时认为这两者类似)
- 访问限定符的访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止, 如果后面没有访问限定符,作用域就到 } 即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C),一般不建议使用默认访问权限,都要自己写。
注意:访问限定符只在编译时有用,映射到内存后没有区别。
补充:若在类外想使用私有的成员变量,可以通过成员函数来访问。
3.2.封装:
面向对象的三大特性:封装、继承、多态。
学习类和对象时,主要对应的是封装。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
4.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。
在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
5.类的实例化
用类类型创建对象的过程,称之为类的实例化。
class Stack //类的定义,没有实际开辟空间,不能储存内容,不是实例化
{
public:
void init(int capacity)
{
//...
}
void push(int x)
{
//....
}
private:
int* _a; //成员变量的声明
int _size;
int _capacity;
};
int main()
{
Stack st1;//类的实例化
Stack st2;
Stack.init(4);//错误,Stack只是类的定义,没有实际开辟空间对对象实例化
st1.init(4);
st1.push(1);
}
- 类是对对象的描述,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
- 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
6.类象的存储方式
在类中:
成员变量是声明。
而成员函数是定义。
而对于实例化出来的对象,成员变量存储在对象中,而成员函数没有储存在对象中,成员函数的地址也没有储存在对象中。
那么为什么成员函数没有储存在类中呢?
因为每个对象中成员变量是不同的,但是调用同一份函数,如果成员变量和成员函数都在类中存储,当一个类创建多个对象时,每个对象中都会保存一份成员函数的代码,相同代码保存多次,浪费空间。
同时通过sizeof计算类实例化出来的对象的大小,可以发现成员函数的地址也没有储存在对象中。
成员函数实际存储的位置在代码段中。
总结:
每个对象的成员变量是不同的,需要独立存储;
每个对象调用成员函数是一样的,放在公共区域(代码段);
6.1.类的大小
一个类的大小,实际就是该类中成员变量之和,要注意内存对齐。
注意空类的大小,若对象中只有成员函数,或者成员变量和成员函数都没有,那么就占一个字节,是用来占位的,不存储有效数据,仅标识对象被定义出来了。
7.this指针
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 d1;
Date d2;
d1.init(2023, 1, 1);
d2.init(2023, 1, 2);
return 0;
}
对于上述代码,存在一个问题:
在Data类中有一个init函数,使用Data类实例化了d1和d2两个对象,当我们用d1调用init函数时,函数是怎么知道该设置d1对象还是d2对象呢?(函数不是存储在对象中的,是存储在代码段中共享的)
在C++中,引入了this指针来解决这个问题:
C++给每个非静态的成员函数增加了一个隐藏指针参数,该指针指向当前调用它的对象,在调用函数时,函数体中的所有用到成员变量的操作都是通过这个指针来访问的。这些操作由编译器自动完成,用户看不见。
并且编译器不允许用户自己传递这个指针参数(在形参和实参的位置不能自己用)。但是在函数体内部用户可以显式使用this指针。
this指针存储的位置:
前面说了类中只存储成员变量,所以this指针没有存储在类中。
this指针实际存储在栈上,因为this指针时函数的一个形参临时变量,只不过是编译器自动生成隐藏的。(vs下存储在ecx寄存器)
7.1.this指针的特性
- this指针的类型:类类型* const,在成员函数中,不能给this指针赋值。
- 只能在成员函数的内部使用。
- this指针本质是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针。
- this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
7.1.1.this指针是否可以为空指针nullptr
class Date
{
public:
void init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void _ptr()
{
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.init(2023, 1, 1);
d2.init(2023, 1, 2);
//
Data* ptr = nullptr;
ptr -> _ptr(); //正常运行
ptr -> init(2023, 1, 3); //程序崩溃
//
return 0;
}
为什么这里用空指针调用_ptr函数可以通过,而调用init函数程序则会崩溃呢?
因为函数不存储在对象里面,这里的空指针ptr只是给编译器说明了函数在类域中,作为this指针存在,在函数体内并没有发生解引用,所以不会报错。
总结:
在_ptr函数中,ptr虽然是空指针,但是没有解引用,所以没问题。
在init函数中,虽然传入了ptr空指针作为this指针,但是在函数内部进行了队成员变量的解引用,所以程序会崩溃。
补充:若为 (*ptr)._ptr(); 同样不会报错,原理和前面一样,并不是说出现了" * " 或 " -> "就会解引用,有没有解引用,取决于右边的对象在不在类中,函数体不在类中,ptr指针只起到this指针的作用。
8.C语言结构体与C++类的对比
C语言结构体:
- 数据与操作数据的方法分离
- 数据的访问是自由的,不受限制
- 实现相对复杂一点,容易出错
- 结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中
C++类:
- 数据与操作数据的方法结合,封装在类中,使用更加方便安全
- 通过控制访问权限可以控制访问方式,允许直接访问的设置公有,不允许的设置私有