C++之类(上)
- 一、类的定义
-
- [1.1 类定义格式](#1.1 类定义格式)
- [1.2 访问限定符](#1.2 访问限定符)
- [1.3 类域](#1.3 类域)
- 二、实例化
-
- [2.1 实例化的概念](#2.1 实例化的概念)
- [2.2 对象大小](#2.2 对象大小)
- 三、this指针
一、类的定义
1.1 类定义格式
1、class为定义类的关键字 ,{}中为类的主体,注意类定义结束时后⾯分号不能省略 。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。
c
class stack
{
int*arr;
int size;
int capacity;//成员变量
void Push(int x);
int Top();
void Pop();//成员函数
};
这个就是一个栈的类,它与结构体不同,类是变量与函数不分离。
2、为了区分成员变量,⼀般习惯上成员变量会加⼀个特殊标识,如成员变量前⾯或者后⾯加_ 或者 m开头。
c
class direction
{
public:
void Init(int left, int right, int up, int down)
{
_left = left;
_right = right;
_up = up;
_down = down;
}
private:
//在这里有两种方式可以用来区分
//1、在成员变量前加_
//2、用this指针例如(this->left=left)
int _left;
int _right;
int _up;
int _down;
};
3、 C++中struct也可以定义类,C++兼容C中struct的⽤法,同时struct升级成了类,明显的变化是struct中可以定义函数, ⼀般情况下我们还是推荐⽤class定义类。
c
struct stack
{
int*arr;
int size;
int capacity;//成员变量
void Push(int x);
int Top();
void Pop();//成员函数
};
这个也是可以的。
4、定义在类⾯的成员函数默认为inline。如果我们不想在类里写我们的函数体,就得用::域作用限定符。
c
class direction
{
public:
void Init(int left, int right, int up, int down);
private:
//在这里有两种方式可以用来区分
//1、在成员变量前加_
//2、用this指针例如(this->left=left)
int _left;
int _right;
int _up;
int _down;
};
void direction::Init(int left, int right, int up, int down)
{
_left = left;
_right = right;
_up = up;
_down = down;
}
1.2 访问限定符
1、C++⼀种实现封装 的⽅式,⽤类将对象的属性与⽅法结合在⼀块 ,让对象更加完善,通过访问权限选择性的将其接⼝提供给外部的⽤户使⽤。这里的接口指的就是函数。
2、public修饰的成员在类外可以直接被访问 ;protected和private修饰的成员在类外不能直接被访问 ,protected和private是⼀样的,在继承章节才会体现它俩的不同。
大家可以看到size可以轻易的访问,而Top就不能被访问。
3、访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到 }即类结束。
c
class stack
{
public:
int*arr;
int size;
int capacity;//成员变量
//public的范围到private之前
private:
void Push(int x);
int Top();
void Pop();//成员函数
//后面已经没有访问限定符了,那么private的范围到"}"之前
};
4、class定义成员没有被访问限定符修饰时默认为private,struct默认为public。
5、最后访问限定符可以多次出现,但实际使用一般不这样写。
1.3 类域
类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤ :: 作⽤域操作符指明成员属于哪个类域。
特别是这种两个类成员函数名字一样的,一定要指明类域。
二、实例化
2.1 实例化的概念
1、⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
c
class direction
{
public:
void Init(int left, int right, int up, int down);
private:
//在这里有两种方式可以用来区分
//1、在成员变量前加_
//2、用this指针例如(this->left=left)
int _left;
int _right;
int _up;
int _down;
};
void direction::Init(int left, int right, int up, int down)
{
_left = left;
_right = right;
_up = up;
_down = down;
}
int main()
{
direction d1, d2;
return 0;
}
在这里我们就实例化了两个对象d1,d2。
2、类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间。
什么意思呢?就是我们定义的类只是一个声明,当我们创建对象,例如上面的d1,d2时才会分配空间。
3、⼀个类可以实例化出多个对象,实例化出的对象 占⽤实际的物理空间,存储类成员变量。
2.2 对象大小
类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针。
我们只需要记住在算对象大小的时候,不需要计算成员函数,只计算成员变量。
那现在问题来了,要是我们的类里什么都没有,但我们用它实例化了对象,那实例化后的对象的大小是多少啊?
这⾥给1字节,纯粹是为了占位标识对象存在。
其次我们要算对象的大小我们只需要计算成员变量的大小,其算法与struct的算法一致都是符合内存对齐原则:
那大家有没有想过为什么要内存对齐啊?
三、this指针
首先我们先看一个代码:
c
class direction
{
public:
void Init(int up, int down, int left, int right)
{
_left = left;
_right = right;
_up = up;
_down = down;
}
public:
int _left;
int _right;
int _up;
int _down;
};
int main()
{
direction d1, d2;
d1.Init(0, 1, 2, 3);
d2.Init(3, 2, 1, 0);
cout << d1._up << ' ' << d1._down << ' ' << d1._left << ' ' << d1._right << endl;
cout << d2._up << ' ' << d2._down << ' ' << d2._left << ' ' << d2._right << endl;
return 0;
}
为什么这里是同一个函数,打印的确实一样的呢?这里就引入了this指针,this指针通常都是隐含的。
那我们将它恢复回原来的样子:
c
class direction
{
public:
//void Init(data*const this,int up,int down......)
void Init(int up, int down, int left, int right)
{
this->_left = left;
this->_right = right;
this->_up = up;
this->_down = down;
}
public:
int _left;
int _right;
int _up;
int _down;
};
int main()
{
direction d1, d2;
d1.Init(0, 1, 2, 3);
//d1.Init(&d1, 0, 1, 2, 3);
d2.Init(3, 2, 1, 0);//d2.Init(data* const this,3,2,1,0);
//d2.Init(&d2, 3, 2, 1, 0);
cout << d1._up << ' ' << d1._down << ' ' << d1._left << ' ' << d1._right << endl;
cout << d2._up << ' ' << d2._down << ' ' << d2._left << ' ' << d2._right << endl;
return 0;
}
需要注意的是:C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。
最后我们需要知道this指针存在于哪个区域,是堆、栈,还是常量区还是对象里?
我们要知道this指针是作为形参传递的对不对,形参存储于栈里,所以在这里栈比较合适,但是由于c++里经常使用this指针,所以有些编译器会将其放入寄存器,但是在做题过程中我们选栈。
这两个题目有助于我们理解this指针,大家可以简单的看下
1、问它运行结果是什么?
c
#include<iostream>
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
//A、编译报错 B、运⾏崩溃 C、正常运⾏
这个题目选择正常运行,虽然这里p是空指针,但我们这里是将它的地址作为参数传递,成员函数不存于类的变量中,我们访问成员函数并没有通过this指针。
c
#include<iostream>
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}//A、编译报错 B、运⾏崩溃 C、正常运⾏
而在这里我们使用了_a这个成员变量,这里解引用了空指针,所以这里运行崩溃。