在我们系统学习CPP这门面向对象的语言时,类和对象是我们要翻越的第一座大山。本篇博客我们将和大家一起学习CPP中的类和对象。希望阅读完本篇博客后对你在CPP的理解上有一定的帮助。
面向过程和面向对象初步认识
C语言是面向过程的,其关注的点是过程,分析出求解问题的步骤,通过函数调用逐步解决问题,而CPP是基于面向对象的,其关注的点是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
下面通过洗衣服的例子来初步认识什么是面向过程,什么是面向对象。
一、类的定义
class className
{
... // 类体:由成员函数和成员变量组成
}; // 一定不要漏掉后面的分号
class为定义类的关键字,className为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或类的成员变量; 类中的函数称为类的方法或类的成员函数。
| 类的两种定义方式 |
类的声明与定义分离最大的好处就是增加了代码的可读性。
二、访问限定符
在CPP中规定了三种访问限定符,分别为:public(公有)、protected(保护)、private(私有)。访问限定符的作用是限制在类外对类中成员的访问权限。
【访问限定符说明】
- public修饰的成员在类外可以直接被访问。
- protected和private修饰的成员在类外不能直接被访问。
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
- 如果后面没有访问限定符,作用域就到 } (即类结束)。
- class的默认访问权限为private,struct的默认访问权限为public(因为要兼容C语言中struct的用法)。
三、类的实例化
用类类型来创建一个对象的过程,称为类的实例化。
- 类是用于描述对象的,限定了一个类中有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
- 一个类可以实例化出多个对象,通过类实例化出的对像占用实际的内存空间来存储类中的成员变量。
下面将举一个例子来形象的理解类和对象之间的关系。
通过以上的分析,你能解释以下代码的错误吗?
class A
{
public:
int _data;
};
int main()
{
A::_data = 10;
return 0;
}
四、类对象大小的计算
一个类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
class A
{
public:
void SetData(char c = '0', int i = 0)
{
_c = c;
_i = i;
}
void PrintA()
{
cout << _c << _i << endl;
}
private:
char _c;
int _i;
};
类对象的存储方式猜测
以上给出了三种存储方式,计算机到底采用哪一种呢?接下来将用一个简单的程序来验证。
#include <iostream>
using namespace std;
// 类中既有成员变量,又有成员函数
class A1
{
public:
void Func1()
{
;
}
private:
int _i;
};
// 类中仅有成员函数
class A2
{
public:
void Func2()
{
;
}
};
// 类中什么都没有---空类
class A3
{
};
int main()
{
cout << sizeof(A1) << endl;
cout << sizeof(A2) << endl;
cout << sizeof(A3) << endl;
return 0;
}
由程序运行结果可以得知,sizeof(A1) = 4,sizeof(A2) = 1,sizeof(A3) = 1。
| 结论 |
一个类的大小,实际就是该类中”成员变量”之和,但是要注意内存对齐。(内存对齐规则可参考我的另一篇博客 --> 结构体内存对齐规则)
值得注意的是,当一个类中只有成员函数或者为空,则该类的大小为一个字节(当通过该类实例化出具体对象时,系统为该对象分配一个字节的内存空间,该空间不存储有效数据,仅用来占位,标识对象的存在)。
五、this指针
我们先定义一个A类,然后执行以下代码。
#include <iostream>
using namespace std;
class A
{
public:
void Init(int i = 0)
{
_i = i;
}
void Print()
{
cout << _i << endl;
}
private:
int _i;
};
int main()
{
A a1, a2;
a1.Init(10);
a2.Init(100);
a1.Print();
a2.Print();
return 0;
}
对于上述代码中定义的A类,引出这样一个问题:
A类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当a1调用 Init 函数时,该函数是如何知道应该设置a1对象,而不是设置a2对象呢?
CPP中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
| this指针的特性 |
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用。
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。(面试题)
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递。
问:this指针存在哪个区域呢?
答:因为this指针是形参,所以一般是存在栈帧中的(Visual Studio环境下对this指针进行了优化,使用ecx寄存器来传递this指针)。
本次与大家一起学习CPP中关于“类和对象”(上)的内容到这就已经接近尾声了,期待下次与你相遇。
< 你的关注,点赞,评论,收藏都是对我创作最大的鼓励 >
( 若本篇博客存在错误,望指出,感谢! )