C++类和对象¶
约 2295 个字 424 行代码 2 张图片 预计阅读时间 13 分钟
类与对象的基本认识¶
C语言中的面向过程指只要涉及到解决问题的主要步骤就需要去实现对应的方法,不论这个方法属于哪一个对象的,而在C++中,每一个对象只需要实现自己的方法,剩下的交给对象之间的交互完成即可
类和对象的基本概念¶
类¶
类的认识¶
在C语言中,结构体中只能定义变量,但是在C++中,结构体可以定义变量和函数
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
但是在C++中,更喜欢用class
代表类,struct
更多还是结构体,所以上面代码改为
C++ | |
---|---|
1 2 3 4 5 6 7 8 |
|
类的定义¶
在C++中,使用下面的语法进行类的定义
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
在类的定义语法中,class
为类定义的关键字,className
为类名,{}
中的内容为类的主体
类的内容一般称作成员,而类中的变量一般成为成员变量,类中的方法一般称为方法或者成员函数
Note
需要注意的是,成员变量可以出现在成员函数的上方也可以出现在成员函数的下方,因为编译器在寻找成员变量时是将类当做一个整体看待,此时不论是在成员函数的上方还是下方都能寻找到需要的成员变量
类的两种定义方式¶
类的声明和定义分开¶
在C++中,如果在大项目中,一般使用类的声明和定义分开的方式,即在头文件中声明,在实现文件中定义,但是需要注意下面的问题:
- 因为声明和定义分开处理,所以定义所在的文件需要引入类声明所在的头文件
- 当定义需要实现类中声明的方法时将看不到放在头文件中的类的成员变量,此时需要使用域作用限定符指定对应的类
代码实例:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
类的声明和定义放在一起¶
当项目不够大时,或者只是简单的实现时,可以考虑将类的声明和定义放在同一个文件中,需要注意的是,当成员函数的声明和定义在一起时,编译器可能会当做内联函数处理
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
成员变量名的命名规则建议¶
当成员函数的定义在类中时,需要考虑到成员函数的形式参数(局部变量)和成员变量之间的关系
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
如果像上面的代码中,成员变量和add
函数中的局部变量相同,那么会因为局部优先原则导致成员变量并没有被赋值成功,如下图所示
而如果将成员变量和add
函数中的局部变量加以区分,则此时可以成功将局部变量的值存入成员变量中
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
结果如下图所示:
Note
上面的区分成员变量和形式参数的方式仅供参考,具体以环境要求为主
类的封装¶
面向对象的三大特性:封装、继承和多态
在C++中,类的封装表示将方法和成员变量进行结合,对外只通过公开的接口与其他对象进行交互
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
在上面的代码中,test
函数并没有公开,因为访问权限的问题,所以外部对象将无法使用
类的访问限定符¶
在C++中,一共有三个访问限定关键字,即private
(私有)、protected
(受保护)以及public
(公开)
在C++中,类的访问权限有下面的特点
public
修饰的成员在类外可以直接被访问protected
和private
修饰的成员在类外不能直接被访问- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到
}
即类结束 class
的默认访问权限为private
,struct
为public
(因为struct
要兼容C语言)
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
需要访问类中的函数时,可以指定访问权限,使用public
后可以在类外访问
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
类的作用域¶
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
对象¶
类的实例化¶
用类类型创建对象的过程,称为类的实例化
-
类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//无法直接使用类名访问成员,除非成员是静态的 #include <iostream> using namespace std; class Person { public: int age; static int num; }; int main() { Person::age = 0;//类在没有对象之前不可以直接使用非静态的成员变量 Person::num = 0;//静态变量可以直接用类名访问 return 0; } 报错信息: 对非静态成员“Person::age”的非法引用
在上面的代码中,因为类中的变量只是声明,告诉编译器当前类有哪些成员变量,但是并不为其开辟空间,而静态变量在编译过程中会分配空间,故可以直接用类名进行访问
需要访问类中的非静态变量时,需要用类名实例化出对象,再用对象去访问类中的变量
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include <iostream> using namespace std; class Person { public: int age; }; int main() { Person p;//实例化,创建对象 p.age = 10; cout << p.age << endl; return 0; } 输出结果: 10
在上面的代码中,因为
p
为Person
类的对象,故在编译时会为p
分配空间,此时p
对象的成员变量age
就有了存储空间 -
一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#include <iostream> using namespace std; class Person { public: int age; }; int main() { //Person类的三个对象 Person p; Person p1; Person p2; p.age = 10; p1.age = 20; p2.age = 30; cout << p.age << endl; cout << p1.age << endl; cout << p2.age << endl; return 0; } 输出结果: 10 20 30
类对象的大小¶
在计算类对象的大小时,和计算结构体大小的方式相同,并且都需要考虑到内存对齐的规则,成员函数和静态成员均不会考虑到大小计算中
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Quote
计算一个类的大小实际上是在计算类对象的大小,所以有sizeof(类)
=sizeof(类对象)
在类对象访问成员变量时,每一个成员变量属于各自的对象存放在栈区,而成员函数是所有对象公共的存放在公共代码段区
如果类中没有成员变量,即只包括成员函数或者是空类时,类的大小为1个字节,这一个字节是为了表示给类占位,标识这个类存在
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
this
关键字¶
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
在上面的代码中,对象p1
和对象p2
的成员变量age
和num
有自己的存储的空间,两个对象的成员变量不会冲突,并且尽管test
函数是公共的,在test
函数中age
和num
也对应着各自的对象
在C++中,有一个关键字this
,是C++编译器给每个非静态的成员函数增加的一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成,所以上面的代码实际上是
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
但是上面的代码只是演示,实际过程中不可以显式得将this
作为函数的形参
this
指针的特点¶
this
指针的类型:类型* const
,即成员函数中,不能给this
指针赋值。- 只能在成员函数的内部使用
this
指针本质上是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this
形参。所以对象中不存储this
指针。this
指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx
寄存器自动传递,不需要用户传递- 一般情况下,
this
指针存储在栈区上或者寄存器ecx
(VS编译器做出的优化)中
Note
注意,this
指针有时可以为空,有时不可以为空
- 当对象调用函数不需要实际进行解引用操作时,
this
指针可以为空指针 - 当对象调用的函数实际进行解引用操作时,
this
指针不可以为空指针,否则会出现空指针解引用错误
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
在上面的代码中,二者均不会出现编译错误,但是对于调用test2
来说,因为test2
函数中涉及到访问成员变量的空间,而指针对象p
是空指针,没有实际的地址空间,导致成员变量val
的空间也不存在,从而出现空指针解引用错误,而之所以访问成员函数不需要解引用是因为成员函数并不在对象所在的空间中,不需要在对象的空间中找函数,而是直接去公共代码段中寻找