C++:类和对象(上)
一、引言
类和对象是C语言所没有的,也是非常重要的一部分,理解它至关重要。
C++中的类是面向对象编程的核心概念,它是一种用户自定义的数据类型,用于封装数据(属性)和操作数据的方法(函数)。类定义了对象的蓝图或模板,描述了对象应具备的特征和行为。对象则是类的具体实例,是根据类定义创建的实际实体,占用内存空间并包含具体的属性值。通过类和对象,C++实现了封装、继承和多态等面向对象特性,使代码更模块化、可重用和易于维护。
二、类的定义
2.1、类的定义格式
class 为类的关键字,具体语法形式如下:
class name //name为类的名字,自己取
{//……类的成员,可以为变量,可以为函数等}; //分号不能省略
(1)类的成员名称为了区分,要在前面加上 " _ " 或者 " m " 或者其他,具体看公司要求;
(2)struct 也可以充当关键字来定义类,与C语言不同的是,C++中的struct的成员可以为函数。但是,我们还是推荐使用 class 定义类;
(3)在类的内部定义的函数,都默认为内联函数;如果不想内联,可以在类中声明,然后在外部定义;
(4)类一般定义在头文件中。
2.2、类的访问限定符
类的访问限定符:就是用来限定类的内部成员权限的符号。共有三个:private ,public ,protected 。
2.2.1、private (私有)
用法如下:
class student
{
private:int number;char[10] name;int age;int sex;
};
private 意味着私有,也就是只能在Student 内部访问,不能在外部访问。例如,下面的代码就是错误的,因为访问了私有成员。
Student student1;
std::cout << student1.name << endl;
对了,如果类的成员变量什么访问限制符都没有,则默认为被 private 修饰。
2.2.2、public (公有)
public 意思是公有,可以被外部访问。用法和private一样,如下:
class Student
{
private:int number;int age;char[10] name;
public:void DisplayAge(){cout << "学生年龄:" << age << endl;}
};
一般我们会把函数成员放在公有部分,私有部分一般都是存放一些数据的。
2.2.3、protected (保护)
用法和上面两个一样,这里不多说。
被 protected 修饰的成员为保护成员,在外部不能直接访问,只有通过派生类和内部才能访问。(派生类后面会将)
注意:这三个访问限定符没有先后之分,你可以先写公有部分,也可以先写私有部分
2.3、类域
(1)类域就是类定义的一个新的作用域,类的所有成员都在类的作用域中。在类的外部定义成员要用 :: ;
(2)类域影响编译器的查找规则。如果在调用一个函数时,没有指定类域,那么编译器就会默认该函数为全局函数,会在全局寻找其定义和声明,如果没有搜索到就会报错。
三、类的实例化
3.1、实例化的概念
创建一个类,只是创建了一个类,只是告诉编译器有这样的一个类,使用了吗?没有使用。你可以类比C语言中的结构体,你只是声明了一个结构体,并没有使用,使用结构体首先需要创建一个变量,类也是如此。
简单来说,类的实例化就是用类创建一个对象。一个类可以创建很多的对象。例如:
class Student
{
private:int _age;char[10] _name;
public:void SetInfo(int age, char* name){_age = age;_name = name;}
};int main()
{Student student1; //实例化SetInfo( 18 , "zhangsan" );return 0;
}
3.2、对象的大小
用类创建一个对象叫实例化,系统就会给这个对象分配一个空间,那么这个空间有多大呢?里面的成员要不要遵循对齐原则?
3.2.1、对齐原则
对象是存在内存对齐的,规则同C语言的结构体对齐,这里就省略了。
3.2.2、成员函数的储存
对象中的内存里没有成员函数的位置。成员函数会单独放在一个地方等着调用。
3.2.3、空的类的对象的大小
求下面类创建的对象的大小:
class B
{};
最后结果是1,开辟1个字节的空间就是为了标记这个类的对象存在过。就像唯一能证明她来过的只有心里的那道疤痕。
四、this 指针
先看以下代码,记住两个对象 s1 和 s2 。
class Student
{
private:int _age;int _number;char[10] _name;
public:void ShowData(){cout << "学生的学号:" << _number << endl;cout << "学生的姓名:" << _name << endl;cout << "学生的年龄:" << _age << endl;}//…………
};int main()
{Student s1;Student s2;//…………填写学生信息s1.ShowData();s2.ShowData();return 0;
}
好,我们可以看到,s1 和 s2 都分别调用了打印各自数据的函数。但是,函数是不会存储在对象中的,而是其他地方,所以,s1 和 s2 调用的同一个函数。问题来了,这个函数如何区分到底打印 s1 的数据还是 s2 的数据呢?答案就在 this 指针。
原来,编译器会在类的成员函数的第一个参数添加一个新的参数,叫 this指针。实际上,ShowData函数的原型为:
void ShowData( Student* const this)
{cout << "学生的学号:" << this->_number << endl;cout << "学生的姓名:" << this->_name << endl;cout << "学生的年龄:" << this->_age << endl;
}
通过 this 指针,函数就能区分到底该打印谁了。
注意:C++规定,不能在函数的实参和形参的位置写出 this 指针 ,但是可以在函数内部写出this指针。
练习:下面的代码是正常运行还是运行崩溃?
using namespace std;
class A
{
public:void Print() {cout << "A::Print()" << endl;}
private:int _a;
};int main()
{A* p = nullptr;p->Print();return 0;
}
答案是正常运行。虽然 p 为空指针,还引用了成员函数Print(),但是,成员函数储存在别的地方,在编译的时候就已经确定了,所以不会报错。但是,如果成员函数涉及this指针的解引用,那就会报错了,因为空指针不能解引用。
五、C语言和C++实现栈的对比
C的代码就不放到这里了,大家都会。
下面是C++版本的栈:
//创建一个栈的C++版本
typedef int StackDataType;
class Stack
{
private:StackDataType* _arr;int _top;int _capacity;
public://栈的初始化void StackInit(){assert(this);_arr = nullptr;_top = _capacity = 0;}//入栈void StackPush(StackDataType x){if (_top == _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;StackDataType* tmd = (StackDataType*)realloc(_arr ,sizeof(StackDataType) * newCapacity);if (tmd == nullptr){perror("malloc fail!");exit(1);}_arr = tmd;_capacity = newCapacity;}_arr[_top++] = x;}//判断栈是否为空bool StackEmpty(){return _top == 0;}//出栈void StackPop(){assert(!StackEmpty());_top--;}//取栈顶StackDataType StackTop(){assert(!StackEmpty());return _arr[_top-1];}//销毁栈void StackDestory(){if (_arr)free(_arr);_arr = nullptr;_top = _capacity = 0;}//打印栈void StackPrint(){assert(!StackEmpty());for (int i = 0; i < _top; i++){cout << _arr[i] << " ";}cout << endl;}
};
int main()
{Stack s1;s1.StackInit();s1.StackPush(1);s1.StackPush(2);s1.StackPush(3);s1.StackPush(4);s1.StackPush(5);s1.StackPrint();s1.StackPop();s1.StackPrint();s1.StackDestory();return 0;
}
感觉C++更好用,把这些函数都封装了起来,而且使用起来更加方便。建议读者下去要亲自敲几个类体验体验!才能体会到类的魅力。
六、结语
到这里,就已经认识,会用类了。但这还远远不够,更多细节还在后面,比如类里面自带的几个函数等。加油,追梦人!