前言
学习了C++的入门,下面继续学习C++中的关键点:类和对象。这部分的内容很难。
1. 类定义
C++中类的定义和C语言中结构体很像,但是是在结构体上面进行了升级,即可以在类中定义函数。并且将结构体进行了升级,即也可以用struct 进行类的定义。
1.1 访问限定符
类的定义需要用到访问限定符,即private 、protected、public。这里的protected和private在后面的学习中才有区别,在现在我们可以认为是一样的,其修饰的成员在类外都不能被直接访问。而public是修饰的成员是在类外可以直接被访问的。
访问限定符的作用域从出现的位置直到下一个访问限定符出现的位置,如果后面没有访问限定符,则作用域到},即类结束。
class定义的成员如果没有被访问限定符修饰时默认为private,struct默认为public。
一般情况下,成员变量都会被限制为private\protected,而成员函数一般被设置为public。
1.2 类定义格式
class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省 略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或 者成员函数。
为了区分成员变量,通常在成员变量的前面加一个特殊标识,如_等。
定义在类里面的成员函数默认为inline函数。
定义出类后,用类名就可以直接进行创建对象,而不需要加类型。
class stack
{
public:
void init(int capacity = 4)
{
_arr = (int*)malloc(sizeof(int)*capacity);
if (_arr == nullptr)
{
perror("malloc fail");
exit(1);
}
_capacity = capacity;
_top = 0;
}
void push(int x)
{
if (_capacity == _top)
{
_capacity = 2 * _capacity;
int* newarr = (int*)realloc(_arr,(sizeof(int) * _capacity));
if (newarr == nullptr)
{
perror("realloc fail");
exit(2);
}
_arr = newarr;
}
_arr[_top++] = x;
}
int top()
{
assert(_top > 0);
return _arr[_top - 1];
}
void destroy()
{
free(_arr);
_arr = nullptr;
_top = 0;
_capacity = 0;
}
private:
int _capacity;
int _top;
int* _arr;
};
1.3 类域
C++里面的共有四个域,分别是全局域,局部域,命名域和类域。因此当在类域外定义成员时,需要用到::指明成员属于哪个域。
2. 实例化
2.1 什么是实例化
用类类型在物理内存中创建对象的过程,称为类实例化出对象。
类在声明的时候是没有内存空间的,要用类实例化出对象时,才会分配对象。
用一个类可以实例化出多个对象。
class date
{
public:
void init(int year = 1 ,int month= 1,int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout<<_year<<"/"<<_month<<"/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1;
date d2;
d1.init();
d1.print();
return 0;
}
这里用date类进行两次实例化。这里用了全缺省函数对date进行初始化。使用类和使用结构体的方法是一样的,如果是类,就用“.”,如果是类指针,就用->。
但是需要注意,这里定义变量的时候并没有开辟空间,当进行实例化之后才对变量开辟空间。
2.2 对象的大小
类中有各种变量,还有函数,但是函数是不包含在类中的。所以就是单独的存放各种变量的大小。
所以类的大小跟结构体是由一样计算的。
面试考题:为什么需要内存对齐呢?
答:因为CPU在读取数据的时候往往进行4,8,16个字节的读取,如果不进行内存对齐,当读取所需数据时可能需要进行多次读取,因此我们通过空间换时间。
内存对齐规则 •
第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
• 注意:对齐数 = 编译器默认的⼀个对齐数 与 该成员大小的小值。
• VS中默认的对齐数为8
• 结构体总大小为:最大对齐数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小 就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
但是不一样的是,如果类中没有变量,此时类的空间大小为1,用于表示对象存在过。
3. this 指针
date类中的函数没有关于不同对象的区分,那么当d1调用函数时,该函数怎么知道调用哪个对象呢?这里C++给了一个隐含的this指针用于解决这里的问题。
在编译后,类的成员函数会默认在形参的第一个位置增加一个当前类型的指针,叫做this指针。即
init 函数的真实原型为:void Init(Date* const this, int year, int month, int day)。
类的成员函数中访问成员变量,本质都是通过this指针访问的,如init给_year赋值,是this->_year = year;
C++规定,不能在形参和实参的位置写this指针,但可以在函数体内显性使用this指针。
4. 三个测试题
答案是:CBA
分析:
1. 主函数内定义了一个A类型的指针,并且为空,然后解引用调用print函数,但是实际上没有解引用,因为函数并没有存储在对象中,所以会正常运行。
2. 跟第一题类似,但是增加了一个输出类里面的_a,所以需要进行解引用,但是是空指针,所以不能解引用,因此在运行的时候会崩溃。
3.之前讲了,this指针是形参,所以存储在栈里面。
5.C++和C语言实现stack对比
C++将数据和函数封装到了类里面,通过访问限定符进行限制,避免了乱访问的问题。C++中封装函数可以不让用户进行深度了解就可以使用类中的函数。如栈中的top,用户不需要了解top是指向最后一个数据还是最后一个数据的后面,就可以直接使用函数就可以了,也不需要传参数。
C++使用类可以不用typedef就进行使用,更加方便。