文章目录
前言
上篇我们初步学习了类的基本概念以及实例化
今天我们来学习类的构造以及析构还有类的默认成员函数,类和对象这一部分都会有点难
跟着我一起来吧
一、this指针
Date类中有 Init 与 Print 两个成员函数 ,函数体中没有关于不同对象的区分 ,那当d1调用Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这里就要看到C++给了一个隐含的this指针 解决这里的问题
编译器编译后,类的成员函数默认都会在形参第一个位置,增加一个当前类类型的指针,叫做this指针 。比如Date类的Init的真实原型为,*void Init(Date const this, int year,int month, int day)**。类的成员函数中访问成员变量,本质都是通过this指针访问的 ,如Init函数中给_year赋值 , this->_year = year;
C++规定不能在实参和形参的位置显示的写this指针 (编译时编译器会处理),但是可以在函数体内显示使用this指针。
cpp
#include<iostream>
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day) 本质是这样的
void Init(int year, int month, int day)
{
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这里只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
// d1.Init(&d1, 2024, 3, 31);
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
这里有两个比较有意思的题
下面程序编译运行结果是()
A、编译报错 B、运行崩溃 C、正常运行
cpp
#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;
}
可能大家都会觉得选B,以为 p=nullpter,为空指针,调用函数的时候程序会崩溃
但事实却不是这样的,这里只是定义了一个类,但是没有初始化,也就是还没有开辟空间或者其他的活动
也没用对this指针进行调用,所以是不会报错的,会输出 A::Print()
看看下面这个
下面程序编译运行结果是()
A、编译报错 B、运行崩溃 C、正常运行
cpp
#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;
}
这会应该知道这个题是运行崩溃了,因为在调用print函数的时候,要输出一个参数 _a 这个时候就对this指针进行了调用
this指针在前面已经置为空,所以这里是对空指针进行调用,运行崩溃
二、c++和c语言的初步对比
面向对象三大特性:封装、继承、多态,下面的对比我们可以初步了解一下封装
通过下面两份代码对比,我们发现C++实现Stack形态上还是发生了挺多的变化,底层和逻辑上没啥变化。
c实现Stack代码,其实都是以前写过的代码,这里就不多解释了
cpp
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
int main()
{
ST s;
STInit(&s);
STPush(&s, 1);
STPush(&s, 2);
STPush(&s, 3);
STPush(&s, 4);
while (!STEmpty(&s))
{
printf("%d\n", STTop(&s));
STPop(&s);
}
STDestroy(&s);
return 0;
}
c++实现stack,其实最大的区别就是c++把stack封装到了一个类里面 ,然后又有了隐式的this指针,看起来c++实现方便些
cpp
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
// 成员函数
void Init(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}
_capacity = n;
_top = 0;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
--_top;
}
bool Empty()
{
return _top == 0;
}
int Top()
{
assert(_top > 0);
return _a[_top - 1];
}
void Destroy()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
int main()
{
Stack s;
s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
while (!s.Empty())
{
cout << s.Top() <<' ';
s.Pop();
}
s.Destroy();
return 0;
}
C++中数据和函数都放到了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++封装的一种体现,这个是最重要的变化。这里的封装的本质是一种更严格规范的管理,避免出现乱访问修改的问题。当然封装不仅仅是这样的,我们后面还需要不断的去学习。
• C++中有一些相对方便的语法,比如Init给的缺省参数会方便很多 ,成员函数每次不需要传对象地址 ,因为this指针隐含的传递了,方便了很多 ,使用类型不再需要typedef用类名就很方便
• 在我们这个C++入门阶段实现的Stack看起来变了很多,但是实质上变化不大。等着我们后面看STL中的用适配器实现的Stack,大家再感受C++的魅力。
总结
今天把c++的类和对象的上篇完结了,最基本的类和对象就到这里啦,下一篇博客小编就和大家一起探讨探讨类和对象里面的一些默认成员函数
最近期末考试都有点忙,更新的很慢,内容也少了一些,小编在这里说一声抱歉,必然马不停蹄的更新新的内容~~~~